Describe the bug
I have a component that exports using HOC:
const Component = (props) => (
...
);
export default withLanguage(PaymentMethodItem);
The function "withLanguage" implements a Consumer. In my tests with Enzyme I need to provide a Provider on mount, like this:
wrapper = mount((
<LanguageContext.Provider value={i18n}>
<Component onPress={() => null} />
</LanguageContext.Provider>
));
It's impossible to setProps to the Component. There's no way to that. Neither using find, or anything:
// NOT WORKING. Can only setProps on root component!!
component.find(Component).setProps({ onPress: () => console.log('hello world') })
Expected behavior
There must be a way to use setProps with Context API components.
enzyme doesn't yet support new Context.
Is there any PR that address this? maybe it can be a possible contribution?
I think I found a way to work with the children component:
.find('your children') it this will return a React Wrapper.instance() this will return a React Component. I need it to check the State, so this was my solution. Let me know how does it work with props.This last part will let you investigate the state I asume you can also set props to it
I've worked around this by doing something like this:
// Write a wrapper around the component to test
function ComponentWithContext(props) {
return (
<LanguageContext.Provider value={i18n}>
<Component {...props} />{/* pass all props to the component being tested */}
</LanguageContext.Provider>
);
}
const wrapper = mount(
<ComponentWithContext onPress={() => null} />
);
// ComponentWithContext will pass the changed props to Component
wrapper.setProps({ onPress: () => alert('I was pressed') });
It's not perfect. For example,
wrapper.type() === Component; // false
wrapper.type() === ComponentWithContext; // true
But you can always
wrapper.find(Component)
to get your _actual_ component.
P.S. I created a library that could be of some use to you if you're dealing with a lot of context in your app. It's called enzyme-context and it allows you to create mount/shallow functions that are specific to _your_ application and that provide the components you mount with the context they need to render. The specialized mount/shallow functions are configurable via plugins so it would be very straightforward to create a plugin that automatically applies the workaround from my previous comment.
@minznerjosh thanks for a workaround. However, it won't work if you want to use .setState():
Error: ReactWrapper::setState() can only be called on the root
setState() is allowed on non-roots in the latest version of enzyme.
@minznerjosh thanks for a response.
Are docs outdated then?
https://github.com/airbnb/enzyme/blob/2932d8c40a2898d9b120ef232b1788b88c83190c/docs/api/ReactWrapper/setState.md#L10
They appear to be! Wanna open a PR? 馃槉
@minznerjosh Let me know if you have comments I can add them.
Had the similar problem recently so here's my work-around in case it's useful to anyone.
Here I have a BabylonScene component which provides the context for a Camera component but which needs props to be provided to it.
// By using this as a WrappingComponent we can use standard Enzyme wrappers to test a component which needs context
const TestBabylonScene = (props: any) => {
return <BabylonScene config="Context provider can be configured here" />;
};
test("Name changes are reflected in internalCamera", () => {
const wrapper = mount<Camera>(<Camera name="TestCamera" />, {
wrappingComponent: TestBabylonScene
});
wrapper.setProps({ name: "RenamedCamera" });
const camera: Camera = wrapper.instance();
expect(camera.internalCamera.name).toEqual("RenamedCamera");
});
Closing, since the wrappingComponent should handle this.
Please file new issues if anyone's still having trouble.
Most helpful comment
I've worked around this by doing something like this:
It's not perfect. For example,
But you can always
to get your _actual_ component.