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 does not work on root ReactWrapper #134

Closed
amccloud opened this issue Jan 22, 2016 · 17 comments
Closed

hasClass does not work on root ReactWrapper #134

amccloud opened this issue Jan 22, 2016 · 17 comments

Comments

@amccloud
Copy link

I think this is because MountedTraversal#instHasClassName short circuits to false if the instance does not pass the isDOMComponent check. Could we relax the check?

Example test case (React 0.14):

const shallowComponent = shallow(<Video src="example.mp4" />);
const mountComponent = mount(<Video src="example.mp4" />);

// From ShallowTraversal
// Works!
assert.equal(shallowComponent.node.props.className, 'Video');

// Works!
assert.isTrue(shallowComponent.hasClass('Video'));

// Fails :( because isDOMComponent(mountComponent.node) is false
// Would otherwise pass
assert.isTrue(mountComponent.hasClass('Video'));

// From MountedTraversal (without isDOMComponent check)
// Works!
assert.equal(findDOMNode(mountComponent.node).className, 'Video');

// Works! ... but seems unnecessary
assert.isTrue(mountComponent.find('.Video').hasClass('Video'));
@lelandrichardson
Copy link
Collaborator

hmmm. @amccloud this is an interesting side effect of how both react + enzyme is implemented. The react render tree is sort of a two-dimensional graph, where-as in shallow it is one-dimensional. It's hard to know what the right implementations are.

I need to look at what the side effects of this are, but I suspect relaxing the isDOMComponent requirement will end up doubling the number of results getting returned for class-based selection...

const Bar = () => (
  <div className="bar" />
);
const Foo = () => (
  <div><Bar /></div>
);

const wrapper = mount(<Foo />);
// with your suggested change:
wrapper.find('.bar');
// <Bar />
// <div className="bar" />
// right now it does this...
wrapper.find('.bar');
// <div className="bar" />

@dozoisch
Copy link

I have the exact same issue. Was working with < 1.6. I've also tested on 2.0.0, same issue. Using react 0.14

Here is a minimal example to repro

const TestComponent = ({ className }) => (
  <div className={className}/>
);

describe('TestComponent', () => {
  it('mount allows us to set custom props', () => {
    const wrapper = mount(<TestComponent className="test" />);
    assert(wrapper.hasClass('test')); // false
  });
  it('shallow allows us to set custom props', () => {
    const wrapper = shallow(<TestComponent className="test" />);
    assert(wrapper.hasClass('test')); // true, as expected
  });
});

Note:

  • .props() returns an empty object in mount, but not in shallow
  • .debug()returns an empty string in mount, but expected string in shallow.

@lelandrichardson
Copy link
Collaborator

Hmm. This seems like a real bug. Will try to take a look at this ASAP. It may be worthwhile to put the fix in as a patch to 1.x branch as well.

@ryanalane
Copy link

The crux of this behavior seems to be various incompatibilities with mounting Stateless Function Components, due to them not having proper react instances by design, but which enzyme relies on to render certain values. #220 contains workaround fixes for props() and debug() to return useful values for SFCs.

However, hasClass() and its cousins in MountedTraversal are all blocked behind the same isDOMComponent check as @amccloud mentioned. Should we consider a different checking strategy to allow SFCs to have fully-functional wrappers when mounted?

@egeste
Copy link

egeste commented May 13, 2016

I'm seeing this same behavior in 2.3.0

@devrelm
Copy link

devrelm commented Oct 7, 2016

I'm still seeing this in 2.4.1 as well. I'm not sure if it's for the same reason, but it has the same behavior of hasClass working with shallow, but not mount.

@christian-schulze
Copy link
Contributor

I just spent hours trying to figure out why tests were failing using hasClass. wrapper.html() shows me the class is there, and yet hasClass still returns false. This is really frustrating and unexpected behaviour, which wastes everybody's time.

May I suggest we document this in the readme, with some alternatives for checking if the wrapper has a class?

@nibblesnbits
Copy link

Has this commit made it into an updated npm package? I'm still seeing it in 2.7.1.

@jacek213
Copy link

jacek213 commented Oct 2, 2017

Have the same problem on 3.0.0 with react 0.16, .html() shows the class is in there but hasClass returns false.

@rooch84
Copy link

rooch84 commented Oct 31, 2017

I'm also seeing it in 3.1.0.

@ljharb
Copy link
Member

ljharb commented Oct 31, 2017

@jacek213 @rooch84 can you file a new issue for enzyme 3?

Note that this is likely an issue with the fact that shallow wrappers represent what the component renders, but mounted wrappers represent the component itself.

@rooch84
Copy link

rooch84 commented Oct 31, 2017

I think I've finally figured out my issue - because I'm using Meteor, the react components are wrapped with a ReactMeteorDataComponent. So I need to find my component first before I can then inspect its classes, props, etc.

@ljharb
Copy link
Member

ljharb commented Oct 31, 2017

@rooch84 that's what .dive() is for.

@rooch84
Copy link

rooch84 commented Oct 31, 2017

@ljharb But I thought .dive() only worked on shallow?

@ljharb
Copy link
Member

ljharb commented Oct 31, 2017

@rooch84 ah, true. then yes, if you're using mount, you'll need to find your component first.

@josh08h
Copy link

josh08h commented Nov 8, 2017

I have the same issue whilst using React@15.5.4 and Enzyme@3.1.1.

> wrapper.html()
"<div class="fixedDataTableCellLayout_wrap1 public_fixedDataTableCell_wrap1 Cells__closedAccount___q0Nnb Cells__attributeCell___2-rsR"><div class="fixedDataTableCellLayout_wrap2 public_fixedDataTableCell_wrap2"><div class="fixedDataTableCellLayout_wrap3 public_fixedDataTableCell_wrap3"><div class="public_fixedDataTableCell_cellContent"><div title="CR6737" class="data_fb_panel" data-fb-panel="ref" style="width: 206px;">CR6737</div></div></div></div></div>"


>wrapper.hasClass('Cells__closedAccount___q0Nnb')
false

I guess I have to find the component first i.e. wrapper.find('div').first().hasClass('...')

@ljharb
Copy link
Member

ljharb commented Nov 8, 2017

yes; wrapper.html() returns the inner HTML, not the outer HTML. wrapper.debug() will show this.

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