NextState on componentWillUpdate is not correct when tested with Jest (using wrapper adapter as well)
I am using Jest 0.4.0. I have a component wrapped in this (from the responder docs):
var stubRouterContext = (Component, props, stubs) => {
function RouterStub() { }
Object.assign(RouterStub, {
makePath () {},
makeHref () {},
transitionTo () {},
replaceWith () {},
goBack () {},
getCurrentPath () {},
getCurrentRoutes () {},
getCurrentPathname () {},
getCurrentParams () {},
getCurrentQuery () {},
isActive () {},
getRouteAtDepth() {},
setRouteComponentAtDepth() {}
}, stubs)
return React.createClass({
childContextTypes: {
router: React.PropTypes.func,
routeDepth: React.PropTypes.number
},
getChildContext () {
return {
router: RouterStub,
routeDepth: 0
};
},
render () {
return <Component {...props} />
}
});
};
My component uses componentWillUpdate
:
getInitialState: function(){
return {something: ""};
},
componentWillUpdate: function(nextProps, nextState) {
if(nextState.something === "12345"){
this.context.router.transitionTo("MyRoute", {id: nextState.something});
}
},
In my test:
var ComponentWrapper = stubRouterContext(MyComponent, {});
var myComponentInstance = TestUtils.renderIntoDocument(<ComponentWrapper />);
it('expects to do something on componentWillUpdate', function(){
myComponentInstance.setState({something: "12345"});
expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][0]).toEqual('MyRoute');
expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][1]).toEqual({id: '12345'});
});
As far as I call it setState
, mine nextState
is componentWillUpdate
always something: ""
. However, in a test, if I check the content myComponentInstance.state
then this is something: "12345"
. So basically componentWillUpdate
gets called, but doesn't have a new state, even if its instance does.
Any ideas on this?
-
EDIT 1
The suggestions below are based on the asynchronous setState function, but that didn't fix the problem. I also tried to simulate changing the store (stream pattern) like this:
myStore.getState = jest.genMockFunction().mockImplementation(function() {
return{
something: "12345",
};
});
myComponentInstance.onChange(); //Simulate store change (this function has setState inside taking values from the store)
Well it didn't work either, in fact I was told it was onChange
not defined. So my problem is with the reactor-router shell. I found a solution, but I'm not sure if there are better ones because it looks very hacky. This is the following:
var RouterWrapper = stubRouterContext(Component, {ref: "myRealComponentInstance"});
var renderedComponent = TestUtils.renderIntoDocument(<RouterWrapper />);
var myComponentInstance = renderedComponent.refs.myRealComponentInstance;
This way, both myComponentInstance.setState
, and mimicking myComponentInstance.onChange
, poking fun at the work of the store, and I don't need to use async functions.
source to share
setSate is an asynchronous function, so you need to use a callback like below:
myComponentInstance.setState({something: "12345"}, function() {
expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][0]).toEqual('MyRoute');
expect(myComponentInstance.getChildContext().router.transitionTo.mock.calls[0][1]).toEqual({
id: '12345'
});
});
source to share