Hello!
With enzyme version ^2.9.1.
Given a test written as such:
it('fires onFocus handler when user clicks away from input', () => {
const component = mount(<Component />, { context });
component.instance().onFocus = jest.fn();
component.find('input').simulate('focus');
expect(component.instance().onFocus).toHaveBeenCalledTimes(1);
});
And the component render (only relevant part left in):
<input
onFocus={this.onFocus}
type="search"
maxLength="100"
/>
The test states that the onFocus callback has never been called. However, the following works as expected:
<input
onFocus={() => this.onFocus()}
type="search"
maxLength="100"
/>
Thank you.
I would discourage using simulate at all; try component.find('input').prop('onFocus')() instead.
@ljharb just wondering why simulate is discouraged, I've see that comment before but just wondering why..
@dmitrif also suggestion, don't couple your tests to implementation details. Even if it's "not likely" doesn't matter; searching on an input tag couples your test to the DOM or virtual DOM. I suggest using a test css marker. Add a cssClass="ft-input-name" or something..whatever makes sense and do a find on that; that way if someone decides to change how input is implemented or use a different kind of component, your tests won't break. You don't want tests breaking for the wrong reason, all you care is a) that it's there and you can get to whatever, and b) you can call whatever on it
@dschinkel because it doesn't actually faithfully simulate anything; it's simpler to just invoke the prop function directly.
thanks @ljharb, I always appreciate your responses.
Just found this. I want to chime in and say if you do use this solution,component.find('input').prop('onFocus')(), you will lose access to the event object in the callback. To fix this would be to use component.find('input').simulate('focus').
@jonmajorc that's desirable; you should pass your own event object to the invoked prop rather than use simulate.
@ljharb When you say "it doesn't faithfully simulate anything". Can you elaborate a bit? Why doesn't it?
For one, events don't bubble; for another, simulating "change" doesn't also trigger all the key events that led to it.
Invoking a prop explicitly, and passing your own event object, is the proper way to test things.
@ljharb
component.find('input').prop('onFocus')
Will this work in case we are mounting parent component and input component is nested one level down
for eg:
const Parent = (props) => {
return (
<div>
<input onFocus={randomFunction}/>
</div
)
}
@aqumus try it and see :-) it should work fine.
I did tried this and wasn't working for me,so basically ( for enzyme 2.7) :
PS: I was trying to find input by doing
wrapper.find('.some-random-class input')
@aqumus what about in enzyme 3? The behavior around selectors is vastly improved.
Using prop('onFocus') and prop('onBlur') does not trigger the proper function call.
it('should call handleInputFocus when input focuses', () => {
let wrapper = shallow(<Timestamp />)
let component = wrapper.instance()
let mock = jest.fn()
component.handleInputFocus = mock
component.forceUpdate()
wrapper.update()
wrapper.find('input').prop('onFocus')
expect(mock).toHaveBeenCalled()
})
This throws an error when running jest
@ljharb If using .simulate is discouraged then shouldn't it be removed from all the documentation in replacement for .prop() ? Or at least include the clarifications that you mention there
@reyronald Yes, I'd like to see that happen; but that requires consensus from all the maintainers.
@GarrettGeorge that's because you're trying to spy/mock after the wrapper is created. For spies to work properly, you must spy on everything before creating the wrapper in the first place. (One common mistake is using arrow functions in class properties; don't do that)
@ljharb Does that mean that the rest of the maintainers don't necessarily agree that using .simulate() is potentially a bad practice? Or just that it hasn't really been discussed seriously? What do you think it would take to push that forward?
After reading this thread my eyes twitch every time I see a simulate in any code sample in the repo :/, would like to see that changed or at least addressed properly in the docs.
Probably a little bit of both.
Closing; please file a new issue if needed.
What's wrong with simulate - if your onChange handlers are inline in the component, what other way is there to check the logic?
@QuantumInformation .prop('onChange')()
Even though the name would imply this simulates an actual event, .simulate() will in fact target the component's prop based on the event you give it. For example, .simulate('click') will actually get the onClick prop and call it.
Based on the documentation, simulate('click') and .prop('onClick')() should work in the same way, I guess.
Most helpful comment
I would discourage using simulate at all; try
component.find('input').prop('onFocus')()instead.