Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dive shallow in a Context Consumer #1908

Closed
2 tasks done
Tracked by #1553
flackou opened this issue Nov 18, 2018 · 21 comments · Fixed by #1966
Closed
2 tasks done
Tracked by #1553

Dive shallow in a Context Consumer #1908

flackou opened this issue Nov 18, 2018 · 21 comments · Fixed by #1966

Comments

@flackou
Copy link

flackou commented Nov 18, 2018

Hi,

Current behavior

I'm trying to dive into a Component wrapped with a HOC that adds a Context Consumer but I get an error TypeError: ShallowWrapper::dive() can only be called on components. Here is my test case to reproduce :

import React from 'react';
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

const ThemeContext = React.createContext('light');

const withTheme = (Component) => {
  class ComponentWithTheme extends React.Component {
    render() {
      return (
        <ThemeContext.Consumer>
          {theme => <Component {...this.props} theme={theme} />}
        </ThemeContext.Consumer>
      );
    }
  }
  ComponentWithTheme.displayName = `withTheme(${Component.displayName || Component.name})`;
  return ComponentWithTheme;
}

class MyComponent extends React.Component {
  render() {
    const { theme } = this.props;
    return <div>{theme}</div>;
  }
}
const MyComponentWithTheme = withTheme(MyComponent);

const wrapper = shallow(<MyComponentWithTheme />, { context: 'dark' }).dive(); // TypeError

I'm not familiar with React Contexts, but it seems related to the "function as a child", which is however the regular way to use contexts (cf Context.Consumer in React doc).

Expected behavior

In my test case, the dive should succeed and return a wrapper around <div>dark</div> I guess.

Your environment

My package.json :

{
  "dependencies": {
    "react": "^16.5.1",
    "react-dom": "^16.6.3"
  },
  "devDependencies": {
    "@babel/core": "^7.1.6",
    "@babel/node": "^7.0.0",
    "@babel/preset-env": "^7.1.6",
    "@babel/preset-react": "^7.0.0",
    "enzyme": "^3.7.0",
    "enzyme-adapter-react-16": "^1.7.0"
  }
}

API

  • shallow

Version

library version
enzyme 3.7.0
react 16.5.1
react-dom 16.6.3
react-test-renderer 16.3.2
adapter (below) 1.7.0

Adapter

  • enzyme-adapter-react-16

Am I getting something wrong ? Is there anything particular to do when "shallow diving" in a Context Consumer ? I can't see where's my error on this case so any help would be appreciated !

@ljharb
Copy link
Member

ljharb commented Nov 19, 2018

Indeed; this is because you're shallow rendering your arrow function, which means your render prop is never invoked.

You can try: shallow(<MyComponent />, { context: 'baz' }).shallow() - which should give you a wrapper around the div.

@flackou
Copy link
Author

flackou commented Nov 19, 2018

Hi @ljharb, thanks for your answer. It's not better with .shallow() :

Invariant Violation: ReactShallowRenderer render(): Shallow rendering works only with custom components, but the provided element type was `object`.

But you made me realize that my use case is not accurate : in fact, I met this error while diving into a HOC that adds Context.Consumer wrapper. I edited my initial comment to make it clearer. And to be more precise, my "real word" use case is with React Native and the withNavigation HOC provided by React Navigation package, but I guess it's not very important as far as I can reproduce the error with a simple test case like above.

@ljharb
Copy link
Member

ljharb commented Nov 19, 2018

Ah - that’s the problem then. enzyme does not yet support new-style Context.

@flackou
Copy link
Author

flackou commented Nov 19, 2018

Oh OK ! So I guess I have to wait for the checkbox "New context APIs in 16.3" to be checked in #1553 ? If so, I'll just close my ticket and follow the latter for updates.

@WNemencha
Copy link

Hello, any workaround ?

@ljharb
Copy link
Member

ljharb commented Jan 3, 2019

The workaround is, continue to use the regular context APIs and avoid createContext for now.

@thousight
Copy link

@ljharb do you have an example on how to do that?

@ljharb
Copy link
Member

ljharb commented Jan 9, 2019

@thousight there's tons of examples all over the internet on how to use the only form of context that existed prior to React v16.3 :-)

@AdrienLemaire
Copy link

Enzyme is the final hurdle we have to complete the react-redux v6 migration (which uses the React v16.3 new context API).
Looking forward to see the PR #1966 completed!

@mkatanski
Copy link

mkatanski commented Feb 14, 2019

@thousight @WNemencha I've done some workaround for it, below is my example. If someone knows better solution I'm eager to see it 😈

In my example I can test whether the changeByOffset method that comes in context is called in onChange event.

DataListPagination.test.tsx

const mockChangePageByOffset = jest.fn().mockReturnValue('mock changePageByOffset');
jest.mock('../../providers/DataPaginationProvider', () => ({
  DataPaginationContext: {
    // tslint:disable-next-line:no-any
    Consumer: (props: any) =>
      (
        <div id="DataPaginationContext.Consumer" {...props}>
          {props.children({
            limit: 'limit',
            offset: 'offset',
            totalCount: 'totalCount',
            changePageByOffset: mockChangePageByOffset,
          })}
        </div>
      ),
  },
}));

describe('DataListPagination', () => {

    it('should handle offsetChange event correctly', () => {
      const component = enzyme.shallow<DataListPagination, {}, DataListPagination>(
        (<DataListPagination />));

      // call shallow() method to invoke render prop function
      // and return component inside it
      const deepComponent = component.shallow();
     
     // find component and simulate onChange event
      deepComponent.find('OffsetPagination').simulate('change', 'test_value');

      expect(mockChangePageByOffset).toHaveBeenCalledTimes(1);
      expect(mockChangePageByOffset).toHaveBeenCalledWith('test_value');
    });
});

Tested component DataListPagination.tsx

export class DataListPagination extends Component<{}> {
  handleOffsetChange = (changePageByOffset: (n: number) => void) =>
    (newOffset: number) => changePageByOffset(newOffset)

  renderChildren = ({ limit, offset, totalCount, changePageByOffset }: DataPaginationContextType) => {
    return !!totalCount && (
      <OffsetPagination
        limit={limit}
        offset={offset}
        maxItems={totalCount}
        floated="right"
        onChange={this.handleOffsetChange(changePageByOffset)}
      />
    );
  }

  render() {
    return (
      <DataPaginationContext.Consumer>
        {this.renderChildren}
      </DataPaginationContext.Consumer>
    );
  }
}

@zhaiduo
Copy link

zhaiduo commented Mar 30, 2019

Indeed; this is because you're shallow rendering your arrow function, which means your render prop is never invoked.

You can try: shallow(<MyComponent />, { context: 'baz' }).shallow() - which should give you a wrapper around the div.

I have the same problem, but return the following error:

Invariant Violation: ReactShallowRenderer render(): Shallow rendering works only with custom components, but the provided element type was object

@nclsjstnn
Copy link

From Enzyme v3.9.0 you can dive in context consumers ;)

@atsikov
Copy link

atsikov commented Jun 20, 2019

Looks like this issue is not fully fixed. .dive() works correctly for jsx, but fails to dive into class of functional components.

This will pass

    const wrapper = shallow(
        <Context.Consumer>
            {({ text }) => <Component text={text} />}
        </Context.Consumer>,
    );
    expect(wrapper.dive().text()).toBe("from context");

However this will fail

const WithConsumerFC = props => (
    <Context.Consumer>
        {({ text }) => <Component text={text} />}
    </Context.Consumer>
);
const wrapper = shallow(<WithConsumerFC />);
expect(wrapper.dive().text()).toBe("from context");
Expected: "from context"
Received: "<Component />"

https://codesandbox.io/s/sweet-meadow-jjktt

@atsikov
Copy link

atsikov commented Jun 20, 2019

Actually, I am wrong. Just need to dive deeper to get actual Component rendered.

@mjancarik
Copy link

I created module for workaround https://www.npmjs.com/package/shallow-with-context. The module works well in our projects.

@ljharb
Copy link
Member

ljharb commented Dec 13, 2019

@mjancarik if the workaround fixes the problem, filing it as a PR would be helpful - but you may want to look into the wrappingComponent option.

@vishalc129
Copy link

Actually, I am wrong. Just need to dive deeper to get actual Component rendered.

what do you mean by dive deeper, i'm facing same issue

@atsikov
Copy link

atsikov commented Jan 14, 2020

@vishalc129 you need to call .dive() once again. The first call returns what is rendered by <Context.Consumer>, second will return what is rendered by your wrapped component.

@vishalc129
Copy link

vishalc129 commented Jan 14, 2020

@vishalc129 you need to call .dive() once again. The first call returns what is rendered by <Context.Consumer>, second will return what is rendered by your wrapped component.

im wrapping my component as
const component = shallow(<Login {...props} />);

when i tried to debug using console.log(component.debug()); then o/p is --

'<ContextConsumer>
         [function]
       </ContextConsumer>'

and when i use - component.dive() it says -
TypeError: ShallowWrapper::dive() can not be called on Host Components

@ljharb
Copy link
Member

ljharb commented Jan 14, 2020

@vishalc129 please file a new issue and i'll help you debug it.

@LingVuDev
Copy link

For all who came here for a solution. I just did

componentWrapper = shallow(<SomeComponent {...props} />).dive()

,which worked for a component wrapped by a context consumer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.