API de Interatividade
O Vitest implementa um subconjunto das APIs do @testing-library/user-event
utilizando o Chrome DevTools Protocol ou webdriver em vez de simular eventos. Isso resulta em um comportamento de navegador mais confiável e consistente com a forma como os usuários interagem com uma página.
import { userEvent } from '@vitest/browser/context';
await userEvent.click(document.querySelector('.button'));
A maioria dos métodos userEvent
herda as opções do provedor configurado. Para visualizar todas as opções disponíveis no seu IDE, adicione os tipos webdriver
ou playwright
(dependendo do provedor que você está utilizando) ao seu arquivo tsconfig.json
:
{
"compilerOptions": {
"types": ["@vitest/browser/providers/playwright"]
}
}
{
"compilerOptions": {
"types": ["@vitest/browser/providers/webdriverio"]
}
}
userEvent.setup
function setup(): UserEvent;
Cria uma nova instância de user event
. Isso é útil se você precisar manter o estado do teclado para pressionar e soltar as teclas corretamente.
WARNING
Ao contrário do @testing-library/user-event
, a instância padrão userEvent
do @vitest/browser/context
é criada uma única vez, e não a cada chamada de seus métodos! Você pode observar a diferença de comportamento neste exemplo:
import { userEvent as vitestUserEvent } from '@vitest/browser/context';
import { userEvent as originalUserEvent } from '@testing-library/user-event';
await vitestUserEvent.keyboard('{Shift}'); // mantém Shift pressionado
await vitestUserEvent.keyboard('{/Shift}'); // solta Shift
await originalUserEvent.keyboard('{Shift}'); // mantém Shift pressionado
await originalUserEvent.keyboard('{/Shift}'); // NÃO solta Shift porque a instância é diferente
Este comportamento é mais vantajoso porque não estamos apenas emulando o teclado; estamos realmente pressionando a tecla Shift. Manter o comportamento original poderia causar problemas inesperados ao digitar em um campo.
userEvent.click
function click(
element: Element | Locator,
options?: UserEventClickOptions
): Promise<void>;
Simula um clique em um elemento. Herda as opções do provedor. Consulte a documentação do provedor para uma explicação detalhada sobre como este método funciona.
import { page, userEvent } from '@vitest/browser/context';
test('clicks on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.click(logo);
// ou você pode acessá-lo diretamente pelo locator
await logo.click();
});
Referências:
userEvent.dblClick
function dblClick(
element: Element | Locator,
options?: UserEventDoubleClickOptions
): Promise<void>;
Aciona um evento de clique duplo em um elemento.
Consulte a documentação do provedor para uma explicação detalhada sobre como este método funciona.
import { page, userEvent } from '@vitest/browser/context';
test('triggers a double click on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.dblClick(logo);
// ou você pode acessá-lo diretamente pelo locator
await logo.dblClick();
});
Referências:
- API
locator.dblclick
do Playwright - API
element.doubleClick
do WebdriverIO - API
dblClick
do testing-library
userEvent.tripleClick
function tripleClick(
element: Element | Locator,
options?: UserEventTripleClickOptions
): Promise<void>;
Aciona uma sequência de três eventos de clique em um elemento. Como não existe um evento nativo tripleclick
na API do navegador, este método dispara três eventos de clique em sequência. Portanto, você deve verificar o detalhe do evento click para filtrar o evento desejado: evt.detail === 3
.
Consulte a documentação do provedor para uma explicação detalhada sobre como este método funciona.
import { page, userEvent } from '@vitest/browser/context';
test('triggers a triple click on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
let tripleClickFired = false;
logo.addEventListener('click', evt => {
if (evt.detail === 3) {
tripleClickFired = true;
}
});
await userEvent.tripleClick(logo);
// ou você pode acessá-lo diretamente pelo locator
await logo.tripleClick();
expect(tripleClickFired).toBe(true);
});
Referências:
- API
locator.click
do Playwright: implementado viaclick
comclickCount: 3
. - API
browser.action
do WebdriverIO: implementado via API de ações commove
mais três eventosdown + up + pause
em sequência. - API
tripleClick
do testing-library
userEvent.fill
function fill(element: Element | Locator, text: string): Promise<void>;
Define um valor para um campo input
, textarea
ou elemento com contenteditable
. Este método remove qualquer texto existente na entrada antes de definir o novo valor.
import { page, userEvent } from '@vitest/browser/context';
test('update input', async () => {
const input = page.getByRole('input');
await userEvent.fill(input, 'foo'); // input.value == foo
await userEvent.fill(input, '{{a[['); // input.value == {{a[[
await userEvent.fill(input, '{Shift}'); // input.value == {Shift}
// ou você pode acessá-lo diretamente pelo locator
await input.fill('foo'); // input.value == foo
});
Este método foca no elemento, preenche-o e dispara um evento input
após o preenchimento. Você pode usar uma string vazia para limpar o campo.
TIP
Esta API é mais rápida do que usar userEvent.type
ou userEvent.keyboard
, mas não suporta a sintaxe keyboard
do user-event (por exemplo, {Shift}{selectall}
).
Recomendamos usar esta API em vez de userEvent.type
em situações onde você não precisa inserir caracteres especiais ou ter controle granular sobre eventos de pressionamento de tecla.
Referências:
userEvent.keyboard
function keyboard(text: string): Promise<void>;
O userEvent.keyboard
permite simular pressionamentos de teclas. Se algum campo de entrada estiver focado, ele digitará os caracteres nesse campo. Caso contrário, ele disparará eventos de teclado no elemento atualmente focado (document.body
se nenhum elemento estiver focado).
Esta API suporta a sintaxe keyboard
do user-event.
import { userEvent } from '@vitest/browser/context';
test('trigger keystrokes', async () => {
await userEvent.keyboard('foo'); // equivale a: f, o, o
await userEvent.keyboard('{{a[['); // equivale a: {, a, [
await userEvent.keyboard('{Shift}{f}{o}{o}'); // equivale a: Shift, f, o, o
await userEvent.keyboard('{a>5}'); // pressiona 'a' sem soltar e dispara 5 eventos de `keydown`
await userEvent.keyboard('{a>5/}'); // pressiona 'a' por 5 eventos de `keydown` e depois solta
});
Referências:
userEvent.tab
function tab(options?: UserEventTabOptions): Promise<void>;
Envia um evento de tecla Tab
. Este é um atalho para userEvent.keyboard('{tab}')
.
import { page, userEvent } from '@vitest/browser/context';
test('tab works', async () => {
const [input1, input2] = page.getByRole('input').elements();
expect(input1).toHaveFocus();
await userEvent.tab();
expect(input2).toHaveFocus();
await userEvent.tab({ shift: true });
expect(input1).toHaveFocus();
});
Referências:
userEvent.type
function type(
element: Element | Locator,
text: string,
options?: UserEventTypeOptions
): Promise<void>;
WARNING
Se você não precisa utilizar caracteres especiais (por exemplo, {shift}
ou {selectall}
), é recomendado usar userEvent.fill
em vez deste método para obter melhor desempenho.
O método type
implementa a utilidade type
do @testing-library/user-event
, construída sobre a API keyboard
.
Esta função permite inserir caracteres em um elemento input/textarea/contenteditable. Ela suporta a sintaxe keyboard
do user-event.
Se você apenas precisa simular pressionamentos de teclas sem um campo de entrada específico, utilize a API userEvent.keyboard
.
import { page, userEvent } from '@vitest/browser/context';
test('update input', async () => {
const input = page.getByRole('input');
await userEvent.type(input, 'foo'); // input.value == foo
await userEvent.type(input, '{{a[['); // input.value == foo{a[
await userEvent.type(input, '{Shift}'); // input.value == foo{a[
});
INFO
O Vitest não expõe o método .type
diretamente no locator (como input.type
) porque ele existe principalmente para compatibilidade com a biblioteca userEvent
. Considere usar .fill
em vez disso, pois é mais rápido.
Referências:
userEvent.clear
function clear(element: Element | Locator): Promise<void>;
Este método limpa o conteúdo de um elemento de entrada.
import { page, userEvent } from '@vitest/browser/context';
test('clears input', async () => {
const input = page.getByRole('input');
await userEvent.fill(input, 'foo');
expect(input).toHaveValue('foo');
await userEvent.clear(input);
// ou você pode acessá-lo diretamente pelo locator
await input.clear();
expect(input).toHaveValue('');
});
Referências:
userEvent.selectOptions
function selectOptions(
element: Element | Locator,
values: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
options?: UserEventSelectOptions
): Promise<void>;
O userEvent.selectOptions
permite selecionar uma ou mais opções em um elemento <select>
.
WARNING
Se o elemento select não possuir o atributo multiple
, o Vitest selecionará apenas o primeiro elemento fornecido no array de valores.
Atualmente, ao contrário do @testing-library
, o Vitest não suporta a interação com elementos com o role listbox
, mas planejamos adicionar suporte a ele no futuro.
import { page, userEvent } from '@vitest/browser/context';
test('clears input', async () => {
const select = page.getByRole('select');
await userEvent.selectOptions(select, 'Option 1');
// ou você pode acessá-lo diretamente pelo locator
await select.selectOptions('Option 1');
expect(select).toHaveValue('option-1');
await userEvent.selectOptions(select, 'option-1');
expect(select).toHaveValue('option-1');
await userEvent.selectOptions(select, [
page.getByRole('option', { name: 'Option 1' }),
page.getByRole('option', { name: 'Option 2' }),
]);
expect(select).toHaveValue(['option-1', 'option-2']);
});
WARNING
O provedor webdriverio
não suporta a seleção de múltiplos elementos, pois não oferece uma API específica para isso.
Referências:
- API
locator.selectOption
do Playwright - API
element.selectByIndex
do WebdriverIO - API
selectOptions
do testing-library
userEvent.hover
function hover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Este método move a posição do cursor para o elemento selecionado, simulando o evento de hover. Consulte a documentação do provedor para uma explicação detalhada sobre como este método funciona.
WARNING
Se você estiver utilizando o provedor webdriverio
, o cursor será movido para o centro do elemento por padrão.
Se você estiver utilizando o provedor playwright
, o cursor se move para "algum" ponto visível do elemento.
import { page, userEvent } from '@vitest/browser/context';
test('hovers logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.hover(logo);
// ou você pode acessá-lo diretamente pelo locator
await logo.hover();
});
Referências:
userEvent.unhover
function unhover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Este método funciona de forma semelhante a userEvent.hover
, mas move o cursor para o elemento document.body
em vez de um elemento específico.
WARNING
Por padrão, a posição do cursor está em "algum" lugar visível (no provedor playwright
) ou no centro (no provedor webdriverio
) do elemento body. Portanto, se o elemento atualmente sob o cursor já estiver na mesma posição, este método não terá efeito visível.
import { page, userEvent } from '@vitest/browser/context';
test('unhover logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.unhover(logo);
// ou você pode acessá-lo diretamente pelo locator
await logo.unhover();
});
Referências:
userEvent.upload
function upload(
element: Element | Locator,
files: string[] | string | File[] | File
): Promise<void>;
Define os arquivos especificados para um elemento de entrada de arquivo (<input type="file">
).
import { page, userEvent } from '@vitest/browser/context';
test('can upload a file', async () => {
const input = page.getByRole('button', { name: /Upload files/ });
const file = new File(['file'], 'file.png', { type: 'image/png' });
await userEvent.upload(input, file);
// ou você pode acessá-lo diretamente pelo locator
await input.upload(file);
// você também pode usar caminhos de arquivo relativos ao arquivo de teste
await userEvent.upload(input, '../fixtures/file.png');
});
WARNING
O provedor webdriverio
suporta este comando apenas nos navegadores chrome
e edge
. Atualmente, ele também suporta apenas caminhos de arquivo (tipo string).
Referências:
- API
locator.setInputFiles
do Playwright - API
browser.uploadFile
do WebdriverIO - API
upload
do testing-library
userEvent.dragAndDrop
function dragAndDrop(
source: Element | Locator,
target: Element | Locator,
options?: UserEventDragAndDropOptions
): Promise<void>;
Simula a ação de arrastar o elemento de origem (source
) e soltá-lo sobre o elemento de destino (target
). Lembre-se que o elemento source
deve ter o atributo draggable
definido como true
.
import { page, userEvent } from '@vitest/browser/context';
test('drag and drop works', async () => {
const source = page.getByRole('img', { name: /logo/ });
const target = page.getByTestId('logo-target');
await userEvent.dragAndDrop(source, target);
// ou você pode acessá-lo diretamente pelo locator
await source.dropTo(target);
await expect.element(target).toHaveTextContent('Logo is processed');
});
WARNING
Esta API não é suportada pelo provedor preview
padrão.
Referências: