Handling Mouse Events in Your React Component Tests
May 28, 2020 • 6 Minute Read
Introduction
Testing a React component will most often involve some type of user interaction happening before you validate the expected outcome. This could include a user typing into a form field, pressing the escape key to close a modal, or clicking on an element. This guide will show you how to use these types of events in your tests.
React Testing Library
An essential tool for writing tests for React components is the React Testing Library. The React Testing Library (RTL) was created to help you write maintainable tests that focus on how your software is being used, instead of implementation details. Go here to learn more about RTL. The examples in this guide will use RTL.
Testing Click Events with FireEvent
When your React component involves a user clicking on an element, you'll want to use the fireEvent method from RTL. To illustrate this behavior, here is a simple component with a button that, when clicked, toggles the view of an image:
import React from 'react';
export default function App() {
const [isVisible, setIsVisible] = React.useState(false);
const handleClick = () => {
setIsVisible(!isVisible);
};
return (
<div className='App'>
<div>
<button onClick={handleClick}>Toggle Image</button>
</div>
<br />
<div style={{ display: isVisible ? 'block' : 'none' }}>
<p>Look at this pretty cat</p>
<img src='https://cataas.com/cat' alt='random cat' />
</div>
</div>
);
}
When this component first renders, the cat image will not be displayed. You can show and hide the image by clicking a Toggle Image button. To test the toggle functionality you would use the fireEvent.click() method:
- Import the fireEvent method from RTL
- Import jest-dom from RTL to use assertions like toBeVisible()
- Use the fireEvent.click(element) method to click an element
import React from 'react';
// 1
import { render, screen, fireEvent } from '@testing-library/react';
// 2
import '@testing-library/jest-dom';
import App from './App';
describe('<App />', () => {
it('Should toggle the cat image div', () => {
render(<App />);
// Before clicking the toggle button, the image is NOT visible
expect(screen.queryByText(/look at this pretty cat/i)).not.toBeVisible();
// 3
fireEvent.click(screen.queryByText(/toggle image/i));
expect(screen.queryByText(/look at this pretty cat/i)).toBeVisible();
});
});
Testing User Input with User-event
When you need to test a component that involves user input, you should use the user-event methods available in the @testing-library/user-event package. user-event is used to simulate real events that would happen in the browser like typing, clicking, or interacting with a select menu. Here's an example component that has the user type in an email address to see if it's valid:
import React from 'react';
export default function Email() {
const [emailAddress, setEmail] = React.useState();
const [hasValidEmail, setHasValidEmail] = React.useState(false);
const handleChange = (event) => {
setEmail(event.target.value);
};
const handleClick = () => {
const regex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
setHasValidEmail(regex.test(emailAddress));
};
return (
<div className='App'>
<div>
<label htmlFor='email'>Email</label>
<input
type='text'
id='email'
name='email'
onChange={handleChange}
value={emailAddress}
/>
<button onClick={handleClick}>Check if email is valid</button>
</div>
{hasValidEmail && emailAddress ? (
<p>Email Address is valid</p>
) : (
<p>Email Address is NOT valid</p>
)}
</div>
);
}
To test this component you'll use the type method userEvent.type(), where you'll pass in the input element as the first argument and the value as the second argument:
- Use userEvent.type() to enter an invalid email address
- Use the clear method to clear out the value of an input element
- Type in a valid email address
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Email from './Email';
import '@testing-library/jest-dom';
describe('<Email />', () => {
it('Should validate the email address typed by the user', () => {
render(<Email />);
// 1
userEvent.type(screen.queryByLabelText(/email/i), 'ham');
fireEvent.click(
screen.queryByRole('button', { name: /Check if email is valid/i }),
);
expect(screen.queryByText(/email address is not valid/i)).toBeVisible();
// 2
userEvent.clear(screen.queryByLabelText(/email/i));
// 3
userEvent.type(screen.queryByLabelText(/email/i), '[email protected]');
fireEvent.click(
screen.queryByRole('button', { name: /check if email is valid/i }),
);
expect(screen.queryByText(/email address is not valid/i)).toBeNull();
expect(screen.queryByText(/email address is valid/i)).toBeVisible();
});
});