Test window keydown event in Reactjs
Asked Answered
F

2

6

The component I am writing needs to change its behaviour depending on whether ctrl is pressed or not.

I use a window.onkeydown event but Simulate from React Test Utils doesn't allow me to dispatch events against window. I've also tried window.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 17 })); but mocha/node does not recognize KeyboardEvent.

Is there a way to test window.onkeydown using React Test Utils? if not, is there a better way to do it in mocha for node?

Here is some code to ilustrate the issue:

describe('On Keydown', () => {
    it('fires the event', () => {
        // Component
        const Component = class extends React.Component {
            constructor(props) {
                super(props);
                this.state = { key: false };
                window.addEventListener('keydown', e => this.setState({ key: true }));
                window.addEventListener('keyup', e => this.setState({ key: false }));
            }
            render() {
                return <span>test</span>
            };
        };
        // Rendering
        const rendered = renderIntoDocument(<Component/>);
        // Firing event
        expect(rendered.state.key).to.equal(false);
        // Error here
        Simulate.keyDown(window, { keyCode: 17 });
        expect(rendered.state.key).to.equal(true);
    });
});
Featherbedding answered 5/8, 2016 at 20:34 Comment(0)
D
1

If you set up your listener like window.addEventListener('keydown', myFunc) then you only need to test myFunc, you don't actually need to test that addEventListener calls your function when a keydown happens.

By always binding events to functions (rather than doing work in a callback) testing is more direct (you're testing your code) and also you can remove event listeners when you're done with them.

Discovert answered 6/8, 2016 at 7:29 Comment(5)
What about testing the fact that the compononent should listen for keydown event? Perhaps that should be the part of the spec. If not, one could remove the addEventListener, and the component would still pass the test.Turbary
Valid point. Personally I rely on automation tests for those sorts of things and let the unit tests test the logic.Discovert
Well enough. It just bugs me a bit that the event handler is basically a private method, and the test relies on it. But it’s not a big deal.Turbary
Out of interest, did you try manually dispatching a plain Event instead of KeyboardEvent?Discovert
I needed the keydown event to trigger a state change that would affect another method's behavior but you gave me 2 ideas, first bypass the event using rendered.setState({key: true}), and second create a class in test_helper.js to reproduce events on window using node Events. I'm adding the second solution as an alternate answer.Featherbedding
F
1

I solved it thanks to David's comment just by ignoring the event and setting the state to what I needed for the test. I also found out a different way to test window events in the future. Creating a window class that extends EventEmitter you can receive keydown/keyup events like ctrl through window.emit('keydown',{keyCode: 17}).

This is the code of my_test_helper.js:

import jsdom from 'jsdom';
import chai from 'chai';
import EventEmitter from 'events';

const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');

const windowClass = class extends EventEmitter {
    constructor() {
        super(doc.defaultView);
        this.__defineSetter__('onkeydown', f => this.on('keydown', f));
        this.__defineSetter__('onkeyup', f => this.on('keyup', f));
    }
    addEventListener (e,f) {
        this.on(e,f);
    }
};

const win = new windowClass();

global.document = doc;
global.window = win;

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key];
  }
});
Featherbedding answered 8/8, 2016 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.