Ad

How To Mock EventListener When Set In ComponentDidMount()

- 1 answer

Thats my component

class Foo extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            ...
        };

        this.input = React.createRef();
    }

    componentDidMount() {
        const id = 'bar';
        let element = document.getElementById(id);
        element.addEventListener('transitionend', () => {
            this.setState({ ... });
        }, false);
    }

    ...

When I set up my test like so

import React from 'react';
import { mount } from 'enzyme';
import 'jsdom-global/register';

import Foo from './';

it('renders the component correctly', () => {
    const component = mount(
        <Foo />
    );

    component
        .unmount();
});

I get

console.error node_modules/react-dom/cjs/react-dom.development.js:16647 The above error occurred in the component: in Foo (created by WrapperComponent) in WrapperComponent

Consider adding an error boundary to your tree to customize error handling behavior. ● renders the component correctly TypeError: Cannot read property 'addEventListener' of null

I tried

ReactDOM.render(<Foo />, document.body);

or adding this

const map = {};
Window.addEventListener = jest.genMockFn().mockImpl((event, cb) => {
  map[event] = cb;
});

as well as this

const map = {};
    document.addEventListener = jest.fn((event, cb) => {
      map[event] = cb;
    })

before mounting <Foo /> in the test. But it all comes back with the same error. Why is that?

Ad

Answer

One of reasons why direct DOM access is discouraged in React is because it makes testing more complicated and unpredictable.

DOM can be mocked entirely before mounting a component:

const elementMock = { addEventListener: jest.fn() };
jest.spyOn(document, 'getElementById').mockImplementation(() => elementMock);

A stub can be tested that is was called correctly:

expect(elementMock.addEventListener).toBeCalledWith('transitionend', expect.any(Function), false);

And event listener can be tested that it changes the state as expected:

const handler = elementMock.mock.calls[0][1];
handler();
...
Ad
source: stackoverflow.com
Ad