Skip to main content

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.

Fim da Vida

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 install --save-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 textoTeclaModificadorNotas
{enter}EnterN/AInserirá um carácter de nova linha (apenas <textarea />).
{space}' 'N/A
{esc}EscapeN/A
{backspace}BackspaceN/AEliminará o carácter anterior (ou os caracteres dentro do selectedRange, consulte o exemplo abaixo).
{del}DeleteN/AEliminará o próximo carácter (ou os caracteres dentro do selectedRange, consulte o exemplo abaixo).
{selectall}N/AN/ASeleciona 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}ArrowLeftN/A
{arrowright}ArrowRightN/A
{arrowup}ArrowUpN/A
{arrowdown}ArrowDownN/A
{home}HomeN/A
{end}EndN/A
{shift}ShiftshiftKeyNão escreve com maiúsculas os caracteres seguintes.
{ctrl}ControlctrlKey
{alt}AltaltKey
{meta}OSmetaKey
{capslock}CapsLockmodifierCapsLockDispara 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 skipAutoCloseparatrue`).

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 usar userEvent.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 de key):

    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 pressionar f.

  • Por KeyboardEvent.code

    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 e A é referenciado. Se não desejas este comportamento, podes passar autoModify: false quando estiveres a usar userEvent.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 como false) pode ser true ou false para inverter a direção da tabulação.
  • focusTrap (predefinido como document) 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 na jsdom são significa que os componentes como focus-trap-react não funcionarão com userEvent.tab() ou jsdom. Para esta razão, a opção focusTrap está disponível para permitir-te assegurar que o teu utilizador está restrito dentro de uma focus-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:

TeclaCará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')
})