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ção
skipAutoClosepara
true`).
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.keyboard
se quiseres apenas simular o pressionar de botões no teclado. Tu deves usaruserEvent.type
se 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, o
Os 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, o
Isto não mantém qualquer tecla pressionada. Então
Shift
será levantada antes de pressionarf
.userEvent.keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // traduz para: Shift, f, o, o
Por carácter especial ou modificador de
userEvent.type
legado. 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: false
quando estiveres a usaruserEvent.keyboard
no 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 sertrue
oufalse
para 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:
jsdom
nã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 najsdom
são significa que os componentes comofocus-trap-react
não funcionarão comuserEvent.tab()
oujsdom
. Para esta razão, a opçãofocusTrap
está 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')
})