Ir para o conteúdo principal

user-event v13

[Tradução Beta Não Oficial]

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.

[Fim do ciclo de vida]

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 install --save-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 stringKeyModifierNotes
{enter}EnterN/AWill insert a newline character (<textarea /> only).
{space}' 'N/A
{esc}EscapeN/A
{backspace}BackspaceN/AWill delete the previous character (or the characters within the selectedRange, see example below).
{del}DeleteN/AWill delete the next character (or the characters within the selectedRange, see example below)
{selectall}N/AN/ASelects 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}ArrowLeftN/A
{arrowright}ArrowRightN/A
{arrowup}ArrowUpN/A
{arrowdown}ArrowDownN/A
{home}HomeN/A
{end}EndN/A
{shift}ShiftshiftKeyDoes not capitalize following characters.
{ctrl}ControlctrlKey
{alt}AltaltKey
{meta}OSmetaKey
{capslock}CapsLockmodifierCapsLockFires 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 (defina skipAutoClose como true para 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.keyboard para simular pressionamento de teclas.
Use userEvent.type para inserir texto convencionalmente em campos.

Teclas podem ser descritas como:

  • Por caractere imprimível

    userEvent.keyboard('foo') // translates to: f, o, o

    Os 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, o

    Não mantém teclas pressionadas: Shift é liberada antes de f.

  • Por KeyboardEvent.code:

    userEvent.keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // translates to: Shift, f, o, o
  • Modificadores/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 e A for referenciada. Se você não deseja esse comportamento, pode passar autoModify: false ao usar userEvent.keyboard em seu código.

upload(element, file, [{ clickInit, changeInit }], [options])

Faz upload de arquivo em <input>. Para múltiplos arquivos:

  • Use <input> com o atributo multiple
  • 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ão false) pode ser true ou false para inverter a direção da tabulação.

  • focusTrap (padrão document) 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 com userEvent.tab() ou jsdom. Por isso, a opção focusTrap está 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.

KeyCharacter
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')
})