enzyme: window.addEventListener not triggered by simulated events
The first component I tried to test using this was a mixin for detecting clicks outside a component. In order to do this one needs to listen with window.addEventListener('click')
. This handler doesn’t appear to be triggered when using enzyme simulated clicks.
If handling this is out of scope for enzyme, do you have a recommendation on the best way to get this under test?
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 21 (4 by maintainers)
Commits related to this issue
- build(Jest-coverage): ignore scroll-related code - testing-library/react-testing-library#671 - enzymejs/enzyme#426 - [Cypress](https://github.com/testing-library/cypress-testing-library) - [Puppeteer... — committed to sabertazimi/blog by sabertazimi 3 years ago
- build(Jest-coverage): ignore scroll-related code - testing-library/react-testing-library#671 - enzymejs/enzyme#426 - [Cypress](https://github.com/testing-library/cypress-testing-library) - [Puppeteer... — committed to sabertazimi/blog by github-actions[bot] 3 years ago
Generally any test framework(jest, mocha, etc) can solve your problem natively. Your goal here is effectively to make sure the event is bound, and that when its fired something happens in your component. So you’ll have to do some setup prior to rendering, but it is definitely possible to test this code without using context.
To be clear, @aweary is spot on in saying this is not enzyme supported.
For example in jest this is a sort of code you could use.
just as a small update and FYI, for document this is working for me, and on a newer version of jest:
This also works without having to mock
window.addEventListener
, due to all the reasons mentioned above. Test framework: jest + enzyme.A little further explanation:
wrapper.find().instance()
returns a DOM element (whereaswrapper.instance()
would just return the Toast class)–this gives us access toEventTarget.dispatchEvent()
, which you can use to dispatch non-synthetic events, rather than mocking outwindow.addEventListener
. And by adding another div to the document.body, then attaching the mounted wrapper, you ensure the real element event will bubble up to the actual window. (Assumption is that you have a click listener on the window.) Note that I also tried attaching directly to thedocument.body
, as in the first comment, but React then throws “Rendering components directly into document.body is discouraged.”@blainekasten you saved my day 😃
This worked for me, the state of the component is successfully being updated.
Ahh, so
mount
doesn’t actually attach the rendered fragment to the document, so even if simulated events did bubble up through the concrete DOM, they wouldn’t reachwindow
anyway.I found a workaround:
document.body
simulant
to fire “real” DOM events.e.g.
However, I’ve just noticed that in this particular example
enzyme
isn’t actually doing anything haha.We can just
ReactDOM.render
into our JSDOMdocument.body
directly:Curious now as to why not use just always use this method? why enzyme?
@prasadmsvs please file a new issue rather than commenting on a new one; but for this kind of question, the gitter channel linked in the readme is preferred.
@Faline10, that’s awesome! As a note to others, I had to do
.dispatchEvent(new Event('click', { bubbles: true}))
to make it bubble to the windowCan a core contributor respond to this? I’m having the same issue now where instead of attaching a event on a direct element, I’m attaching it on the document so that I can easily detect a click outside of the said element. But I’m having hard time testing this behavior with enzyme.
@Aweary @lelandrichardson
Inspired by @kellyrmilligan’s solution here’s full implementation I use to detect ESC keydown (also useful for any other event type):
ev = new Event('click');
I am getting Event is undefined.
@tleunen I doubt this is a use case we’d support, enzyme is meant to test React components and attaching an event listener to the document with
addEventListener
means the event is not being handled by React’s synthetic event system. Oursimulate
method formount
is a thin wrapper aroundReactTestUtils.Simulate
, which only deals with React’s synthetic event system.I can’t speak to your specific use case, but I would advise that this is generally an anti-pattern in React and should be handled within React’s event system when possible (such as passing down an
onClick
prop from a stateful parent and calling it in the leaf component’sonClick
handler). You can try workaround like @timoxley but your mileage may vary.