-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
window.addEventListener not triggered by simulated events #426
Comments
Ahh, so I found a workaround:
e.g. it('only triggers clickOutside handler when clicking outside component', t => {
const onClickOutside = sinon.spy()
mount(<Page onClickOutside={onClickOutside} />, { attachTo: document.body })
simulant.fire(document.body.querySelector('aside'), 'click')
t.equal(onClickOutside.callCount, 1, 'should fire when clicking menu sibling')
document.body.innerHTML = ''
t.end()
}) However, I've just noticed that in this particular example We can just it('only triggers clickOutside handler when clicking outside component', t => {
const onClickOutside = sinon.spy()
ReactDOM.render(<Page onClickOutside={onClickOutside} />, document.body)
simulant.fire(document.body.querySelector('aside'), 'click')
t.equal(onClickOutside.callCount, 1, 'should fire when clicking menu sibling')
document.body.innerHTML = ''
t.end()
}) Curious now as to why not use just always use this method? why enzyme? |
Can a core contributor respond to this? |
@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 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 |
So you would attach an onClick on the main root component, and then passing it to all components? Maybe using the context then? |
That may be a good use case for |
@aweary , would you mind giving a bit more information about how to tackle those window/body event listeners using context? I've been searching endlessly as to how's the best way of doing it and I don't have an answer yet. I'm having exactly the same problem as @timoxley , but with a KeyPress event... It's proving to be extremely frustrating to test. Thank you |
@rpmonteiro if you were using class App extends React.Component {
getChildContext() {
onKeyPress: this.onKeyPress
}
onKeyPress(event) {
// handle event here
}
} Then in some child component class SomeChildComponentOfApp extends React.Component {
static contextTypes = {
// the onKeyPress function is now available via `this.context.onKeyPress`
onKeyPress: React.PropTypes.func
}
} If you absolutely must use the native event system, you might look into another library for mocking |
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. const map = {};
Window.addEventListener = jest.genMockFn().mockImpl((event, cb) => {
map[event] = cb;
});
// render component
map.event(...args);
// assert changes |
@blainekasten you saved my day :) const map = {};
window.addEventListener = jest.genMockFn().mockImpl((event, cb) => {
map[event] = cb;
});
const component = mount(<SomeComponent />);
map.mousemove({ pageX: 100, pageY: 100}); This worked for me, the state of the component is successfully being updated. |
just as a small update and FYI, for document this is working for me, and on a newer version of jest:
|
Component:
Test:
|
I'm sorry I am a newbie to react and jest. I didn't understand what is happening here
|
@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. |
Inspired by @kellyrmilligan's solution here's full implementation I use to detect ESC keydown (also useful for any other event type): it('calls the dismiss callback on ESC key', () => {
const KEYBOARD_ESCAPE_CODE = 27;
const mockDismissCallback = jest.fn();
// Declaring keydown prop so the linter doesn't compain
const eventMap = {
keydown: null,
};
document.addEventListener = jest.fn((event, cb) => {
eventMap[event] = cb;
});
// MyModalComponent internally uses
// document.addEventListener('keydown', this.onModalDialogKeyDown, false);
// which then via onModalDialogKeyDown binding does some stuff and then calls onDismiss which
// is really mockDismissCallback
const modal = shallow(
<MyModalComponent isOpen={true} onDismiss={mockDismissCallback}>
Test
</MyModalComponent>
);
eventMap.keydown({ keyCode: KEYBOARD_ESCAPE_CODE });
expect(mockDismissCallback.mock.calls.length).toEqual(1);
}); |
Testing a React app I had to simulate a string of text input, arrived at this solution using sinon:
|
This also works without having to mock
A little further explanation: |
@Faline10, that's awesome! As a note to others, I had to do |
I am getting Event is undefined. |
@Faline10 Trying to implement your method. But sporadically running into this error when the test is run.
Would be grateful to anyone who can help. |
@feargalObo In this case, |
@feargalObo let's move these comments to a new issue. |
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?
The text was updated successfully, but these errors were encountered: