Let’s say we have <ModalContainer>
that renders react-modal:
// ModalContainer.js
import React, { Component } from 'react';
import Modal from 'react-modal';
class ModalContainer extends Component {
state = {
isModalOpen: false,
};
toggleModal = () => {
this.setState({
isModalOpen: !this.state.isModalOpen,
});
};
render() {
return (
<div>
<button onClick={this.toggleModal}>Open Modal</button>
<Modal
isOpen={this.state.isModalOpen}
onRequestClose={this.toggleModal}
>
{this.props.children}
</Modal>
</div>
);
}
}
export default ModalContainer;
How would we write a test for the modal component?
Enzyme find
You can use enzyme find
to verify that react-modal renders:
// ModalContainer.test.js
import { shallow } from 'enzyme';
import Modal from 'react-modal';
import ModalContainer from './ModalContainer';
import React from 'react';
it('renders react-modal', () => {
const wrapper = shallow(<ModalContainer />);
expect(wrapper.find(Modal)).toHaveLength(1);
});
Or test that react-modal prop isOpen
changes:
it('opens modal when button is clicked', () => {
const wrapper = shallow(<ModalContainer />);
expect(wrapper.find(Modal).prop('isOpen')).toBe(false);
wrapper.find('button').simulate('click');
expect(wrapper.find(Modal).prop('isOpen')).toBe(true);
});
Or check that react-modal children
matches what you expect:
it('renders childen when modal is open', () => {
const wrapper = shallow(<ModalContainer>modal content</ModalContainer>);
expect(wrapper.find(Modal).prop('children')).toBe('modal content');
// expect(wrapper.find(Modal).prop('children')).toMatchSnapshot();
});
However, these tests only test implementation details, which has low value.
So how can we write more valuable tests that confirm the modal content is rendered when it’s opened?
We could try something like this:
it('renders content when modal is open', () => {
const wrapper = shallow(<ModalContainer>modal content</ModalContainer>);
wrapper.find('button').simulate('click');
expect(wrapper.find(Modal).text()).toBe('modal content');
});
But it ends up failing:
✕ renders content when modal is open
expect(received).toBe(expected)
Expected value to be (using ===):
"modal content"
Received:
"<Modal />"
27 | it('renders content when modal is open', () => {
28 | const wrapper = shallow(<ModalContainer>modal content</ModalContainer>);
29 | wrapper.find('button').simulate('click');
> 30 | expect(wrapper.find(Modal).text()).toBe('modal content');
31 | });
To fix it, we can do one of the following:
- mount the element and get the modal text,
- or get the modal node’s innerText.
mount
The advantage of enzyme mount
is it renders the element to an actual DOM (browser or jsdom):
import { mount } from 'enzyme';
// ...
it('renders content when modal is open', () => {
// mount renders to the dom (real or mocked)
const wrapper = mount(<ModalContainer>modal content</ModalContainer>);
wrapper.find('button').simulate('click');
// element text
expect(wrapper.find(Modal).text()).toBe('modal content');
});
node.innerText
instance
To get react-modal node
(which is an actual DOM node), you need to get the modal instance
:
it('renders content when modal is open', () => {
// mount renders to the dom (real or mocked)
const wrapper = mount(<ModalContainer>modal content</ModalContainer>);
wrapper.find('button').simulate('click');
// instance node
const instance = wrapper.find(Modal).instance();
expect(instance.node.innerText).toBe('modal content');
});
Then you can call innerText
on the node to get the rendered text.
findDOMNode
Since react-modal has a portal property, you can use findDOMNode
to get the node:
import { findDOMNode } from 'react-dom';
// ...
it('renders content when modal is open', () => {
// mount renders to the dom (real or mocked)
const wrapper = mount(<ModalContainer>modal content</ModalContainer>);
wrapper.find('button').simulate('click');
// findDOMNode
const portalNode = findDOMNode(instance.portal);
expect(portalNode.innerText).toBe('modal content');
});
Demo
The code and tests can be found on CodeSandbox: