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'));
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" />
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. 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.
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?
I'm seeing this same behavior in 2.3.0
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.
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?
Has this commit made it into an updated npm package? I'm still seeing it in 2.7.1.
Have the same problem on 3.0.0 with react 0.16, .html() shows the class is in there but hasClass returns false.
I'm also seeing it in 3.1.0.
@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.
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.
@rooch84 that's what .dive() is for.
@ljharb But I thought .dive() only worked on shallow?
@rooch84 ah, true. then yes, if you're using mount, you'll need to find your component first.
I have the same issue whilst using [email protected] and [email protected].
> 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('...')
yes; wrapper.html() returns the inner HTML, not the outer HTML. wrapper.debug() will show this.
Most helpful comment
I'm seeing this same behavior in 2.3.0