Jest - testing a component that uses react-router - reactjs

I am testing a component that renders a child component that has the following contextTypes:
Component.contextTypes = {router: PropTypes.object.isRequired}
I am completely new to jest, but coming from mocha/enzyme I've never ran into this problem.
My first test looks like this, and I'm really just messing around with it to see how it works:
it('should exist', () => {
wrapper = renderer.create(<Companies/>);
let tree = wrapper.toJSON();
expect(tree).toMatchScreenshot();
});
When I run the test I get the following error:
Failed context type: The context `router` is marked as required in `ChildComponent`, but its value is `undefined`.
Is there a work around for this, or just something in the docs I'm missing? Thanks in advance!
SOLUTION: For anyone that runs into the same issue, I used the following in a beforeEach():
MyComponent.contextTypes = {
router: function () {
return {
transitionTo: jest.genMockFunction()
};
}
};

I had a similar issue when I was using <Link> in some component, and faced same error as above.
In my case I was using react-router 4.1.1 and solution above didn't work.
I added object and functions into context, but I hope someone give me better solution for this.
describe('SomeComponent', ()=>{
it('renders component', ()=>{
const wrapper = mount(
<SomeComponent />,
{
context: {
router: {
history: {
push: ()=>{},
replace: ()=>{},
createHref: ()=>{},
}
}
},
childContextTypes: {
router: PropTypes.object.isRequired,
}
}
)
expect(wrapper.text()).toContain('some component text')
})
})
I'm using enzyme for mount.

Related

Prop function in componentDidUpdate not being called in mounted enzyme test

I have a class-based component that has the following method:
componentDidUpdate(prevProps) {
if (prevProps.location.pathname !== this.props.location.pathname) {
this.props.onDelete();
}
}
I have the following test that is failing:
it(`should call the 'onDelete' function when 'location.pathName' prop changes`, () => {
const wrapper = mount(<AlertsList.WrappedComponent {...props} />);
// test that the deleteFunction wasn't called yet
expect(deleteFunction).not.toHaveBeenCalled();
// now update the prop
wrapper.setProps({ location: { ...props.location, pathName: "/otherpath" } });
// now check that the deleteFunction was called
expect(deleteFunction).toHaveBeenCalled();
});
where props is initialized in a beforeEach statement like so:
beforeEach(() => {
props = {
...
location: { pathName: "/" }
};
});
But my test fails in the second case after the setProps is called, where I would expect the lifecycle method to have run. What am I doing wrong here?
Issue was a typo, see comments under my original post.

Test Material UI Tabs and Enzyme

I'm having problems to test Material UI Tabs with Enzyme and Jest.
The problem is simulating the click on Tab component
I already tried with material shallow method (createShallow) and shallow of enzyme but the results are the same
My console.log on Tab component is returning my target element:
<WithStyles(Tab) label="Tab one" />
Here is my code:
const setup = (newProps) => {
const props = {
selected: 0,
changeTab: jest.fn(),
...newProps
}
const wrapper = shallowUntilTarget(<DashboardTabs { ...props } />, Base)
return {
props,
wrapper
}
}
The shallowUntilTarget is just a code snip to find the component recursively with .dive inside a HOC
https://github.com/airbnb/enzyme/issues/539
it('Should call the onChange function', () => {
const { wrapper, props } = setup()
const tab = wrapper.find({ label: 'Tab One' })
tab.simulate('click')
wrapper.update()
console.log(wrapper.debug()) // I should see a differente content after click in this tab
expect(props.changeTab.mock.calls.length).toBe(1) // the mock function call return 0 on the length
})
And nothing happens :(
You need to use WrappedComponent to dive into HOCs on components:
import { shallow } from 'enzyme';
wrapper = shallow(<DashboardTabs .WrappedComponent {...props} />).dive();
or
You can find an element like this:
wrapper.find(‘withStyles(Tab)’)
Maybe it requires a little tweaks according to your use case. But it would help you

When to use "Component.WrappedComponent" when writing enzyme tests

What I'm trying to do:
I'm trying to use shallow rendering from enzyme by following the below pattern which works for many other components in my project.
describe('>> MyComponent - Render', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<MyComponent.WrappedComponent
actions={{}}
history={}
/>);
});
it("test something", () => { expect(wrapper).toEqual(1); });
});
What's the problem I have:
I got an error saying "Cannot read property 'contextTypes' of undefined", which means wrapper is undefined. But when I change <MyComponent.WrappedComponent /> to just <MyComponent />, the tests are successful. This is my code:
describe('>> Legends - render', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<Legends textsAndColor={[]} />);
});
it('+++ render the component', () => {
expect(wrapper.length).toEqual(1);
});
it('+++ match snapshot', () => {
expect(wrapper).toMatchSnapshot();
});
});
Question I have:
What exactly does .WrappedComponent do ? And why does <MyComponent /> works but not <MyComponent.WrappedComponent /> ?
By using .WrappedComponent you are getting access to component, wrapped by redux's connect function.
I assume that most of your components are connected (since there are no problems with use of .WrappedComponent) and component that throwing described error isn't connected.
I suggest you to read redux docs to find out how to write tests for this case. Briefly said they suggest to have default export for your connected component and non-default for raw component. And then import only raw component for testing purposes, like this:
import { MyComponent } from './path/to/my/component`;
after this you will be able to mount (or shallow) your raw component like this:
describe('>> MyComponent - Render', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<MyComponent />);
}
});

Redux-Form : How to mock formValueSelector in Jest

I have a component Demo whose Label depends on the current value of a field in the redux-form state. I am using formValueSelector to get the current value of "param" field from the form state. It works fine. However, while running npm test, the selector function always returns undefined. How can I mock it?
Please let me know if I am doing this in a wrong way.
I have a component like
class Sample extends React.Component {
render() {
const {param, state} = this.props;
const selector = formValueSelector('sampleform');
return (
<div>
<Demo
name="name"
label={selector(state, `${param}`)}
/>
</div>
);
}
}
export default Sample;
and, testing code is like
function setup() {
const spy = jest.fn();
const store = createStore(() => ({}));
const Decorated = reduxForm({ form: 'sampleform' })(Sample);
const props = {
"param":"keyOfState",
"state":{"keyOfState":"Label"}
}
const mockedComponent = <Provider store={store}>
<MuiThemeProvider muiTheme={MuiStyles()}>
<Decorated {...props}>
<span></span>
</Decorated>
</MuiThemeProvider>
</Provider>;
return {
props,
mockedComponent}
}
describe('Sample Component', () => {
it('should render the snapshot', () => {
const { mockedComponent } = setup()
const tree = renderer.create(
mockedComponent
).toJSON();
expect(tree).toMatchSnapshot();
});
});
You aren't providing the formValueSelector with an adequate mock for the state that the selector expects.
Solution: The selector expects the global state object provided by redux. The current mocked state doesn't reflect this. Changing the mock to the shape expected fixes the issue:
It is of this shape:
{
form: {
sampleform: {
values: {
keyOfState: "Label"
}
}
}
}
Note: the object stored at the sampleform key includes more entries, but they are irrelevant for the mock.
Here is a link to a reproduction that resolves your issue:https://github.com/mjsisley/reduxFormMockIssue
Please note: I was directed here by Matt Lowe. I am the developer that has worked with him on a number of other projects.
For anyone in the future - if for some reason you actually need to mock FormValueSelector, I just exported a wrapper for it from my Helpers module:
export const tableTypeSelector = formValueSelector('toggle')
and then mocked that:
import * as Helpers from 'helpers'
...
stub = sinon.stub(Helpers, 'tableTypeSelector').returns('charges')

React Warning: Failed Context Types: Required context `router` was not specified in `Component`

I'm trying to test a React component that requires the react-router in separately from app.js.
I have a component that does a redirect using the mixin Router.Navigation like so:
var React = require('react'),
Router = require('react-router');
var Searchbar = React.createClass({
mixins : [Router.Navigation],
searchTerm: function(e) {
if (e.keyCode !== 13) {
return;
}
this.context.router.transitionTo('/someRoute/search?term=' + e.currentTarget.value)
},
render: function() {
return (
<div className="searchbar-container">
<input type="search" placeholder="Search..." onKeyDown={this.searchTerm} />
</div>
)
}
});
module.exports = Searchbar;
I tried to write a test for this but ran into a wall. Apart from the fact that I'm unable to test that transitionTo works as expected, I've also encountered this error message in my Jest tests:
Warning: Failed Context Types: Required context router was not specified in Searchbar.
Does anyone know how I can get rid of the warning and bonus question, how I can test that the transition works as expected?
I've done research into this and this conversation on Github here: https://github.com/rackt/react-router/issues/400 is the closest I've found to the problem. It looks like I need to export the router separately but that seems like a lot of overhead to just run component tests without the warning a la https://github.com/rackt/react-router/blob/master/docs/guides/testing.md
Is that really the way to go?
In version 0.13 of React Router, the mixins Navigation and State were deprecated. Instead, the methods they provide exist on the object this.context.router. The methods are no longer deprecated, but if you're using this.context.router explicitly you don't need the mixin (but you need to declare the contextTypes directly); or, you can use the mixin, but don't need to use this.context.router directly. The mixin methods will access it for you.
In either case, unless you render your component via React Router (via Router#run), the router object is not supplied to the context, and of course you cannot call the transition method. That's what the warning is telling you—your component expects the router to be passed to it, but it can't find it.
To test this in isolation (without creating a router object or running the component through Router#run), you could place a mocked router object on the component's context in the correct place, and test that you call transitionTo on it with the correct value.
Because the router relies heavily on the lesser known context feature of React you need to stub it like described here
var stubRouterContext = (Component, props, stubs) => {
return React.createClass({
childContextTypes: {
makePath: func,
makeHref: func,
transitionTo: func,
replaceWith: func,
goBack: func,
getCurrentPath: func,
getCurrentRoutes: func,
getCurrentPathname: func,
getCurrentParams: func,
getCurrentQuery: func,
isActive: func,
},
getChildContext () {
return Object.assign({
makePath () {},
makeHref () {},
transitionTo () {},
replaceWith () {},
goBack () {},
getCurrentPath () {},
getCurrentRoutes () {},
getCurrentPathname () {},
getCurrentParams () {},
getCurrentQuery () {},
isActive () {},
}, stubs);
},
render () {
return <Component {...props} />
}
});
};
And use like:
var stubRouterContext = require('./stubRouterContext');
var IndividualComponent = require('./IndividualComponent');
var Subject = stubRouterContext(IndividualComponent, {someProp: 'foo'});
React.render(<Subject/>, testElement);
Here is my Jest file for a complete answer to this question. BinaryMuse’s last paragraph got me on the right track but I find code examples always the most helpful, so here it is for future reference.
jest.dontMock('./searchbar');
describe('Searchbar', function() {
var React = require('react/addons'),
Searchbar = require('../../components/header/searchbar'),
TestUtils = React.addons.TestUtils;
describe('render', function() {
var searchbar;
beforeEach(function() {
Searchbar.contextTypes = {
router: function() {
return {
transitionTo: jest.genMockFunction()
};
}
};
searchbar = TestUtils.renderIntoDocument(
<Searchbar />
);
});
it('should render the searchbar input', function() {
var searchbarContainer = TestUtils.findRenderedDOMComponentWithClass(searchbar, 'searchbar-container');
expect(searchbarContainer).toBeDefined();
expect(searchbarContainer.props.children.type).toEqual('input');
});
});
});
Hope this helps someone else in the future.
My answer is not Jest-specific but it might help people coming across the same problem.
I created a class to wrap router context.
Then in your test just add
<ContextWrapper><YourComponent/></ContextWrapper>
It can be useful to wrap other things like ReactIntl.
Note that you will lose the possibility to use shallow rendering but that's already the case with ReactIntl.
Hope that helps someone.
ContextWrapper.js
import React from 'react';
export default React.createClass({
childContextTypes: {
router: React.PropTypes.object
},
getChildContext () {
return {
router: {}
};
},
render () {
return this.props.children;
}
});

Resources