I'm trying to test a HOC and can't figure out how to do it. I'm pretty sure I'm doing some basic beginner mistake...
Here is the HOC. Basically, it either renders the given component or not, based on some internal logic I've simplified in this example: (logic is based on the context values)
import React, { PropTypes } from 'react';
import hoistStatics from 'recompose/hoistStatics';
const withPermissions = ({ actions, target = null }) => (WrappedComponent) => {
const WithPermissions = (props, context) => {
const { permissions, currentBusiness: { mid: businessMid, type: businessType } } = context;
const { actions: requiredActions } = sanitizeParameters({ actions });
if(businessType === 'test' && requiredActions.includes('test')){
return <WrappedComponent {...props} />;
}
return null;
};
WithPermissions.WrappedComponent = WrappedComponent;
WithPermissions.contextTypes = {
permissions: PropTypes.array.isRequired,
currentBusiness: PropTypes.shape({
type: PropTypes.string.isRequired,
mid: PropTypes.string.isRequired,
}).isRequired,
};
return WithPermissions;
};
export default hoistStatics(withPermissions);
Here is the test for this component:
test('renders the component', () => {
const context = {
currentBusiness: {
mid: 'provider-1',
type: 'provider',
},
};
const shallowComponent = shallow(
compose(
withPermissions({
actions:'list',
})
)(<div>Hello world</div>),
{ context }
);
// expect(() =>
// shallowComponent.containsMatchingElement(<div>Hello world</div>)
// ).to.be.true;
});
But it throws the following when running Jest: _(at the shallow call)_
Invariant Violation: ReactShallowRenderer render(): Invalid component element. Instead of passing a component class, make sure to instantiate it by passing it to React.createElement.
I tried with shallow and mount but the result is the same. What am I doing wrong?
I think compose takes a Component, not an element - ie, you compose first, and then you use the result in jsx.
I tried that using a functional stateless component but the result was the same.
I think you're misunderstanding.
You want something like:
const Component = compose(
withPermissions({
actions:'list',
})
)(CustomComponent)
);
const wrapper = shallow(<Component />);
Ah, I get it know, that could totally be my mistake. I'm still having a
hard time figuring out when to use Component vs
I'll try ASAP! :)
The former is a component, the latter an element. HOCs compose components; elements aren't composable.
Thank you, it was indeed the source of the issue.
Here is how I've finally done it:
describe('when mounting the component with a provider which is allowed the required actions, should', () => {
let mountedComponent;
beforeEach(() => {
const context = {
permissions: permissions,
currentBusiness: {
mid: 'provider-1', // actions: validProviderActions
type: 'provider',
},
};
const ComponentWithPermissions = componentFactory({
actions: validProviderAction1,
});
mountedComponent = mount(<ComponentWithPermissions />, { context });
})
test('render the wrapper of the component', () => {
expect(mountedComponent.exists()).to.be.true;
});
test('render the content of the component', () => {
expect(
mountedComponent.containsMatchingElement(<div>Hello world</div>)
).to.be.true;
});
});
Most helpful comment
Thank you, it was indeed the source of the issue.
Here is how I've finally done it: