How To Mock EventListener When Set In ComponentDidMount()
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?
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();
...
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM