API de interactividad
Vitest implementa un subconjunto de las API de @testing-library/user-event
utilizando el Protocolo de Herramientas de Desarrollo de Chrome o webdriver en lugar de simular eventos. Esto resulta en un comportamiento del navegador más confiable y consistente con la interacción real del usuario.
import { userEvent } from '@vitest/browser/context';
await userEvent.click(document.querySelector('.button'));
Casi todos los métodos de userEvent
utilizan las opciones de su proveedor. Para ver todas las opciones disponibles en tu IDE, añade los tipos de webdriver
o playwright
(según tu proveedor) a tu archivo de configuración o a un archivo de configuración (dependiendo de las inclusiones en tu tsconfig.json
):
/// <reference types="@vitest/browser/providers/playwright" />
/// <reference types="@vitest/browser/providers/webdriverio" />
userEvent.setup
function setup(): UserEvent;
Crea una nueva instancia de evento de usuario. Esto es útil si necesitas mantener el estado del teclado para una correcta pulsación y liberación de teclas.
WARNING
A diferencia de @testing-library/user-event
, la instancia predeterminada de userEvent
de @vitest/browser/context
se crea una única vez, no cada vez que se llaman sus métodos. Puedes ver la diferencia en cómo funciona en este fragmento:
import { userEvent as vitestUserEvent } from '@vitest/browser/context';
import { userEvent as originalUserEvent } from '@testing-library/user-event';
await vitestUserEvent.keyboard('{Shift}'); // presiona shift sin soltar
await vitestUserEvent.keyboard('{/Shift}'); // suelta shift
await originalUserEvent.keyboard('{Shift}'); // presiona shift sin soltar
await originalUserEvent.keyboard('{/Shift}'); // NO liberó la tecla Shift porque el estado es diferente
Este comportamiento es más útil porque no emulamos el teclado, sino que realmente presionamos la tecla Shift. Por lo tanto, mantener el comportamiento original podría causar problemas inesperados al escribir en el campo.
userEvent.click
function click(
element: Element | Locator,
options?: UserEventClickOptions
): Promise<void>;
Realiza un clic en un elemento. Hereda las opciones del proveedor. Para una explicación detallada sobre el funcionamiento de este método, consulta la documentación de tu proveedor.
import { page, userEvent } from '@vitest/browser/context';
test('hace clic en un elemento', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.click(logo);
// o puedes acceder a él directamente en el localizador
await logo.click();
});
Referencias:
userEvent.dblClick
function dblClick(
element: Element | Locator,
options?: UserEventDoubleClickOptions
): Promise<void>;
Realiza un doble clic en un elemento.
Para una explicación detallada sobre el funcionamiento de este método, consulta la documentación de tu proveedor.
import { page, userEvent } from '@vitest/browser/context';
test('dispara un doble clic en un elemento', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.dblClick(logo);
// o puedes acceder a él directamente en el localizador
await logo.dblClick();
});
Referencias:
- API
locator.dblclick
de Playwright - API
element.doubleClick
de WebdriverIO - API
dblClick
de testing-library
userEvent.tripleClick
function tripleClick(
element: Element | Locator,
options?: UserEventTripleClickOptions
): Promise<void>;
Realiza un triple clic en un elemento. Dado que no existe tripleclick
en la API del navegador, este método generará tres eventos de clic consecutivos. Por lo tanto, debes verificar el detalle del evento de clic (evt.detail === 3
) para filtrar el evento deseado.
Para una explicación detallada sobre el funcionamiento de este método, consulta la documentación de tu proveedor.
import { page, userEvent } from '@vitest/browser/context';
test('dispara un triple clic en un elemento', 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);
// o puedes acceder a él directamente en el localizador
await logo.tripleClick();
expect(tripleClickFired).toBe(true);
});
Referencias:
- API
locator.click
de Playwright: implementado medianteclick
conclickCount: 3
. - API
browser.action
de WebdriverIO: implementado mediante la API de acciones conmove
más tres eventosdown + up + pause
consecutivos. - API
tripleClick
de testing-library
userEvent.fill
function fill(element: Element | Locator, text: string): Promise<void>;
Asigna un valor al campo input
/textarea
/contenteditable
. Esto eliminará cualquier texto existente en el campo antes de establecer el nuevo valor.
import { page, userEvent } from '@vitest/browser/context';
test('actualiza la entrada', 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}
// o puedes acceder a él directamente en el localizador
await input.fill('foo'); // input.value == foo
});
Este método enfoca el elemento, lo rellena y genera un evento input
una vez completado el relleno. Se puede usar una cadena vacía para borrar el campo.
TIP
Esta API es más rápida que usar userEvent.type
o userEvent.keyboard
, pero no es compatible con la sintaxis de teclado de user-event (por ejemplo, {Shift}{selectall}
).
Recomendamos usar esta API en lugar de userEvent.type
en situaciones en las que no necesites introducir caracteres especiales o un control granular sobre los eventos de pulsación de teclas.
Referencias:
userEvent.keyboard
function keyboard(text: string): Promise<void>;
userEvent.keyboard
permite disparar pulsaciones de teclado. Si algún campo de entrada tiene el foco, escribirá caracteres en dicho campo. De lo contrario, generará eventos de teclado en el elemento actualmente enfocado (o en document.body
si no hay elementos con foco).
Esta API soporta la sintaxis de teclado de user-event.
import { userEvent } from '@vitest/browser/context';
test('dispara pulsaciones de teclas', async () => {
await userEvent.keyboard('foo'); // se traduce en: f, o, o
await userEvent.keyboard('{{a[['); // se traduce en: {, a, [
await userEvent.keyboard('{Shift}{f}{o}{o}'); // se traduce en: Shift, f, o, o
await userEvent.keyboard('{a>5}'); // presiona 'a' sin soltarla y activa 5 eventos 'keydown'
await userEvent.keyboard('{a>5/}'); // presiona 'a' durante 5 eventos 'keydown' y luego la suelta
});
Referencias:
userEvent.tab
function tab(options?: UserEventTabOptions): Promise<void>;
Envía un evento de la tecla Tab
. Esto es una abreviatura de userEvent.keyboard('{tab}')
.
import { page, userEvent } from '@vitest/browser/context';
test('tab funciona', 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();
});
Referencias:
userEvent.type
function type(
element: Element | Locator,
text: string,
options?: UserEventTypeOptions
): Promise<void>;
WARNING
Si no necesitas caracteres especiales (por ejemplo, {shift}
o {selectall}
), se recomienda usar userEvent.fill
en su lugar para un mejor rendimiento.
El método type
implementa la utilidad type
de @testing-library/user-event
, basada en la API keyboard
.
Esta función permite escribir caracteres en un elemento de entrada (input
/textarea
/contenteditable
). Soporta la sintaxis de teclado de user-event.
Si solo necesitas presionar caracteres sin un campo de entrada, utiliza la API userEvent.keyboard
.
import { page, userEvent } from '@vitest/browser/context';
test('actualiza la entrada', 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
Vitest no expone el método .type
directamente en el localizador (como input.type
) porque su existencia se limita a la compatibilidad con la biblioteca userEvent
. Se recomienda usar .fill
en su lugar, ya que es más rápido.
Referencias:
userEvent.clear
function clear(
element: Element | Locator,
options?: UserEventClearOptions
): Promise<void>;
Este método vacía el contenido del elemento de entrada.
import { page, userEvent } from '@vitest/browser/context';
test('borra la entrada', async () => {
const input = page.getByRole('input');
await userEvent.fill(input, 'foo');
expect(input).toHaveValue('foo');
await userEvent.clear(input);
// o puedes acceder a él directamente en el localizador
await input.clear();
expect(input).toHaveValue('');
});
Referencias:
userEvent.selectOptions
function selectOptions(
element: Element | Locator,
values: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
options?: UserEventSelectOptions
): Promise<void>;
userEvent.selectOptions
permite la selección de un valor en un elemento <select>
.
WARNING
Si el elemento <select>
no tiene el atributo multiple
, Vitest seleccionará únicamente el primer elemento del array.
A diferencia de @testing-library
, Vitest no es compatible con listbox
en este momento, pero planeamos añadirle soporte en el futuro.
import { page, userEvent } from '@vitest/browser/context';
test('borra la entrada', async () => {
const select = page.getByRole('select');
await userEvent.selectOptions(select, 'Option 1');
// o puedes acceder a él directamente en el localizador
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
El proveedor webdriverio
no es compatible con la selección de múltiples elementos, ya que no proporciona una API para ello.
Referencias:
- API
locator.selectOption
de Playwright - API
element.selectByIndex
de WebdriverIO - API
selectOptions
de testing-library
userEvent.hover
function hover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Este método desplaza el cursor al elemento seleccionado. Para una explicación detallada sobre el funcionamiento de este método, consulta la documentación de tu proveedor.
WARNING
Si se utiliza el proveedor webdriverio
, el cursor se moverá al centro del elemento por defecto.
Si se utiliza el proveedor playwright
, el cursor se mueve a un punto visible "cualquiera" del elemento.
import { page, userEvent } from '@vitest/browser/context';
test('pasa el ratón sobre el elemento del logo', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.hover(logo);
// o puedes acceder a él directamente en el localizador
await logo.hover();
});
Referencias:
userEvent.unhover
function unhover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Esto funciona igual que userEvent.hover
, pero mueve el cursor al elemento document.body
.
WARNING
Por defecto, la posición del cursor se encuentra en un lugar visible "cualquiera" (en el proveedor playwright
) o en el centro (en el proveedor webdriverio
) del elemento body. Por lo tanto, si el elemento actualmente sobrevolado ya está en la misma posición, este método no tendrá ningún efecto.
import { page, userEvent } from '@vitest/browser/context';
test('quita el ratón del elemento del logo', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.unhover(logo);
// o puedes acceder a él directamente en el localizador
await logo.unhover();
});
Referencias:
userEvent.upload
function upload(
element: Element | Locator,
files: string[] | string | File[] | File,
options?: UserEventUploadOptions
): Promise<void>;
Asigna los archivos especificados a un elemento de entrada de archivo.
import { page, userEvent } from '@vitest/browser/context';
test('puede subir un archivo', async () => {
const input = page.getByRole('button', { name: /Upload files/ });
const file = new File(['file'], 'file.png', { type: 'image/png' });
await userEvent.upload(input, file);
// o puedes acceder a él directamente en el localizador
await input.upload(file);
// también se pueden usar rutas de archivo relativas a la raíz del proyecto
await userEvent.upload(input, './fixtures/file.png');
});
WARNING
El proveedor webdriverio
solo es compatible con este comando en los navegadores Chrome y Edge. Además, actualmente solo admite tipos de cadena.
Referencias:
- API
locator.setInputFiles
de Playwright - API
browser.uploadFile
de WebdriverIO - API
upload
de testing-library
userEvent.dragAndDrop
function dragAndDrop(
source: Element | Locator,
target: Element | Locator,
options?: UserEventDragAndDropOptions
): Promise<void>;
Arrastra el elemento de origen hasta el elemento de destino. Es importante recordar que el elemento source
debe tener el atributo draggable
establecido en true
.
import { page, userEvent } from '@vitest/browser/context';
test('el arrastrar y soltar funciona', async () => {
const source = page.getByRole('img', { name: /logo/ });
const target = page.getByTestId('logo-target');
await userEvent.dragAndDrop(source, target);
// o puedes acceder a él directamente en el localizador
await source.dropTo(target);
await expect.element(target).toHaveTextContent('Logo is processed');
});
WARNING
Esta API no es compatible con el proveedor preview
predeterminado.
Referencias:
userEvent.copy
function copy(): Promise<void>;
Copia el texto seleccionado en el portapapeles.
import { page, userEvent } from '@vitest/browser/context';
test('copiar y pegar', async () => {
// escribe en 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// selecciona y copia 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.copy();
// pega en 'target'
await userEvent.click(page.getByPlaceholder('target'));
await userEvent.paste();
await expect
.element(page.getByPlaceholder('source'))
.toHaveTextContent('hello');
await expect
.element(page.getByPlaceholder('target'))
.toHaveTextContent('hello');
});
Referencias:
userEvent.cut
function cut(): Promise<void>;
Corta el texto seleccionado en el portapapeles.
import { page, userEvent } from '@vitest/browser/context';
test('copiar y pegar', async () => {
// escribe en 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// selecciona y corta 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.cut();
// pega en 'target'
await userEvent.click(page.getByPlaceholder('target'));
await userEvent.paste();
await expect.element(page.getByPlaceholder('source')).toHaveTextContent('');
await expect
.element(page.getByPlaceholder('target'))
.toHaveTextContent('hello');
});
Referencias:
userEvent.paste
function paste(): Promise<void>;
Pega el texto desde el portapapeles. Para ver ejemplos de uso, consulta userEvent.copy
y userEvent.cut
.
Referencias: