Enzyme: State doesn't get updated even when I do component.update() after simulating

Created on 12 Mar 2018  路  11Comments  路  Source: enzymejs/enzyme

While migrating existing v2 tests.
Seems to be simulating correctly but I dont see the state updated correctly.
Am I missing something ?

const wrapper = mount(<TestComponent {...properties} />);
wrapper.setState({
   focusedColumn: 4
});
wrapper.find('div.base-columns').simulate('mouseleave');
wrapper.update();
expect(wrapper.state('focusedColumn')).toEqual(null); // equals 4 instead 

Similar thing is happening in this case

const wrapper = mount(<TestComponent {...properties} />);
 wrapper.setState({
     focusedColumn: 1
 });
 wrapper.instance().changeFocus({ col: 2 });
// ideally this should've updated the state. IDK
 wrapper.update();
 expect(wrapper.state('focusedColumn')).toEqual(2);

Same here

 const component = mount(<XYZComponent {...properties} />);
 const captionIcon = component.find('.xyz-caption > i');
 captionIcon.simulate('click');
 component.instance().forceUpdate();
  component.update();
 expect(captionIcon.prop('className')).toBe('correctclass'); // but I get the incorrect class coz update isn't updating it . 

API

  • [ ] shallow
  • [ X] mount
  • [ ] render

Version

| library | version
| ---------------- | -------
| Enzyme | latest
| React | 15.5.4

Adapter

  • [ ] enzyme-adapter-react-16
  • [X ] enzyme-adapter-react-15
  • [ ] enzyme-adapter-react-15.4
  • [ ] enzyme-adapter-react-14
  • [ ] enzyme-adapter-react-13
  • [ ] enzyme-adapter-react-helper
  • [ ] others ( )
Need More Information v3 expected difference

Most helpful comment

@Sinha06 every time you call .dive() you create a new wrapper. You'll need to do const wrapper = component.dive(), and then all all those methods on wrapper instead.

All 11 comments

@smoholkar In my environment, the state is updated correctly.
Could you create a repository to reproduce it?

I find similar issue with where i'm not able to get the state value updated:

import React from 'react';
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';

import * as getData from '../../services/DataService';

class Container extends React.Component {

    constructor(props){
        super(props) 
        this.props.actions.getTemplates(1);
        this.state = {
            value: 1
        }

    }

    handlelistOfTemplates = (value, index) => {
        this.setState({ value });
    };

    componentDidMount() {
    }

    render() {
        return(

                <ListOfTemplates listOfTemplates={this.props.listOfTemplates} value={this.state.value} onChange={this.handlelistOfTemplates}/>
            );
    }
}
function mapStateToProps({state}) {
    return {
        listOfTemplates: state.listOfTemplates
    }
}
function mapDispatchToProps(dispatch) {
    return {
    actions: bindActionCreators(getData, dispatch)
    };
   }
module.exports = connect(mapStateToProps, mapDispatchToProps)(Container);

And its test :

import React from 'react';
import sinon from 'sinon';
import expect from 'expect';
import { shallow } from 'enzyme';
import PropTypes from 'prop-types';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import { createMockStore, createMockDispatch } from 'redux-test-utils';

import Container from './Container';
const shallowWithStore = (component, store) => {
    const context = {
        store,
        muiTheme: getMuiTheme(),
    };

    const childContextTypes = {
        muiTheme: React.PropTypes.object.isRequired,
        store: PropTypes.object.isRequired
    }
    return shallow(component, { context, childContextTypes });
};
let store;
const loadComponent = (testState) => {

    const props = {
        actions: {
            getTemplates: () => {return Promise.resolve()}
        }
    }
    store = createMockStore(testState)
    return shallowWithStore(<Container {...props}/>, store);
}

const getFakeState = () => {
    return {
        listOfTemplates: [],

    };
}


export default shallowWithStore;

describe('Container', () => {
    let testState, component;


    describe("when Appeal template is selected from select template dropdown", () => {
        beforeAll(() => {
            testState = getFakeState();
            component = loadComponent(testState);

        });

        fit('should update the content in editor', (done) => {
            component.dive().find('ListOfTemplates').props().onChange(2, 1);
            component.dive().instance().forceUpdate();
            component.dive().update();

            expect(component.dive().state().value).toEqual(2); // it return 1 instead of 2
            done();

        });
    });
});

Am i missing something?

@Sinha06 every time you call .dive() you create a new wrapper. You'll need to do const wrapper = component.dive(), and then all all those methods on wrapper instead.

Happy to reopen if there's a need.

@ljharb I have similar problem:

function setup(props, state) {
  const store = {
    subscribe: Function.prototype,
    getState: () => state || Map({ byId: Map({ memberRoles: [] }) }),
    dispatch: jest.fn(),
  };
  const defaultProps = {
    permissions: [],
  };
  const container = shallow(
    <MemberRoleDialog store={store} {...defaultProps} {...props} />
  );
  const wrapper = container.dive().dive();
  return {
    wrapper,
    container,
    store,
  };
}


test('always renders a save <Button />, which sends only the changed fields', () => {
    const { wrapper, store } = setup(
      { id: 0 },
      Map({ byId: Map({ memberRoles: [role] }) })
    );
    wrapper.setState({ name: 'someOtherName' })
    wrapper.update();
    wrapper.find({ children: 'Save' }).simulate('click');

    expect(store.dispatch).toHaveBeenCalledWith(
      sendMemberRole('memberRoleDialog', 0, {
        name: 'someOtherName',
      })
    );
  });

@AnaRobynn ~Did you try [email protected]?~
This might be solved by https://github.com/airbnb/enzyme/pull/1742, which hasn't been released yet.

@koba04
I'm unsure. I usually don't use setState, but it seemed that neither simulate, nor invoking the "onEdit" prop would result in a state change. So I thought, I must be doing something wrong, let me setState to sanity check.
Fully moubting the component has the same issue, if I remember it correctly.

As soon as I get to work today, I'm going to sanity check by exporting the class itself in stead of the container and diving two times. Do you think it's better to open a new issue?

@AnaRobynn Yeah, it's very helpful to open a new issue with a test case that reproduces your problem 馃憤

@koba04 Sanity check resulted in a green test, restored the original test and it seems to be working now. I think there was an artifact in the docker image, because we recently bumped versions for enzyme. All good now! Yay! :)

Note to self and others: Clean repo, when having weird issues. Like deleting node_modules.

@Sinha06 every time you call .dive() you create a new wrapper. You'll need to do const wrapper = component.dive(), and then all all those methods on wrapper instead.

You are damn genius!

Hi, as per @smoholkar question from a while back, I'm facing a similar issue when mounting my component for testing as well. I can't seem to find anyone else other than this post who is facing the issue of states not updating other than this post - is there a solution that I'm missing?

Was this page helpful?
0 / 5 - 0 ratings