I want write a test using Jest and Enzyme that tests a component that has an event listener, such that when a mousedown event occurs outside of a given html element, a change occurs (in this example, a state value is toggled). I am using jest to mock the event listener and simulate the mousedown event, however, when I try to run this test, I am getting the following error message:
TypeError: Failed to execute 'contains' on 'Node': parameter 1 is not of type 'Node'.
Obviously I think that <div />
is not the right value I should be providing when I simulate my mousedown event with map.mousedown({ target: <section /> });
. How do I properly simulate a mousedown event that occurs outside of my component? You can see the code below. Thanks!
sampleComponent.jsx:
import * as React from 'react';
const SampleComponent = () => {
const [state, setState] = React.useState(false);
const ref = React.useRef(null);
const handleClickOutside = (event) => {
if (
!ref.current.contains(event.target)
) {
setState(true);
}
};
React.useEffect(() => {
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
});
return (
<div
id="sample-div"
ref={ref}
>
{`State value is: ${state}`}
</div>
);
};
export default SampleComponent;
sampleComponent.test.jsx:
import * as React from 'react';
import { mount } from 'enzyme';
import SampleComponent from './sampleComponent';
const setup = () => mount(<SampleComponent />);
test('testing mousedown', () => {
const map = {};
document.addEventListener = jest.fn((event, callback) => {
map[event] = callback;
});
const wrapper = setup();
expect(wrapper.find('#sample-div').text()).toBe('State value is: false');
map.mousedown({ target: <section /> });
expect(wrapper.find('#sample-div').text()).toBe('State value is: true');
});
You can't pass react JSX as the value of event.target
. Since you use document.addEventListener
, it's native DOM event. You need pass a native DOM, like document.createElement('a')
.
Here is the tests cases for your example:
sampleComponent.tsx
:
import React from 'react';
const SampleComponent = () => {
const [state, setState] = React.useState(false);
const ref = React.useRef(null);
const handleClickOutside = event => {
if (!(ref.current as any).contains(event.target)) {
setState(true);
}
};
React.useEffect(() => {
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
});
return (
<div id="sample-div" ref={ref}>
{`State value is: ${state}`}
</div>
);
};
export default SampleComponent;
sampleComponent.test.tsx
:
import React from 'react';
import { mount } from 'enzyme';
import SampleComponent from './sampleComponent';
import { act } from 'react-dom/test-utils';
const setup = () => mount(<SampleComponent />);
beforeEach(() => {
jest.resetAllMocks();
});
test('testing mousedown - 1', () => {
const map: any = {};
document.addEventListener = jest.fn((event, callback) => {
map[event] = callback;
});
const wrapper = setup();
expect(wrapper.find('#sample-div').text()).toBe('State value is: false');
act(() => {
map.mousedown({ target: document.createElement('a') });
});
expect(wrapper.find('#sample-div').text()).toBe('State value is: true');
expect(document.addEventListener).toBeCalledTimes(2);
});
test('testing mousedown - 2', () => {
const map: any = {};
document.addEventListener = jest.fn((event, callback) => {
map[event] = callback;
});
const wrapper = setup();
expect(wrapper.find('#sample-div').text()).toBe('State value is: false');
act(() => {
map.mousedown({ target: wrapper.getDOMNode() });
});
expect(wrapper.find('#sample-div').text()).toBe('State value is: false');
expect(document.addEventListener).toBeCalledTimes(1);
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58610112/sampleComponent.test.tsx (8.041s)
✓ testing mousedown - 1 (46ms)
✓ testing mousedown - 2 (3ms)
---------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
sampleComponent.tsx | 100 | 100 | 100 | 100 | |
---------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 9.5s
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.