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

hasClass returns false unexpectedly in v3 #1177

Closed
wojtekmaj opened this issue Sep 27, 2017 · 23 comments
Closed

hasClass returns false unexpectedly in v3 #1177

wojtekmaj opened this issue Sep 27, 2017 · 23 comments

Comments

@wojtekmaj
Copy link

wojtekmaj commented Sep 27, 2017

Hi!
I have the following piece of code (simplified), in my Jest/Enzyme based test suite:

    it('toggles calendar visibility on label click', () => {
        const component = (
            <DatePicker />
        );

        const datePicker = mount(component).find('.DatePicker');

        // Opening
        datePicker.find('.DatePicker__button').simulate('click');

        console.log(datePicker.getDOMNode().className);
        console.log(datePicker.hasClass('open'));

        expect(datePicker.hasClass('open')).toBe(true);

        // Closing
        datePicker.find('.DatePicker__button').simulate('click');

        expect(datePicker.hasClass('open')).toBe(false);
    });

I'm getting false on hasClass unexpectedly. The two console.logs I have in the code return as follows:

image

I've tried multiple combinations, like datePicker.getDOMNode().hasClass but these are not working either.

Thanks in advance for help!

@ljharb
Copy link
Member

ljharb commented Sep 27, 2017

Your simulate call isn't rerendering the wrapper; try datePicker.update() after each mutation.

@lelandrichardson
Copy link
Collaborator

you'll actually need to not call datePicker.update() but instead do:

const wrapper = mount(component);
const getDatePicker = () => wrapper.find('.DatePicker');

and do wrapper.update() and getDatePicker() afterwards or something like that. .update() should be called on the root.

@wojtekmaj
Copy link
Author

wojtekmaj commented Sep 27, 2017

Thanks for your answers guys.
Hmmm... So I got to this point, but I'm still unable to get expected results. Interestingly, the first expect() passed without an issue. The second, however, did not. Here's how it looks right now:

    it('toggles calendar on label click', () => {
        const component = (
            <DatePicker  />
        );

        const mountedComponent = mount(component);
        const getDatePicker = () => mountedComponent.find('.DatePicker');

        // Opening
        getDatePicker().find('.DatePicker__button').simulate('click');
        mountedComponent.update();

        expect(getDatePicker().hasClass('open')).toBe(true);

        // Closing
        getDatePicker().find('.DatePicker__button').simulate('click');
        mountedComponent.update();

        console.log(getDatePicker().getDOMNode().className);
        console.log(getDatePicker().hasClass('open'));

        expect(getDatePicker().hasClass('open')).toBe(false);
    });

...and here's what the new console.logs are saying:

image

@lelandrichardson
Copy link
Collaborator

hmm. that is strange. Do you think you'd be able to provide a minimal repro in the form of a test or something?

@wojtekmaj
Copy link
Author

Sure thing. I'll get back to you!

@antonyoneill
Copy link

Could this be related to #1163? I'm seeing some interesting failures along these lines. If you were to console.log(getDatePicker().html() I think you'll find that the element does have the 'open' class.

@tleunen
Copy link

tleunen commented Sep 27, 2017

I'm having the same issue. And it seems .update() doesn't always rerender, or at least doesn't rerender with the updated state for one of its children

@jeremywiebe
Copy link

I hope I'm on the same page here, but it seems like this isn't even related to dynamic updating of the component. mount and shallow show different behaviours.

With this test case I get one pass and one fail.

repro.test.jsx

import {mount, shallow} from 'enzyme'
import React from 'react'

const Comp = () => {
    return (
        <div className="test">
            <p>Hello world</p>
        </div>
    )
}

test('It finds the className on the mount()\'d wrapper', () => {
    const mountWrapper = mount(<Comp />)
    expect(mountWrapper.hasClass('test')).toBe(true)
})

test('It finds the className on the shallow()\'d wrapper', () => {
    const shallowWrapper = shallow(<Comp />)
    expect(shallowWrapper.hasClass('test')).toBe(true)
})
npm test -- repro

 FAIL  ./repro_test.js
  ● It finds the className on the mount()'d wrapper

    expect(received).toBe(expected)

    Expected value to be (using ===):
      true
    Received:
      false

      at Object.<anonymous> (repro_test.js:14:43)
          at Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

  ✕ It finds the className on the mount()'d wrapper (21ms)
  ✓ It finds the className on the shallow()'d wrapper (4ms)

@elyobo
Copy link

elyobo commented Sep 28, 2017

@antonyoneill's #1177 (comment) was correct for me; .hasClass failed, but dumping .html() shows that the expected class does actually show up. Tweaking things to instead do .render().hasClass() instead of just .hasClass() then has the tests passing (using the cheerio wrapper to do the test instead).

@tleunen
Copy link

tleunen commented Sep 28, 2017

The problem seem to be with mount in general, not especially with hasClass or setState, ...
Even forcing an update of the wrapper doesn't make it to rerender with the updated data (or to rerender at all).

I didn't notice any issue with shallow.

@tleunen
Copy link

tleunen commented Sep 28, 2017

Also, the v15 adapter and v16 give different results as well. I upgrade to enzyme 3 with v15, then I'm trying to switch to v16, and some of my tests are failing again. All are using mount, and it seems it's always that the component should be updated but it's not the case. Calling .update() doesn't solve anything.

@aju
Copy link

aju commented Oct 4, 2017

I have this issue as well:
Doesn't work:

 const dropDown = mount(<DropdownContainer {...defaultProps} />);

  expect(dropDown.hasClass('sg-dropdown--opened')).toEqual(false);
  expect(dropDown.state('isOpened')).toEqual(false);

  dropDown.simulate('click');
  expect(dropDown.hasClass('sg-dropdown--opened')).toEqual(true);
  expect(dropDown.state('isOpened')).toEqual(true);

  dropDown.simulate('click');
  expect(dropDown.hasClass('sg-dropdown--opened')).toEqual(false);
  expect(dropDown.state('isOpened')).toEqual(false);

And this one works:

 const dropDown = mount(<DropdownContainer {...defaultProps} />);

  expect(dropDown.hasClass('sg-dropdown--opened')).toEqual(false);
  expect(dropDown.state('isOpened')).toEqual(false);

  dropDown.simulate('click');
  expect(dropDown.find('.sg-dropdown').hasClass('sg-dropdown--opened')).toEqual(true);
  expect(dropDown.state('isOpened')).toEqual(true);

  dropDown.simulate('click');
  expect(dropDown.hasClass('sg-dropdown--opened')).toEqual(false);
  expect(dropDown.state('isOpened')).toEqual(false);

The only difference is adding find here:

expect(dropDown.find('.sg-dropdown').hasClass('sg-dropdown--opened')).toEqual(true);

@nosajoahs
Copy link

@elyobo @antonyoneill mine doesn't even get the class to show up when i do html();
using:

@types/enzyme 3.1.0
enzyme 3.1.0
@types/jest 21.1.4
enzyme-adapter-react-16 1.0.2
@types react 16.0.14

also if i'm using typescript does @types/enzyme take priority over the regular enzyme?

jonmpqts added a commit to react-admin-lte/react-admin-lte that referenced this issue Feb 26, 2018
I'm not exactly sure what is going on here. The fix came from this
issue:

enzymejs/enzyme#1177
@fabioSalimbeni
Copy link

Hi All, any update on this?

@nikitanair
Copy link

Hi All, Getting the same error for finding the classname and it returns false even though the class exists. Any update on this?

@anthonyvialleton
Copy link

anthonyvialleton commented May 22, 2018

Also stucked on this bug...

This is not working :

it('should change state when toggle cart description view', () => {
  const component = (<OrderStickyBottom show/>);
  const descriptionDiv = component.find('div#description-view');

  expect(descriptionDiv.hasClass('selected')).toBeFalsy();

  descriptionDiv.simulate('click');
  expect(descriptionDiv.hasClass('selected')).toBeTruthy();

  descriptionDiv.simulate('click');
  expect(descriptionDiv.hasClass('selected')).toBeFalsy();
});

=> output

Expected value to be truthy, instead received
      false

To fix it I had to chain a find + hasClass on the shallow element (so multiplying selector's calls) :

it('should change state when toggle cart description view', () => {
  const component = shallow(<OrderStickyBottom show/>);
  const descriptionDivId = 'div#description-view';
  const descriptionDiv = component.find(descriptionDivId);

  expect(component.find(descriptionDivId).hasClass('selected')).toBeFalsy();

  descriptionDiv.simulate('click');
  expect(component.find(descriptionDivId).hasClass('selected')).toBeTruthy();

  descriptionDiv.simulate('click');
  expect(component.find(descriptionDivId).hasClass('selected')).toBeFalsy();
});

Using :

enzyme 3.3.0
enzyme-adapter-react-16 1.1.1

Need a fix to improve this..?!

@ljharb
Copy link
Member

ljharb commented Jul 6, 2018

I believe this is actually caused by #1153, and that a wrapper.update() after each change currently would address it.

@ljharb ljharb closed this as completed Jul 6, 2018
@davemcg3

This comment has been minimized.

@ljharb

This comment has been minimized.

@davemcg3

This comment has been minimized.

@renjithspace

This comment has been minimized.

@ljharb

This comment has been minimized.

@renjithspace

This comment has been minimized.

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