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
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.
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'
// or
var userEvent = require('@testing-library/user-event')
Clicks element
, depending on what element
is it can have different side
effects.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('click', () => {
render(
<div>
<label htmlFor="checkbox">Check</label>
<input id="checkbox" type="checkbox" />
</div>,
)
userEvent.click(screen.getByText('Check'))
expect(screen.getByLabelText('Check')).toHaveAttribute('checked', true)
})
You can also ctrlClick / shiftClick etc with
userEvent.click(elem, {ctrlKey: true, shiftKey: true})
See the
MouseEvent
constructor documentation for more options.
Clicks element
twice, depending on what element
is it can have different
side effects.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('double click', () => {
const onChange = jest.fn()
render(<input type="checkbox" id="checkbox" onChange={onChange} />)
const checkbox = screen.getByTestId('checkbox')
userEvent.dblClick(checkbox)
expect(onChange).toHaveBeenCalledTimes(2)
expect(checkbox).toHaveProperty('checked', false)
})
Writes text
inside an <input>
or a <textarea>
.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('type', async () => {
render(<textarea />)
await userEvent.type(screen.getByRole('textbox'), 'Hello,{enter}World!')
expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!')
})
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.
The following special character strings are supported:
Text string | Key | Modifier | Notes |
---|---|---|---|
{enter} |
Enter | N/A | Will insert a newline character (<textarea /> only). |
{esc} |
Escape | N/A | |
{backspace} |
Backspace | N/A | Will delete the previous character (or the characters within the selectedRange ). |
{shift} |
Shift | shiftKey |
Does not capitalize following characters. |
{ctrl} |
Control | ctrlKey |
|
{alt} |
Alt | altKey |
|
{meta} |
OS | metaKey |
A note about modifiers: Modifier keys (
{shift}
,{ctrl}
,{alt}
,{meta}
) will activate their corresponding event modifiers for the duration of type command or until they are closed (via{/shift}
,{/ctrl}
, etc.).
We take the same stance as Cypress in that we do not simulate the behavior that happens with modifier key combinations as different operating systems function differently in this regard.
Uploads file to an <input>
. For uploading multiple files use <input>
with
multiple
attribute and the second upload
argument must be array then. Also
it's possible to initialize click or change event with using third argument.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('upload file', () => {
const file = new File(['hello'], 'hello.png', {type: 'image/png'})
render(
<div>
<label htmlFor="file-uploader">Upload file:</label>
<input id="file-uploader" type="file" />
</div>,
)
userEvent.upload(screen.getByLabelText(/upload file/i), file)
expect(input.files[0]).toStrictEqual(file)
expect(input.files.item(0)).toStrictEqual(file)
expect(input.files).toHaveLength(1)
})
test('upload multiple files', () => {
const files = [
new File(['hello'], 'hello.png', {type: 'image/png'}),
new File(['there'], 'there.png', {type: 'image/png'}),
]
render(
<div>
<label htmlFor="file-uploader">Upload file:</label>
<input id="file-uploader" type="file" multiple />
</div>,
)
userEvent.upload(screen.getByLabelText(/upload file/i), files)
expect(input.files).toHaveLength(2)
expect(input.files[0]).toStrictEqual(files[0])
expect(input.files[1]).toStrictEqual(files[1])
})
Selects the text inside an <input>
or <textarea>
and deletes it.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('clear', () => {
render(<textarea value="Hello, World!" />)
userEvent.clear(screen.getByRole('textbox', 'email'))
expect(screen.getByRole('textbox', 'email')).toHaveAttribute('value', '')
})
Selects the specified option(s) of a <select>
or a <select multiple>
element.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('selectOptions', () => {
render(
<select multiple data-testid="select-multiple">
<option data-testid="val1" value="1">
A
</option>
<option data-testid="val2" value="2">
B
</option>
<option data-testid="val3" value="3">
C
</option>
</select>,
)
userEvent.selectOptions(screen.getByTestId('select-multiple'), ['1', '3'])
expect(screen.getByTestId('val1').selected).toBe(true)
expect(screen.getByTestId('val2').selected).toBe(false)
expect(screen.getByTestId('val3').selected).toBe(true)
})
The values
parameter can be either an array of values or a singular scalar
value.
It also accepts option nodes:
userEvent.selectOptions(screen.getByTestId('select-multiple'), [
screen.getByText('A'),
screen.getByText('B'),
])
Toggle the specified option(s) of a <select multiple>
element.
import * as React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('toggleSelectOptions', () => {
render(
<select multiple>
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>,
)
userEvent.toggleSelectOptions(screen.getByRole('listbox'), ['1', '3'])
expect(screen.getByText('A').selected).toBe(true)
expect(screen.getByText('C').selected).toBe(true)
userEvent.toggleSelectOptions(screen.getByRole('listbox'), ['1'])
expect(screen.getByText('A').selected).toBe(false)
})
The values
parameter can be either an array of values or a singular scalar
value.
Fires a tab event changing the document.activeElement in the same way the browser does.
Options:
shift
(defaultfalse
) can be true or false to invert tab direction.focusTrap
(defaultdocument
) 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, thefocusTrap
option is available to let you ensure your user is restricted within a focus-trap.
import React from 'react'
import {render, screen} 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', () => {
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] = screen.getAllByTestId('element')
expect(document.body).toHaveFocus()
userEvent.tab()
expect(checkbox).toHaveFocus()
userEvent.tab()
expect(radio).toHaveFocus()
userEvent.tab()
expect(number).toHaveFocus()
userEvent.tab()
// cycle goes back to first element
expect(checkbox).toHaveFocus()
})
Looking to contribute? Look for the Good First Issue label.
Please file an issue for bugs, missing documentation, or unexpected behavior.
Please file an issue to suggest new features. Vote on feature requests by adding a π. This helps maintainers prioritize what to work on.
Thanks goes to these people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
MIT