user-event v13
Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →
user-event é uma biblioteca complementar para Testing Library que fornece simulações mais avançadas de interações de navegador do que o método interno fireEvent.
Esta página documenta user-event@13.5.0.
Esta versão não é mais mantida. Por favor, utilize user-event@14 em seu lugar, pois ela inclui correções críticas de bugs e novos recursos.
Instalação
- npm
- Yarn
npm install --save-dev @testing-library/user-event @testing-library/dom
yarn add --dev @testing-library/user-event @testing-library/dom
Agora basta importá-la em seus testes:
import userEvent from '@testing-library/user-event'
// or
const {default: userEvent} = require('@testing-library/user-event')
API
Nota: Todos os métodos do userEvent são síncronos, com uma exceção: quando a opção delay é usada com userEvent.type, conforme descrito abaixo. Também desencorajamos o uso do userEvent dentro de blocos before/after pelos importantes motivos explicados em "Evite Aninhamento Durante Testes".
click(element, eventInit, options)
Clica no element. Dependendo de qual element é clicado, chamar click() pode ter diferentes efeitos colaterais.
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')).toBeChecked()
})
Você também pode realizar ctrlClick / shiftClick etc com:
userEvent.click(elem, {ctrlKey: true, shiftKey: true})
Consulte a documentação do construtor MouseEvent para mais opções.
Note que click acionará eventos de hover antes do clique. Para desativar isso, defina a opção skipHover como true.
Opções de eventos de ponteiro
Tentar clicar em um elemento com pointer-events definido como "none" (ou seja, não clicável) lançará um erro. Para desativar este comportamento, defina skipPointerEventsCheck como true:
userEvent.click(elem, undefined, {skipPointerEventsCheck: true})
A opção skipPointerEventsCheck pode ser passada para qualquer API relacionada a ponteiro, incluindo:
dblClick(element, eventInit, options)
Clica no element duas vezes. Dependendo do que o element é, isso pode ter diferentes efeitos colaterais.
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" onChange={onChange} />)
const checkbox = screen.getByRole('checkbox')
userEvent.dblClick(checkbox)
expect(onChange).toHaveBeenCalledTimes(2)
expect(checkbox).not.toBeChecked()
})
Nota: options inclui opções de eventos de ponteiro
type(element, text, [options])
Digita text dentro de um <input> ou <textarea>.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('type', () => {
render(<textarea />)
userEvent.type(screen.getByRole('textbox'), 'Hello,{enter}World!')
expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!')
})
options.delay é o número de milissegundos entre a digitação de dois caracteres. Por padrão é 0. Use esta opção se seu componente tem comportamentos diferentes para usuários rápidos ou lentos. Se fizer isso, certifique-se de usar await!
type clicará no elemento antes de digitar. Para desativar isso, defina a opção skipClick como true.
Caracteres especiais
As seguintes sequências de caracteres especiais são suportadas:
| Text string | Key | Modifier | Notes |
|---|---|---|---|
{enter} | Enter | N/A | Will insert a newline character (<textarea /> only). |
{space} | ' ' | N/A | |
{esc} | Escape | N/A | |
{backspace} | Backspace | N/A | Will delete the previous character (or the characters within the selectedRange, see example below). |
{del} | Delete | N/A | Will delete the next character (or the characters within the selectedRange, see example below) |
{selectall} | N/A | N/A | Selects all the text of the element. Note that this will only work for elements that support selection ranges (so, not email, password, number, among others) |
{arrowleft} | ArrowLeft | N/A | |
{arrowright} | ArrowRight | N/A | |
{arrowup} | ArrowUp | N/A | |
{arrowdown} | ArrowDown | N/A | |
{home} | Home | N/A | |
{end} | End | N/A | |
{shift} | Shift | shiftKey | Does not capitalize following characters. |
{ctrl} | Control | ctrlKey | |
{alt} | Alt | altKey | |
{meta} | OS | metaKey | |
{capslock} | CapsLock | modifierCapsLock | Fires both keydown and keyup when used (simulates a user clicking their "Caps Lock" button to enable caps lock). |
Nota sobre modificadores: Teclas modificadoras (
{shift},{ctrl},{alt},{meta}) ativarão seus modificadores de evento correspondentes durante o comando type ou até serem fechadas (via{/shift},{/ctrl}, etc.). Se não fechadas explicitamente, eventos serão disparados para fechá-las automaticamente (definaskipAutoClosecomotruepara desativar).
Adotamos a mesma posição do Cypress de não simular o comportamento de combinações de teclas modificadoras, pois diferentes sistemas operacionais funcionam de modo distinto nesse aspecto.
Exemplo de uso com um intervalo de seleção:
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('delete characters within the selectedRange', () => {
render(
<div>
<label htmlFor="my-input">Example:</label>
<input id="my-input" type="text" value="This is a bad example" />
</div>,
)
const input = screen.getByLabelText(/example/i)
input.setSelectionRange(10, 13)
userEvent.type(input, '{backspace}good')
expect(input).toHaveValue('This is a good example')
Por padrão, type anexa ao texto existente. Para adicionar texto no início, redefina o intervalo de seleção do elemento e forneça as opções initialSelectionStart e initialSelectionEnd:
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('prepend text', () => {
render(<input defaultValue="World!" />)
const element = screen.getByRole('textbox')
// Prepend text
element.setSelectionRange(0, 0)
userEvent.type(element, 'Hello, ', {
initialSelectionStart: 0,
initialSelectionEnd: 0,
})
expect(element).toHaveValue('Hello, World!')
})
Suporte a <input type="time" />
A seguir, um exemplo de uso desta biblioteca com <input type="time" />:
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('types into the input', () => {
render(
<>
<label for="time">Enter a time</label>
<input type="time" id="time" />
</>,
)
const input = screen.getByLabelText(/enter a time/i)
userEvent.type(input, '13:58')
expect(input.value).toBe('13:58')
})
keyboard(text, options)
Simula os eventos de teclado descritos por text. Similar ao userEvent.type() mas sem cliques ou alteração do intervalo de seleção.
Use
userEvent.keyboardpara simular pressionamento de teclas.
UseuserEvent.typepara inserir texto convencionalmente em campos.
Teclas podem ser descritas como:
Por caractere imprimível
userEvent.keyboard('foo') // translates to: f, o, oOs colchetes
{e[são caracteres especiais - use duplicados para referenciá-los:userEvent.keyboard('{{a[[') // translates to: {, a, [Por KeyboardEvent.key (suporta apenas valores alfanuméricos de
key)userEvent.keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, oNão mantém teclas pressionadas:
Shifté liberada antes def.Por KeyboardEvent.code:
userEvent.keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // translates to: Shift, f, o, oModificadores/símbolos legados do
userEvent.type:
Modificadores como{shift}(minúsculo) mantêm-se pressionados. Cancele com/no final:userEvent.keyboard('{shift}{ctrl/}a{/shift}') // translates to: Shift(down), Control(down+up), a, Shift(up)
Mantenha teclas pressionadas com > no final do descritor - libere com / no início:
userEvent.keyboard('{Shift>}A{/Shift}') // translates to: Shift(down), A, Shift(up)
userEvent.keyboard retorna um estado do teclado para continuar operações.
const keyboardState = userEvent.keyboard('[ControlLeft>]') // keydown [ControlLeft]
// ... inspect some changes ...
userEvent.keyboard('a', {keyboardState}) // press [KeyA] with active ctrlKey modifier
O mapeamento de key para code usa um mapa padrão de teclado US. Forneça seu próprio mapeamento via opções.
userEvent.keyboard('?', {keyboardMap: myOwnLocaleKeyboardMap})
Versões futuras poderão tentar interpolar os modificadores necessários para alcançar uma tecla imprimível no teclado. Por exemplo, pressionar automaticamente
{Shift}quando o CapsLock não estiver ativo eAfor referenciada. Se você não deseja esse comportamento, pode passarautoModify: falseao usaruserEvent.keyboardem seu código.
upload(element, file, [{ clickInit, changeInit }], [options])
Faz upload de arquivo em <input>. Para múltiplos arquivos:
- Use
<input>com o atributomultiple - Passe array no segundo argumento de
upload
O terceiro argumento inicializa eventos click/change.
Se options.applyAccept for definido como true e o elemento tiver atributo accept, arquivos incompatíveis serão descartados.
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>,
)
const input = screen.getByLabelText(/upload file/i)
userEvent.upload(input, 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>,
)
const input = screen.getByLabelText(/upload file/i)
userEvent.upload(input, files)
expect(input.files).toHaveLength(2)
expect(input.files[0]).toStrictEqual(files[0])
expect(input.files[1]).toStrictEqual(files[1])
})
clear(element)
Seleciona e apaga texto dentro de <input> ou <textarea>.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('clear', () => {
render(<textarea defaultValue="Hello, World!" />)
userEvent.clear(screen.getByRole('textbox'))
expect(screen.getByRole('textbox')).toHaveValue('')
})
selectOptions(element, values, options)
Seleciona a(s) opção(ões) especificada(s) de um elemento <select> ou <select multiple>.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('selectOptions', () => {
render(
<select multiple>
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>,
)
userEvent.selectOptions(screen.getByRole('listbox'), ['1', '3'])
expect(screen.getByRole('option', {name: 'A'}).selected).toBe(true)
expect(screen.getByRole('option', {name: 'B'}).selected).toBe(false)
expect(screen.getByRole('option', {name: 'C'}).selected).toBe(true)
})
O parâmetro values pode ser um array de valores ou um único valor escalar.
Também aceita nós de opção:
userEvent.selectOptions(screen.getByTestId('select-multiple'), [
screen.getByText('A'),
screen.getByText('B'),
])
Nota: options inclui opções de eventos de ponteiro
deselectOptions(element, values, options)
Remove a seleção da(s) opção(ões) especificada(s) de um elemento <select multiple>.
import * as React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
test('deselectOptions', () => {
render(
<select multiple>
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>,
)
userEvent.selectOptions(screen.getByRole('listbox'), '2')
expect(screen.getByText('B').selected).toBe(true)
userEvent.deselectOptions(screen.getByRole('listbox'), '2')
expect(screen.getByText('B').selected).toBe(false)
// can do multiple at once as well:
// userEvent.deselectOptions(screen.getByRole('listbox'), ['1', '2'])
})
O parâmetro values pode ser um array de valores ou um único valor escalar.
Nota: options inclui opções de eventos de ponteiro
tab({shift, focusTrap})
Dispara um evento de tabulação alterando o document.activeElement da mesma forma que o navegador faz.
Opções:
shift(padrãofalse) pode ser true ou false para inverter a direção da tabulação.focusTrap(padrãodocument) um elemento contêiner para restringir a tabulação dentro dele.
Nota sobre tabulação:
O jsdom não suporta tabulação, então este recurso permite testar a tabulação da perspectiva do usuário final. Porém, essa limitação significa que componentes como focus-trap-react não funcionarão comuserEvent.tab()ou jsdom. Por isso, a opçãofocusTrapestá disponível para garantir que seu usuário fique restrito a uma armadilha de foco.
import React from 'react'
import {render, screen} from '@testing-library/react'
import '@testing-library/jest-dom'
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 the body element
expect(document.body).toHaveFocus()
userEvent.tab()
expect(checkbox).toHaveFocus()
})
hover(element, options)
Passa o mouse sobre o element.
import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Tooltip from '../tooltip'
test('hover', () => {
const messageText = 'Hello'
render(
<Tooltip messageText={messageText}>
<TrashIcon aria-label="Delete" />
</Tooltip>,
)
userEvent.hover(screen.getByLabelText(/delete/i))
expect(screen.getByText(messageText)).toBeInTheDocument()
userEvent.unhover(screen.getByLabelText(/delete/i))
expect(screen.queryByText(messageText)).not.toBeInTheDocument()
})
Nota: options inclui opções de eventos de ponteiro
unhover(element, options)
Remove o mouse de cima do element.
Veja acima para um exemplo
Nota: options inclui opções de eventos de ponteiro
paste(element, text, eventInit, options)
Permite simular o usuário colando texto em um campo de entrada.
test('should paste text in input', () => {
render(<MyInput />)
const text = 'Hello, world!'
const element = getByRole('textbox', {name: /paste your greeting/i})
userEvent.paste(element, text)
expect(element).toHaveValue(text)
})
Você pode usar o eventInit se o conteúdo colado precisar ter clipboardData
(como files).
specialChars
Conjunto de caracteres especiais usados no método type.
| Key | Character |
|---|---|
| arrowLeft | {arrowleft} |
| arrowRight | {arrowright} |
| arrowDown | {arrowdown} |
| arrowUp | {arrowup} |
| home | {home} |
| end | {end} |
| enter | {enter} |
| escape | {esc} |
| delete | {del} |
| backspace | {backspace} |
| selectAll | {selectall} |
| space | {space} |
| whitespace | ' ' |
Exemplo de uso:
import React, {useState} from 'react'
import {render, screen} from '@testing-library/react'
import userEvent, {specialChars} from '@testing-library/user-event'
const InputElement = () => {
const [currentValue, setCurrentValue] = useState('This is a bad example')
return (
<div>
<label htmlFor="my-input">Example:</label>
<input
id="my-input"
type="text"
value={currentValue}
onChange={e => setCurrentValue(e.target.value)}
/>
</div>
)
}
test('delete characters within the selectedRange', () => {
render(<InputElement />)
const input = screen.getByLabelText(/example/i)
input.setSelectionRange(10, 13)
userEvent.type(input, `${specialChars.backspace}good`)
expect(input).toHaveValue('This is a good example')
})