Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to React16 and Enzyme3 broke test that use simulate or click event #1201

Closed
kopax opened this issue Sep 29, 2017 · 49 comments
Closed

Comments

@kopax
Copy link

kopax commented Sep 29, 2017

I have updated to React16 and Enzyme3

For some reason, I have many test failing with class looking like this :

    export default class DropdownColorExample extends React.Component {   
      state = {
        dropdownOpen: false,
      }    
      toggle = () => {
        console.log('clicked')
        this.setState({
          dropdownOpen: !this.state.dropdownOpen,
        });
      }    
      render() {
        return (
          <Dropdown isOpen={this.state.dropdownOpen} toggle={this.toggle}>
            <DropdownToggle color="primary" caret>
              Dropdown
            </DropdownToggle>
            <DropdownMenu>
              <DropdownItem header>Header</DropdownItem>
              <DropdownItem disabled>Action</DropdownItem>
              <DropdownItem>Another Action</DropdownItem>
              <DropdownItem divider />
              <DropdownItem>Another Action</DropdownItem>
            </DropdownMenu>
          </Dropdown>
        );
      }
    }

Test were both using props directly and simulate() to trigger the state change and the new props of some child components:

Example of test now failing :

    import React from 'react';
    import { mount } from 'enzyme';
    import DropdownColorExample from '../DropdownColorExample';
    const renderComponent = (props = {}) => mount(
      <DropdownColorExample {...props} />
    );    
    describe('<DropdownColorExample />', () => {
      it('should change state when button is clicked', () => {
        const renderedComponent = renderComponent();
        expect(renderedComponent.find('Dropdown').prop('isOpen')).toBe(false);
        renderedComponent.find('button').at(0).prop('onClick')();
        expect(renderedComponent.find('Dropdown').prop('isOpen')).toBe(true);
      });
      it('should change state when button is clicked', () => {
        const renderedComponent = renderComponent();
        expect(renderedComponent.find('Dropdown').props().isOpen).toBe(false);
        renderedComponent.find('button').at(0).simulate('click');
        expect(renderedComponent.find('Dropdown').props().isOpen).toBe(true);
      });
    });

I can see in both case enzyme log my clicks:

console.log app/components/docs/DropdownsDoc/DropdownColorExample.js:11
clicked

But the next test always fail because the child props have not change:

  ● <DropdownColorExample /> › should change state when button is clicked

    expect(received).toBe(expected)
    
    Expected value to be (using ===):
      true
    Received:
      false

Reproduction

Repo: https://github.com/kopax/enzyme-react16

Broken React16 and Enzyme3

git clone git@github.com:kopax/enzyme-react16.git && cd enzyme-react16 && npm install && npm test

Working React15 and Enzyme2.8

git clone git@github.com:kopax/enzyme-react16.git && cd enzyme-react16 && git checkout v15 && npm install && npm test

Any idea on how I should migrate my tests ?

@figalex
Copy link

figalex commented Sep 30, 2017

I'm also having problems with Enzyme 3 and simulating events. I'm trying to pass a mocked event object like this:

checkbox.simulate('change', {stopPropagation: () => {}});

But I'm getting a default React event when simulating event:

Expected mock function to have been called with:
      {"stopPropagation": [Function stopPropagation]} as argument 2, but it was called with {"_dispatchInstances": null, "_dispatchListeners": null, "_targetInst": {"_ancestorInfo": {"aTagInScope": null, "buttonTagInScope": null, "current": {"instance": [Circular], "tag": "input"}, "dlItemTagAutoclosing": null, "formTag": null, "listItemTagAutoclosing": null, "nobrTagInScope": null, "pTagInButtonScope": null}, "_contentDebugID": null, "_currentElement": <input checked={false} className="sc-bwzfXH bEFgvg" id="1234" onChange={[Function bound checkboxDidChange]} type="checkbox" />, "_debugID": 9, "_domID": 2, "_flags": 1, "_hostContainerInfo": {"_ancestorInfo": {"aTagInScope": null, "buttonTagInScope": null, "current": {"instance": null, "tag": "div"}, "dlItemTagAutoclosing": null, "formTag": null, "listItemTagAutoclosing": null, "nobrTagInScope": null, "pTagInButtonScope": null}, "_idCounter": 4, "_namespaceURI": "http://www.w3.org/1999/xhtml", "_node": <div><div class="sc-htpNat dsxSlV" data-reactroot=""><input … /><label … /></div></div>, "_ownerDocument": {"_reactListenersID6142566933764189": 0, "location": {"valueOf": [Function valueOf], Symbol(impl): [LocationImpl]}, Symbol(impl): {"_URL": [Object], "_activeNodeIterators": [Array], "_activeNodeIteratorsMax": 10, "_agentOptions": [Object], "_attached": true, "_childNodesList": null, "_childrenList": null, "_contentType": undefined, "_cookieJar": [CookieJar], "_core": [Object], "_currentScript": null, "_customResourceLoader": undefined, "_defaultView": [Window], "_documentElement": [HTMLHtmlElementImpl], "_encoding": "UTF-8", "_eventListeners": [Object], "_global": [Window], "_history": [HistoryImpl], "_htmlToDom": [HtmlToDom], "_ids": [Object], "_implementation": [DOMImplementationImpl], "_lastFocusedElement": null, "_lastModified": "09/29/2017 18:12:26", "_location": [LocationImpl], "_memoizedQueries": [Object], "_nwmatcher": [Object], "_origin": "http://localhost", "_ownerDocument": [Circular], "_parsingMode": "html", "_pool": [Object], "_proxy": undefined, "_queue": [ResourceQueue], "_referrer": "", "_requestManager": [RequestManager], "_strictSSL": true, "_styleSheets": [StyleSheetList], "_version": 11, "nodeType": 9, "readyState": "complete", Symbol(DOM SymbolTree): [SymbolTreeNode], Symbol(wrapper): [Circular]}}, "_tag": "div", "_topLevelWrapper": {"_calledComponentWillUnmount": false, "_compositeType": 0, "_context": {}, "_currentElement": <TopLevelWrapper child={<WrapperComponent … />} />, "_debugID": 0, "_hostContainerInfo": [Circular], "_hostParent": null, "_instance": {"_reactInternalInstance": [Circular], "context": [Object], "props": [Object], "refs": [Object], "rootID": 1, "state": null, "updater": [Object]}, "_mountImage": null, "_mountIndex": 0, "_mountOrder": 1, "_pendingCallbacks": null, "_pendingElement": null, "_pendingForceUpdate": false, "_pendingReplaceState": false, "_pendingStateQueue": null, "_renderedComponent": {"_calledComponentWillUnmount": false, "_compositeType": 0, "_context": [Object], "_currentElement": <WrapperComponent … />, "_debugID": 3, "_hostContainerInfo": [Circular], "_hostParent": null, "_instance": [WrapperComponent], "_mountImage": null, "_mountIndex": 0, "_mountOrder": 2, "_pendingCallbacks": null, "_pendingElement": null, "_pendingForceUpdate": false, "_pendingReplaceState": false, "_pendingStateQueue": null, "_renderedComponent": [], "_renderedNodeType": 1, "_rootNodeID": 0, "_topLevelWrapper": [Circular], "_updateBatchNumber": null, "_warnedAboutRefsInRender": false}, "_renderedNodeType": 1, "_rootNodeID": 0, "_topLevelWrapper": null, "_updateBatchNumber":null, "_warnedAboutRefsInRender": false}}, "_hostNode": <input class="sc-bwzfXH bEFgvg" id="1234" type="checkbox" />, "_hostParent": {"_ancestorInfo":{"aTagInScope": null, "buttonTagInScope": null, "current": {"instance": [Circular], "tag": "div"}, "dlItemTagAutoclosing": null, "formTag": null, "listItemTagAutoclosing": null, "nobrTagInScope": null, "pTagInButtonScope": null}, "_contentDebugID": null, "_currentElement": <div className="sc-htpNat dsxSlV"><styled.input checked={false} id="1234" onChange={[Function bound checkboxDidChange]} /><styled.label htmlFor="1234" /></div>, "_debugID": 6, "_domID": 1, "_flags": 1, "_hostContainerInfo": {"_ancestorInfo": {"aTagInScope": null, "buttonTagInScope": null, "current": [Object], "dlItemTagAutoclosing": null, "formTag": null, "listItemTagAutoclosing": null, "nobrTagInScope": null, "pTagInButtonScope": null}, "_idCounter": 4, "_namespaceURI": "http://www.w3.org/1999/xhtml", "_node": <div><div … /></div>, "_ownerDocument": {"_reactListenersID6142566933764189": 0, "location": [Location], Symbol(impl): [DocumentImpl]}, "_tag": "div", "_topLevelWrapper": {"_calledComponentWillUnmount": false, "_compositeType": 0, "_context": [Object], "_currentElement": <TopLevelWrapper … />, "_debugID": 0, "_hostContainerInfo": [Circular], "_hostParent": null, "_instance": [], "_mountImage": null, "_mountIndex": 0, "_mountOrder": 1, "_pendingCallbacks": null, "_pendingElement": null, "_pendingForceUpdate": false, "_pendingReplaceState": false, "_pendingStateQueue": null, "_renderedComponent": [], "_renderedNodeType": 1, "_rootNodeID": 0, "_topLevelWrapper": null, "_updateBatchNumber": null, "_warnedAboutRefsInRender": false}}, "_hostNode": <div class="sc-htpNat dsxSlV" data-reactroot=""><input class="sc-bwzfXH bEFgvg" id="1234" type="checkbox" /><label class="sc-bdVaJa dcNulu" for="1234" /></div>, "_hostParent": null, "_mountImage": null, "_mountIndex": 0, "_namespaceURI": "http://www.w3.org/1999/xhtml", "_previousStyle": null, "_previousStyleCopy": null, "_renderedChildren": {".0": {"_calledComponentWillUnmount": false, "_compositeType": 0, "_context": [Object], "_currentElement": <styled.input … />, "_debugID": 7, "_hostContainerInfo": [Object], "_hostParent": [Circular], "_instance": [StyledComponent], "_mountImage": null, "_mountIndex": 0, "_mountOrder": 5, "_pendingCallbacks": null, "_pendingElement": null, "_pendingForceUpdate": false, "_pendingReplaceState": false, "_pendingStateQueue": null, "_renderedComponent": [Circular], "_renderedNodeType": 0, "_rootNodeID": 0, "_topLevelWrapper": null, "_updateBatchNumber": null, "_warnedAboutRefsInRender": false}, ".1": {"_calledComponentWillUnmount": false, "_compositeType": 0, "_context": [Object], "_currentElement": <styled.label … />, "_debugID": 8, "_hostContainerInfo": [Object], "_hostParent": [Circular], "_instance": [StyledComponent], "_mountImage": null, "_mountIndex": 1, "_mountOrder": 6, "_pendingCallbacks": null, "_pendingElement": null, "_pendingForceUpdate": false, "_pendingReplaceState": false, "_pendingStateQueue": null, "_renderedComponent": [ReactDOMComponent], "_renderedNodeType": 0, "_rootNodeID": 0, "_topLevelWrapper":null, "_updateBatchNumber": null, "_warnedAboutRefsInRender": false}}, "_rootNodeID": 1, "_tag": "div", "_topLevelWrapper": null, "_wrapperState": null}, "_mountImage": null, "_mountIndex": 0, "_namespaceURI": "http://www.w3.org/1999/xhtml", "_previousStyle": null, "_previousStyleCopy": null, "_renderedChildren": null, "_rootNodeID": 2, "_tag": "input", "_topLevelWrapper": null, "_wrapperState": {"controlled": true, "initialChecked": false, "initialValue": undefined, "listeners": [{"remove": [Function remove]}], "onChange": [Function bound _handleChange], "valueTracker": {"getValue": [Function anonymous], "setValue": [Function anonymous], "stopTracking": [Function anonymous]}}}, "bubbles": undefined, "cancelable": undefined, "currentTarget": null, "defaultPrevented": undefined, "dispatchConfig": {"dependencies": ["topBlur", "topChange", "topClick", "topFocus", "topInput", "topKeyDown", "topKeyUp", "topSelectionChange"], "phasedRegistrationNames": {"bubbled": "onChange", "captured": "onChangeCapture"}}, "eventPhase": undefined, "isDefaultPrevented": [Function anonymous], "isPersistent": [Function anonymous], "isPropagationStopped": [Function anonymous], "isTrusted": undefined, "nativeEvent": {"target": <input class="sc-bwzfXH bEFgvg" id="1234" type="checkbox" />, "type": "change"}, "stopPropagation": [Function stopPropagation], "target":<input class="sc-bwzfXH bEFgvg" id="1234" type="checkbox" />, "timeStamp": 1506733947226, "type": "change"}

I'm using Enzyme 3 with React 15.6.1.

@d3ni00
Copy link

d3ni00 commented Oct 1, 2017

I have the same Problem with Enzyme 3.0.0 and React 16.0.0. The event is triggered but the state will not be updated (simulate click).

@sontek
Copy link

sontek commented Oct 2, 2017

Just ran into this issue as well. Some of the state is updated but not all of it, here is the code for my test:

import {TabContainer, TabItem} from './tabs';
import {mount, render, shallow} from 'enzyme';

import React from 'react';

const getTabComponent = () => {
    return (
        <TabContainer>
            <TabItem title="one">
                <div>one</div>
            </TabItem>
            <TabItem title="two">
                <div>two</div>
            </TabItem>
        </TabContainer>
    );
}

describe("materials/tabs", () => {
    it("Tabs render minimally without blowing up", () => {
        const wrapper = shallow(getTabComponent());
        expect(wrapper).toHaveLength(1);
    });

    it("Can change tabs", () => {
        const wrapper = mount(getTabComponent());

        const tabs = wrapper.find(".tabs span");
        expect(tabs.length).toEqual(2);

        const activeTab = wrapper.find(".tabs .active");
        expect(activeTab.length).toEqual(1);
        expect(activeTab.at(0).text()).toEqual("one");
        console.log("====BEFORE=====", wrapper.debug());
        tabs.at(1).simulate("click");
        console.log("====AFTER CLICK=====", wrapper.debug());
        wrapper.update();
        console.log("====AFTER UPDATE=====", wrapper.debug());

        const changedTab = wrapper.find(".tabs .active");
        expect(changedTab.length).toEqual(1);
        expect(changedTab.at(0).text()).toEqual("two");

        expect(wrapper).toHaveLength(1);
    });
});

So you can see I log the component before click, after click, and after an update. You will see the property active changes but the className doesn't. But you will also seeing the logs for rendering the tab, it is updating the className.

    console.log static/js/materials/tabs.js:91
      RENDERING TAB one true active
    console.log static/js/materials/tabs.js:91
      RENDERING TAB two false
    console.log static/js/materials/tabs.test.js:34
      ====BEFORE===== <TabContainer>
        <div className="tabs">
          <ul>
            <TabItem title="one" active={true} onClick={[Function]}>
              <li>
                <span onClick={[Function]} className="active">
                  one
                </span>
              </li>
            </TabItem>
            <TabItem title="two" active={false} onClick={[Function]}>
              <li>
                <span onClick={[Function]} className="">
                  two
                </span>
              </li>
            </TabItem>
          </ul>
          <div className="TabContent">
            <div>
              one
            </div>
          </div>
        </div>
      </TabContainer>
    console.log static/js/materials/tabs.js:91
      RENDERING TAB one false
    console.log static/js/materials/tabs.js:91
      RENDERING TAB two true active
    console.log static/js/materials/tabs.test.js:36
      ====AFTER CLICK===== <TabContainer>
        <div className="tabs">
          <ul>
            <TabItem title="one" active={false} onClick={[Function]}>
              <li>
                <span onClick={[Function]} className="active">
                  one
                </span>
              </li>
            </TabItem>
            <TabItem title="two" active={true} onClick={[Function]}>
              <li>
                <span onClick={[Function]} className="">
                  two
                </span>
              </li>
            </TabItem>
          </ul>
          <div className="TabContent">
            <div>
              two
            </div>
          </div>
        </div>
      </TabContainer>
    console.log static/js/materials/tabs.test.js:38
      ====AFTER UPDATE===== <TabContainer>
        <div className="tabs">
          <ul>
            <TabItem title="one" active={false} onClick={[Function]}>
              <li>
                <span onClick={[Function]} className="active">
                  one
                </span>
              </li>
            </TabItem>
            <TabItem title="two" active={true} onClick={[Function]}>
              <li>
                <span onClick={[Function]} className="">
                  two
                </span>
              </li>
            </TabItem>
          </ul>
          <div className="TabContent">
            <div>
              two
            </div>
          </div>
        </div>
      </TabContainer>

@sontek
Copy link

sontek commented Oct 2, 2017

@lelandrichardson @ljharb Any idea what could cause this? Upgrading enzyme and react broke majority of my tests because of updates not being reflected.

@pfhayes
Copy link
Contributor

pfhayes commented Oct 2, 2017

We are observing this as well.

FWIW, the example in the docs (http://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html) fails as well

@ljharb
Copy link
Member

ljharb commented Oct 2, 2017

What about React 15 and enzyme 3?

In other words, your tests on React 16 never worked on enzyme 2, so we'd need you to compare between react 15 and 16 on enzyme 3, to know whether the behavior change was caused by enzyme 3, or by the react 16 adapter.

@sontek
Copy link

sontek commented Oct 2, 2017

Yeah, it breaks on react 15 as well, here is the change I made to break it:

diff --git a/package.json b/package.json
index 966c513..7692c9e 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,8 @@
     "draftjs-utils": "^0.8.4",
     "emoji-flags": "^1.2.0",
     "env-cmd": "^5.0.0",
+    "enzyme-adapter-react-15": "^1.0.0",
     "eslint": "^4.0.0",
     "eslint-loader": "^1.8.0",
     "eslint-plugin-react": "^7.1.0",
@@ -156,8 +158,8 @@
     "concurrently": "^3.4.0",
     "deep-diff": "^0.3.4",
     "deep-freeze": "latest",
-    "enzyme": "^2.8.2",
-    "enzyme-to-json": "^2.0.0",
+    "enzyme": "^3.0.0",
+    "enzyme-to-json": "^3.0.1",
     "eslint-plugin-import": "^2.2.0",
     "express": "^4.15.3",
     "happypack": "^3.0.3",
diff --git a/static/js/tests/setup.js b/static/js/tests/setup.js
index bfe1030..8321b99 100644
--- a/static/js/tests/setup.js
+++ b/static/js/tests/setup.js
@@ -1,3 +1,7 @@
 import "babel-polyfill";
 
+import Adapter from 'enzyme-adapter-react-15';
+import { configure } from 'enzyme';
 import fetch from 'isomorphic-fetch';
+
+configure({ adapter: new Adapter() });

Which is:

  • Upgrade enzyme/enzyme-to-json
  • Add the adapter and configure it

and now all my tests fail

@sergioviniciuss
Copy link

I was having the same issue, then I downgraded enzyme to v 2.9.0 and it started working again :). I'll have to read the documentation of v3 to make the changes..

@kamthamc
Copy link

kamthamc commented Oct 8, 2017

@kamthamc
Copy link

kamthamc commented Oct 8, 2017

@kopax sorry if you misunderstood it. I didnt solve it

@HipsterZipster
Copy link

Also really looking for some help here. simulate('mouseenter') stopped working once we upgraded enzyme and added the r16 adapter. We've managed to get everything else working.
We're using enzyme 3.1.0 with enzyme-adapter-react-16 1.0.1

@ljharb
Copy link
Member

ljharb commented Oct 17, 2017

@HipsterZipster does it work with enzyme 3 and the react 15 adapter?

@HipsterZipster
Copy link

@ljharb do you mean converting the actual react / react-dom dependencies to use r15 with the r15 enzyme adapter or just trying the r15 adapter with r16 dependencies?

@sontek
Copy link

sontek commented Oct 17, 2017

I did the conversion of using enzyme3 with react15 and it broke, you can see the proof in my comment here:

#1201 (comment)

Also, as the commenter above noted the examples in the documentation don't work with react15 + enzyme3:

#1201 (comment)

@ljharb
Copy link
Member

ljharb commented Oct 17, 2017

@HipsterZipster i mean getting your codebase passing tests on enzyme 3 with react 15, prior to upgrading to react 16 - upgrading to react 16 and enzyme 3 at the same time is reckless :-)

@sontek thanks

@sontek
Copy link

sontek commented Oct 19, 2017

Anyone looking into this? I don't know enough about enzyme to contribute but this is the only dependency preventing us from upgrading.

If there is anymore things I can do to help reproduce and move it along, I'm willing to do so.

@ljharb
Copy link
Member

ljharb commented Oct 20, 2017

@lelandrichardson, any thoughts?

@EduardoAC
Copy link

EduardoAC commented Oct 23, 2017

I am having exactly the same problem, our core team decided to update React from 15.3.2 -> 15.6.2 and we got errors running enzyme 3.1.0.

The only tests got broken (broken === doesn't do anything) were related with simulate input and select changes, the rest of my simulate are working fine. I tried multiple combinations without luck. Example below

package.json

"dependencies": {
  "react": "15.6.2",
  "react-dom": "15.6.2",
  "react-redux": "^5.0.6",
  "react-router": "^4.1.2",
  "redux": "^3.7.2",
  "redux-form": "^6.7.0",
  "sinon": "^2.2.0",
...
},
"devDependencies": {
  "enzyme": "^3.1.0",
  "enzyme-adapter-react-15": "^1.0.2",
  "jsdom": "^11.0.0",
  "mocha": "3.2.0",
....
}
it('should map state to timeWindows prop', () => {
  const store = createFakeStore(initialState);
  const wrapper = mount(
    <Provider store={store}>
      <BookingFormContainer {...props}/>
    </Provider>
  );

  const container = wrapper.find('BookingForm');
  const datesDropwDown = wrapper.find('select[name="date"]');

  const dateToSelect = initialState.booking.dates[1];
  datesDropwDown.simulate('change', { target: { value: dateToSelect.date } });

  assert.deepEqual(container.props().timeWindows, [
    { value: '0', label: expectedGeneratedFormatedTime(dateToSelect.timeWindows[0]) },
    { value: '1', label: expectedGeneratedFormatedTime(dateToSelect.timeWindows[1]) }
  ]);
});

@ljharb
Copy link
Member

ljharb commented Oct 24, 2017

I believe React 15.6 changed how the "change" event works; it no longer fires when the value hasn't actually changed. I'm not sure if that's your issue or not.

@EduardoAC
Copy link

EduardoAC commented Oct 24, 2017

@ljharb currently the value pass from [] to dateToSelect.date which is not empty. However, the value on the asset is empty so it doesn't update when I run simulate click.

Perhaps, it's related in how should I target the events, but I am using redux-form for that which in theory update the value before the upgrade.

@palaniichukdmytro
Copy link

palaniichukdmytro commented Oct 25, 2017

I have the same issue, when I updated
"react": "^16.0.0", "react-dom": "^16.0.0", "chai": "^4.1.1", "chai-enzyme": "^1.0.0-beta.0", "enzyme": "^3.1.0", "enzyme-adapter-react-16": "^1.0.2", "mocha": "^4.0.1", "react-addons-test-utils": "^15.6.2", "react-test-renderer": "^16.0.0", "sinon": "^4.0.1",

My test failed with error AssertionError: expected false to be true, before updating it works,

 it('should set isUserInfoShown to true and userInfoPositionElement from input event when showUserInfo is called', () => {
        let currentTarget = {
            type: 'button',
        }
        let spy = sinon.spy()
        let event = {
            preventDefault: spy,
            currentTarget: currentTarget,
        }
        account.find(AccountButton).props().onClick(event)
        spy.calledOnce.should.be.true
        account.find(AccountButton).props().isPopoverShown.should.be.true

        account.find(AccountButton).props().positionElement.should.be.eql(currentTarget)
    })

When I change two lines, test passed
account.find(AccountButton).simulate('click', event)
expect(account.find(AccountButton).props().isPopoverShown).to.be.true

Now we should always use expect instead should.be.true? If so, should may be better use some codemode
why does simulate work?

@palaniichukdmytro
Copy link

The issue with was solved after I use account.update()

@stevenmusumeche
Copy link

I am having the same issue with 3.1 and react 16 adapter.

            policySelect.simulate('change', { target: { value: 123 } });
            expect(policySelect.prop('value')).to.equal(123);

This no longer works, but did before.

@stevenmusumeche
Copy link

FYI, if you "refind" the element, it does work. For example

let policySelect = wrapper.find('select').at(0);
/// other code
policySelect.simulate('change', { target: { value: 123 } });
// you have to find it again
policySelect = wrapper.find('select').at(0);
expect(policySelect.prop('value')).to.equal(123);

@palaniichukdmytro
Copy link

@stevenmusumeche do you use mount or shallow ?, what error did you get ?

@stevenmusumeche
Copy link

In this case, mount.

@mikehdt
Copy link

mikehdt commented Oct 29, 2017

I've been doing some experiments with upgrading our dependencies for React and Enzyme, and with R16 and Enzyme 3, these simulate tests have been failing. Strangely, it seems simulating twice lets it show up once.

e.g.

const component = mount(<TestComponent />);
const spy = jest.spyOn(component.instance(), 'handleClick');
component.update();
component.find('button').simulate('click'); // <-- fails if it is only called once
component.update(); // <-- forcing a second update doesn't do anything
component.find('button').simulate('click'); // <-- succeeds if we simulate a second time
expect(spy).toHaveBeenCalled();

Is there perhaps some kind of async action going on?

Edit: To test further, I went back to the Master branch which is still on React 15.6 and upgraded just Enzyme from 2.9.1 to 3.1.0 (adding the React 15 adapter). This above behaviour happens as well.

@ljharb
Copy link
Member

ljharb commented Oct 29, 2017

Anyone having issues with React 16 and enzyme 3: please first ensure that your tests pass on React 15 and enzyme 3, so that we can narrow down the failure to "upgrading to react 16 and switching to the react 16 adapter".

@Natumsol
Copy link

Natumsol commented Oct 30, 2017

I have the same issue with React 15.x and Enzyme 3: the click event has been triggered, but the prop(className) has not been updated correspondingly, however, when I use console.log(wrapper.html()) to print the node,the className has been updated already :

image

@EduardoAC
Copy link

EduardoAC commented Oct 30, 2017

@ljharb My example was a pure migration problem the test passed on 15.3.2 but it doesn't on 15.6.2, I will try to figure out the problem check the library internally, but no promises :)

@ljharb
Copy link
Member

ljharb commented Oct 30, 2017

@EduardoAC thanks, if you could file a new issue for your concern, that might be a difference between the 15.4 and the 15 adapters (this issue is primarily about react 16)

@sontek
Copy link

sontek commented Oct 30, 2017

@ljharb Is it primarily about react 16 if the same error happens on both? My tests fail the same as the issue on both React15 and React16, the only difference being enzyme3

@ljharb
Copy link
Member

ljharb commented Oct 30, 2017

@sontek i guess it'd depend on whether it was in the adapter or in enzyme itself; certainly if it's across adapters that suggests it's related to enzyme itself.

@sontek
Copy link

sontek commented Oct 30, 2017

@ljharb Yeah, well I've seen at least @mikehdt [1], @EduardoAC [2], Me [3], and @figalex [4] report that we are experiencing this issue on React 15, so it does seem to be more widespread.

Also, I switched the original reproduction code from the creator of the ticket to react15 and their tests still fail, but with enzyme 2 they pass.

https://github.com/sontek/enzyme-react16/tree/react15

[1] #1201 (comment)
[2] #1201 (comment)
[3] #1201 (comment)
[4] #1201 (comment)

@palaniichukdmytro
Copy link

palaniichukdmytro commented Oct 31, 2017

with shallow component the simulate works as expected and it update() not necessary with simulate, but with mount it deals with real dom and jsdom so the event system kicks out the real event with real nodes, so current solution with mount for me

wrapper.find('component').props().onChange(eventObject('foo')));  
wrraper.update()

With shallow you can use simulate as expected

@lukeggchapman
Copy link

lukeggchapman commented Nov 8, 2017

I'm not sure if this is the same issue, but I thought it would help with investigation.

The below test case fails for me with:

  • enzyme@3.1.1
  • enzyme-adapter-react-16@1.0.4 (also with 1.0.6 built from source)
  • react@16
  • react-dom@16
  • react-test-renderer@16
import { shallow } from 'enzyme';
import React from 'react';

class Test extends React.Component {
    render () {
        return null;
    }
}

class Wrapper extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            somethingHappened: false,
            componentDidUpdateWasCalled: false,
        };

        this.someHandler = this.someHandler.bind(this);
    }

    componentDidUpdate() {
        !this.state.componentDidUpdateWasCalled && this.setState({
            componentDidUpdateWasCalled: true,
        });
    }

    someHandler() {
        this.setState({ somethingHappened: true });
    }

    render() {
        return <Test onSomething={this.someHandler} />;
    }
}

describe('enzyme test', () => {
  it('calls componentDidUpdate', () => {
    const wrapper = shallow(<Wrapper />);

    wrapper.find(Test).prop('onSomething')();
    wrapper.update(); // makes no difference - not required for react 15 passing test

    expect(wrapper.state('somethingHappened')).toBe(true); // passes
    expect(wrapper.state('componentDidUpdateWasCalled')).toBe(true); // fails
  });
});

It passes with:

  • enzyme@3.1.1
  • enzyme-adapter-react-15@1.0.5
  • react@15
  • react-dom@15
  • react-test-renderer@15

@onchainguy-btc
Copy link

onchainguy-btc commented Nov 14, 2017

For me the following code does not work:

const wrapper = mount(<Foo />);
const button = wrapper.find('button');
const span = wrapper.find('span');
button.simulate('click');
expect(span).toHaveText('clicked');

but this does:

const wrapper = mount(<Foo />);
const button = wrapper.find('button');
button.simulate('click');
expect(wrapper.find('span')).toHaveText('clicked');

Placing a wrapper.update() somewhere in between has no effect. So I am only able to fix it by calling .find() again on the root element after triggering the event.

@ljharb
Copy link
Member

ljharb commented Nov 15, 2017

@seb0zz that's intentional in v3; you now always have to re-find from the root every time if there's been changes.

@onchainguy-btc
Copy link

onchainguy-btc commented Nov 15, 2017

@ljharb okay why is that? Don't think this is really intuitive 😞

@ljharb
Copy link
Member

ljharb commented Nov 15, 2017

It's in the migration guide; it makes found subwrappers immutable, which simplifies the code and makes the code easier to reason about: https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3.md#element-referential-identity-is-no-longer-preserved

@onchainguy-btc
Copy link

@ljharb thanks, get the point. Nevertheless, the side effect

that enzyme no longer has access to the actual object references

is pretty confusing from my point of view. Wrote like a few 100 unit tests with enzyme and found having the reference quit comfortable 😉

@Darper
Copy link

Darper commented Feb 2, 2018

Any suggestions on what to do with something like this? I've tried every suggestion I can find online and nothing has worked.

const component = mount(
  <CheckboxGroup title={"CheckboxGroup test"}>
    <Checkbox name={"1"} />
    <Checkbox name={"2"} />
    <Checkbox name={"3"} />
    <Checkbox name={"4"} />
  </CheckboxGroup>
);

component.childAt(1).simulate("click");

expect(component.state().totalChecked).toBe(1);

Update: I had to change the path to find the correct child to click (component.childAt(0).childAt(1).simulate("click");), and then call .update() post click (component.update();). This is resolved.

@cavaloni
Copy link

Is there a suggested way of writing tests with this update that doesn't require re-writing the code to find elements? In my case, an input element is having its prop changed after a change event fires. To make it pass, I need to find the element before and after. I'm not entirely sure yet how this is desirable behavior, but I am totally open to seeing how it is. Just wanting to learn the best pattern for it now. Thanks!

@jcrben
Copy link

jcrben commented Mar 29, 2018

Is there a suggested way of writing tests with this update that doesn't require re-writing the code to find elements?

@cavaloni I'm pretty sure the answer is no. You need to refind the element - see https://github.com/airbnb/enzyme/blob/0fe0d730ef4f27e99144e5be7ec292f3ce014003/docs/guides/migration-from-2-to-3.md#for-mount-updates-are-sometimes-required-when-they-werent-before

...This has many advantages, but one of the side effects is that now the intermediate representation does not receive automatic updates.

@jcrben
Copy link

jcrben commented Mar 29, 2018

From all the chatter above, it seems like a fair number of comments are just confusion around the 3 breaking changes? @ljharb to be honest I found that migration guide really hard to follow - it reads more on the implementation detail level - even re-reading it now it's hard for me to pull the message "you now always have to re-find from the root every time if there's been changes" from https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3.md#element-referential-identity-is-no-longer-preserved which focuses on a discussion around removing duplicates

Pulling https://github.com/sontek/enzyme-react16/tree/react15 down and switching to the react15 branch, it did seem like 3.0.0 failed and 2.9.1 succeeded - note that you need to be on the react15 branch since 2.9.1 does not work with v16 (you get TypeError: Cannot read property 'getPublicInstance' of undefined)

I decided to give bisecting (left some comments in the channel) a try following https://web.archive.org/web/20150830120011/https://medium.com/@rasjani/debugging-3rd-party-npm-module-regression-d10530f2b67 but for now I've give up - the steps are tricky (switching node versions, lerna run build, etc) - might circle back around but this bug isn't biting me (yet)

@ljharb
Copy link
Member

ljharb commented Mar 29, 2018

@jcrben PRs to improve the migration guide are welcome! I’m quite sure it’s not perfect :-)

The same commit won’t work with both enzyme 2 and 3; the important part is to get things passing on enzyme 3 before changing your react version.

@ljharb
Copy link
Member

ljharb commented Jul 6, 2018

I believe this is answered; PRs to the migration guide/docs, and new issues if needed, are both welcome and encouraged.

@ljharb ljharb closed this as completed Jul 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests