user-event v13
A user-event é uma biblioteca acompanhante para Testing Library que
fornece simulação mais avançada de interações do navegador do que o método
fireEvent embutido.
Esta página descreve a user-event@13.5.0. Esta versão já não é mantida. Use a
versão mais recente user-event@14 no lugar daquela, já
que inclui correções de erro importante e novas funcionalidades.
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 simplesmente importe-a nos teus testes:
import userEvent from '@testing-library/user-event'
// ou
const {default: userEvent} = require('@testing-library/user-event')
API
Nota: Todos os métodos de userEvent são síncronos com um exceção: quando a
opção delay é usada com a userEvent.type conforme descrita abaixo. Nós
também desencorajamos de todo usar userEvent dentro dos blocos before/after,
por motivos importantes descritos na
"Evite Encaixamento Quando Estiveres a Testar".
click(element, eventInit, options)
Clica sobre o element, dependendo de qual element for 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()
})
Tu podes também manipular ctrlClick ou shiftClick etc. Com:
userEvent.click(elem, {ctrlKey: true, shiftKey: true})
Consulte a documentação do construtor
MouseEvent
para mais opções.
Nota que click acionará eventos de "pairar sobre" antes de clicar. Para
desativar isto, defina a opção skipHover para true.
Opções dos eventos de Ponteiro
Tentar clicar sobre um elemento com pointer-events sendo definido para
"none" (por exemplo, não clicável) lançará um erro. Se quiseres desativar este
comportamento podes definir skipPointerEventsCheck para true:
userEvent.click(elem, undefined, {skipPointerEventsCheck: true})
A opção skipPointerEventsCheck pode ser passada para qualquer API relacionada
ao ponteiro incluindo:
dblClick(element, eventInit, options)
Clica sobre o elemento duas vezes, dependendo de qual element é clicado 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 incluem
opções dos eventos de Ponteiro
type(element, text, [options])
Escreve o text dentro de um <input>ou um <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!')
})
A options.delay é o número de milissegundos que passam entre dois caracteres
que são digitados. Por padrão é 0. Tu podes usar esta opção se o teu componente
tiver um comportamento diferente para utilizadores rápidos ou lentos. Se fizeres
isto, precisaras de certificar-te de await (esperar)!
A type clicará sobre o elemento antes de digitar. Para desativar isto, defina
a opção skipClick para true.
Os caracteres especiais
As seguintes sequências de caracteres de carácter especial são suportas:
| Sequência de caracteres de texto | Tecla | Modificador | Notas |
|---|---|---|---|
{enter} | Enter | N/A | Inserirá um carácter de nova linha (apenas <textarea />). |
{space} | ' ' | N/A | |
{esc} | Escape | N/A | |
{backspace} | Backspace | N/A | Eliminará o carácter anterior (ou os caracteres dentro do selectedRange, consulte o exemplo abaixo). |
{del} | Delete | N/A | Eliminará o próximo carácter (ou os caracteres dentro do selectedRange, consulte o exemplo abaixo). |
{selectall} | N/A | N/A | Seleciona todo o texto do elemento. Nota que isto apenas funcionará para elementos que suportam os limites de seleção (então, nem email, password, number, entre outros). |
{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 | Não escreve com maiúsculas os caracteres seguintes. |
{ctrl} | Control | ctrlKey | |
{alt} | Alt | altKey | |
{meta} | OS | metaKey | |
{capslock} | CapsLock | modifierCapsLock | Dispara ambos keydown (pressionar) e keyup (soltar) quando usada (simula um utilizadores a clicarem sobre os seus botões "Caps Lock" para ativar o "Caps Lock"). |
Um nota a respeito dos modificadores: As teclas modificadora ({shift}
,{ctrl},{alt},{meta}) ativarão os seus modificadores de evento correspondente para a duração do comando de digitar ou até serem fechados (através de{/shift},{/ctrl}, etc.). Se não forem fechadas explicitamente, então os eventos serão disparados para fechá-las automaticamente (para desativar isto, defina a opçãoskipAutoCloseparatrue`).
Nós partilhamos da mesma postura que a Cypress em que não simulamos o comportamento que acontece com as combinações de tecla modificadora já que diferentes sistemas operacionais funcionam diferentemente neste aspeto.
Um exemplo de um uso com um limite 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 no final ao texto existente. Para anexar no início do
texto, reinicie o limite 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')
// Anexar ao início do texto
element.setSelectionRange(0, 0)
userEvent.type(element, 'Hello, ', {
initialSelectionStart: 0,
initialSelectionEnd: 0,
})
expect(element).toHaveValue('Hello, World!')
})
Suporte de <input type="time" />
O bloco seguinte é um exemplo de uso desta biblioteca com o
<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 pelo text. Isto é parecido com
userEvent.type() mas sem qualquer clique ou mudança do limite de seleção.
Tu deves usar
userEvent.keyboardse quiseres apenas simular o pressionar de botões no teclado. Tu deves usaruserEvent.typese apenas quiseres inserir convenientemente algum texto dentro de um campo de entrada ou área de texto.
Os toques nas teclas podem ser descritos:
Por carácter passível de impressão:
userEvent.keyboard('foo') // traduz para: f, o, oOs parêntesis
{e[são usados como carácter especial e podem ser referenciados duplicando-os:userEvent.keyboard('{{a[[') // translates to: {, a, [Por
KeyboardEvent.key(apenas suporta os valores alfanumérico dekey):userEvent.keyboard('{Shift}{f}{o}{o}') // traduz para: Shift, f, o, oIsto não mantém qualquer tecla pressionada. Então
Shiftserá levantada antes de pressionarf.userEvent.keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // traduz para: Shift, f, o, oPor carácter especial ou modificador de
userEvent.typelegado. Os modificadores como{shift}(nota as minúsculas) serão automaticamente mantidos pressionados, tal como antes. Tu podes cancelar este comportamento adicionando um/ao final do descritor.userEvent.keyboard('{shift}{ctrl/}a{/shift}') // translates to: Shift(down), Control(down+up), a, Shift(up)
As teclas podem ser mantidas pressionadas adicionando a > ao final do
descritor - e levantada adicionando um / ao início do descritor:
userEvent.keyboard('{Shift>}A{/Shift}') // traduz para: Shift(down), A, Shift(up)
A userEvent.keyboard retornam um estado do teclado que pode ser usado para
continuar as operações de teclado.
const keyboardState = userEvent.keyboard('[ControlLeft>]') // pressionar [ControlLeft]
// ... inspecionar algumas mudanças ...
userEvent.keyboard('a', {keyboardState}) // pressionar [KeyA] com o modificador "ctrlKey" ativo
O mapeamento de key para code é realizado por um
mapa de tecla padrão
descrevendo um teclado US "padrão". Tu podes fornecer o teu próprio mapeamento
de teclado local por opção:
userEvent.keyboard('?', {keyboardMap: myOwnLocaleKeyboardMap})
As versões futuras podem tentar interpolar os modificadores necessários para alcançar uma tecla passível de impressão no teclado. Por exemplo, automaticamente pressionado
{Shift}quando "CapsLock" não estiver ativo eAé referenciado. Se não desejas este comportamento, podes passarautoModify: falsequando estiveres a usaruserEvent.keyboardno teu código.
upload(element, file, [{ clickInit, changeInit }], [options])
Carrega o ficheiro para um <input>. Para carregar vários ficheiros use o
<input> com o atributo multiple e o segundo argumento upload como um
arranjo. Também é possível inicializar um evento de clique ou mudança usando um
terceiro argumento.
Se options.applyAccept for definido para true e houver um atributo accept
no elemento, os ficheiros que não correspondem 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 o texto dentro de um <input> ou <textarea> e eliminá-o.
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 opção ou opções especificada 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 tanto um arranjo de valores ou um valor escalar
singular.
Ele também aceita nós de opção:
userEvent.selectOptions(screen.getByTestId('select-multiple'), [
screen.getByText('A'),
screen.getByText('B'),
])
Nota: options inclui as
opções dos eventos de ponteiro
deselectOptions(element, values, options)
Remove a seleção para a opção ou opções especificadas 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)
// também podes fazer vários de uma vez:
// userEvent.deselectOptions(screen.getByRole('listbox'), ['1', '2'])
})
O parâmetro values pode ser tanto um arranjo de valores ou um valor escalar
singular.
Nota: options inclui as
opções dos eventos de ponteiro
tab({shift, focusTrap})
Dispara um evento de tabulação mudando o document.activeElement da mesma
maneira que o navegador faz.
Opções:
shift(predefinido comofalse) pode sertrueoufalsepara inverter a direção da tabulação.focusTrap(predefinido comodocument) um elemento contentor para restringir a tabulação por dentro de.
Um nota sobre a tabulação:
jsdomnão suporta a tabulação, assim esta funcionalidade é uma maneira de ativar os testes para verificar a tabulação a partir da perspetiva do utilizador final. No entanto, esta limitação najsdomsão significa que os componentes comofocus-trap-reactnão funcionarão comuserEvent.tab()oujsdom. Para esta razão, a opçãofocusTrapestá disponível para permitir-te assegurar que o teu utilizador está restrito dentro de umafocus-trap.
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()
// o ciclo volta para o elemento de corpo
expect(document.body).toHaveFocus()
userEvent.tab()
expect(checkbox).toHaveFocus()
})
hover(element, options)
Paira 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: a options inclui
opções dos eventos de ponteiro.
unhover(element, options)
Para o pairar sobre o elemento
Consulte o bloco acima por um exemplo.
Nota: a options inclui
opções dos eventos de ponteiro.
paste(element, text, eventInit, options)
Permite-te simular o utilizador colando algum texto dentro de uma 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)
})
Tu podes usar o eventInit se o que estás a colar deveria ter clipboardData
(como files).
specialChars
Um conjunto de meia dúzia de caracteres especiais usados no método type:
| Tecla | Carácter |
|---|---|
| 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')
})