What is @testing-library/user-event?
The @testing-library/user-event package is a library built on top of @testing-library/dom that simulates user interactions with the DOM. It allows developers to write tests that mimic user behavior in a way that is consistent with how a user would use a web application. This helps ensure that tests are realistic and that the application will work as expected when real users interact with it.
What are @testing-library/user-event's main functionalities?
Typing text into an input element
Simulates typing text into an input field or other element that can receive input.
userEvent.type(screen.getByRole('textbox'), 'Hello, World!')
Clicking elements
Simulates a click event on a button, link, or any other clickable element.
userEvent.click(screen.getByText('Submit'))
Selecting options in a select element
Simulates selecting an option from a dropdown select element.
userEvent.selectOptions(screen.getByRole('combobox'), 'optionValue')
Uploading files
Simulates a file upload interaction on an input of type file.
userEvent.upload(screen.getByLabelText('Upload'), file)
Tabbing through elements
Simulates tabbing through focusable elements on the page.
userEvent.tab()
Other packages similar to @testing-library/user-event
selenium-webdriver
Selenium WebDriver is a browser automation library. It is more comprehensive than @testing-library/user-event as it can control the browser at a lower level, but it is also more complex and can be overkill for simple DOM interaction tests.
cypress
Cypress is an end-to-end testing framework that includes a rich set of methods for simulating user events. It is similar to @testing-library/user-event but is designed for full end-to-end testing and offers a complete testing environment with a browser-based UI.
puppeteer
Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. It is used for browser automation and can perform actions similar to @testing-library/user-event, but it operates at the browser level and can be used for tasks beyond testing, such as web scraping or generating pre-rendered content.
The problem
From
testing-library/dom-testing-library#107:
[...] it is becoming apparent the need to express user actions on a web page
using a higher-level abstraction than fireEvent
The solution
user-event
tries to simulate the real events that would happen in the browser
as the user interacts with it. For example userEvent.click(checkbox)
would
change the state of the checkbox.
The library is still a work in progress and any help is appreciated.
Installation
With NPM:
npm install @testing-library/user-event --save-dev
With Yarn:
yarn add @testing-library/user-event --dev
Now simply import it in your tests:
import userEvent from "@testing-library/user-event";
var userEvent = require("@testing-library/user-event");
API
click(element)
Clicks element
, depending on what element
is it can have different side
effects.
import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("click", () => {
const { getByText, getByTestId } = render(
<div>
<label htmlFor="checkbox">Check</label>
<input id="checkbox" data-testid="checkbox" type="checkbox" />
</div>
);
userEvent.click(getByText("Check"));
expect(getByTestId("checkbox")).toHaveAttribute("checked", true);
});
dblClick(element)
Clicks element
twice, depending on what element
is it can have different
side effects.
import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("double click", () => {
const onChange = jest.fn();
const { getByTestId } = render(
<input type="checkbox" id="checkbox" onChange={onChange} />
);
const checkbox = getByTestId("checkbox");
userEvent.dblClick(checkbox);
expect(onChange).toHaveBeenCalledTimes(2);
expect(checkbox).toHaveProperty("checked", false);
});
async type(element, text, [options])
Writes text
inside an <input>
or a <textarea>
.
import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
const { getByTestId } = test("click", () => {
render(<textarea data-testid="email" />);
});
await userEvent.type(getByTestId("email"), "Hello, World!");
expect(getByTestId("email")).toHaveAttribute("value", "Hello, World!");
If options.allAtOnce
is true
, type
will write text
at once rather than
one character at the time. false
is the default value.
options.delay
is the number of milliseconds that pass between two characters
are typed. By default it's 0. You can use this option if your component has a
different behavior for fast or slow users.
selectOptions(element, values)
Selects the specified option(s) of a <select>
or a <select multiple>
element.
import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
const { getByTestId } = render(
<select multiple data-testid="select-multiple">
<option data-testid="val1" value="1">
1
</option>
<option data-testid="val2" value="2">
2
</option>
<option data-testid="val3" value="3">
3
</option>
</select>
);
userEvent.selectOptions(getByTestId("select-multiple"), ["1", "3"]);
expect(getByTestId("val1").selected).toBe(true);
expect(getByTestId("val2").selected).toBe(false);
expect(getByTestId("val3").selected).toBe(true);
The values
parameter can be either an array of values or a singular scalar
value.
tab({shift, focusTrap})
Fires a tab event changing the document.activeElement in the same way the
browser does.
Options:
shift
(default false
) can be true or false to invert tab direction.focusTrap
(default document
) a container element to restrict the tabbing
within.
A note about tab: jsdom does not support tabbing, so this feature is a way to enable tests to verify tabbing from the end user's perspective. However, this limitation in jsdom will mean that components like focus-trap-react will not work with userEvent.tab()
or jsdom. For that reason, the focusTrap
option is available to let you ensure your user is restricted within a focus-trap.
import React from "react";
import { render } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import userEvent from "@testing-library/user-event";
it("should cycle elements in document tab order", () => {
const { getAllByTestId } = render(
<div>
<input data-testid="element" type="checkbox" />
<input data-testid="element" type="radio" />
<input data-testid="element" type="number" />
</div>
);
const [checkbox, radio, number] = getAllByTestId("element");
expect(document.body).toHaveFocus();
userEvent.tab();
expect(checkbox).toHaveFocus();
userEvent.tab();
expect(radio).toHaveFocus();
userEvent.tab();
expect(number).toHaveFocus();
userEvent.tab();
expect(checkbox).toHaveFocus();
});
Contributors
Thanks goes to these wonderful people
(emoji key):
This project follows the
all-contributors
specification. Contributions of any kind welcome!