API di Interazione
Vitest implementa un sottoinsieme delle API di @testing-library/user-event
utilizzando il Chrome DevTools Protocol o webdriver anziché simulare eventi. Questo approccio garantisce un comportamento del browser più affidabile e coerente con il modo in cui gli utenti interagiscono realmente con una pagina.
import { userEvent } from '@vitest/browser/context';
await userEvent.click(document.querySelector('.button'));
Quasi tutti i metodi userEvent
ereditano le opzioni del provider. Per visualizzare tutte le opzioni disponibili nel tuo IDE, aggiungi i tipi webdriver
o playwright
(a seconda del provider scelto) al tuo file di setup o a un file di configurazione (a seconda di ciò che è incluso in tsconfig.json
):
/// <reference types="@vitest/browser/providers/playwright" />
/// <reference types="@vitest/browser/providers/webdriverio" />
userEvent.setup
function setup(): UserEvent;
Crea una nuova istanza di interazione utente. Questo è utile quando è necessario mantenere lo stato della tastiera per gestire correttamente la pressione e il rilascio dei tasti.
WARNING
A differenza di @testing-library/user-event
, l'istanza userEvent
predefinita da @vitest/browser/context
viene creata una sola volta, non ad ogni chiamata dei suoi metodi! Puoi osservare la differenza nel suo funzionamento in questo snippet:
import { userEvent as vitestUserEvent } from '@vitest/browser/context';
import { userEvent as originalUserEvent } from '@testing-library/user-event';
await vitestUserEvent.keyboard('{Shift}'); // ha premuto Maiusc senza rilasciare
await vitestUserEvent.keyboard('{/Shift}'); // ha rilasciato Maiusc
await originalUserEvent.keyboard('{Shift}'); // ha premuto Maiusc senza rilasciare
await originalUserEvent.keyboard('{/Shift}'); // NON ha rilasciato Maiusc perché lo stato è diverso
Questo comportamento è più vantaggioso perché non emuliamo la tastiera; il tasto Maiusc viene effettivamente premuto, quindi mantenere il comportamento originale potrebbe causare problemi inattesi durante la digitazione in un campo di input.
userEvent.click
function click(
element: Element | Locator,
options?: UserEventClickOptions
): Promise<void>;
Esegue un clic su un elemento. Eredita le opzioni del provider. Per una spiegazione dettagliata sul funzionamento di questo metodo, fare riferimento alla documentazione del proprio provider.
import { page, userEvent } from '@vitest/browser/context';
test('clicks on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.click(logo);
// oppure puoi accedervi direttamente sul locator
await logo.click();
});
Riferimenti:
userEvent.dblClick
function dblClick(
element: Element | Locator,
options?: UserEventDoubleClickOptions
): Promise<void>;
Genera un evento di doppio clic su un elemento.
Per una spiegazione dettagliata sul funzionamento di questo metodo, fare riferimento alla documentazione del proprio provider.
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);
// oppure puoi accedervi direttamente sul locator
await logo.dblClick();
});
Riferimenti:
- API
locator.dblclick
di Playwright - API
element.doubleClick
di WebdriverIO - API
dblClick
di testing-library
userEvent.tripleClick
function tripleClick(
element: Element | Locator,
options?: UserEventTripleClickOptions
): Promise<void>;
Genera un evento di triplo clic su un elemento. Poiché non esiste un evento tripleclick
nelle API del browser, questo metodo attiverà tre eventi di clic in successione. Pertanto, è necessario controllare il dettaglio dell'evento click per filtrare l'evento: evt.detail === 3
.
Per una spiegazione dettagliata sul funzionamento di questo metodo, fare riferimento alla documentazione del proprio provider.
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);
// oppure puoi accedervi direttamente sul locator
await logo.tripleClick();
expect(tripleClickFired).toBe(true);
});
Riferimenti:
- API
locator.click
di Playwright: implementato tramiteclick
conclickCount: 3
. - API
browser.action
di WebdriverIO: implementato tramite l'API delle azioni conmove
e tre eventidown + up + pause
in successione. - API
tripleClick
di testing-library
userEvent.fill
function fill(element: Element | Locator, text: string): Promise<void>;
Imposta un valore in un campo input
, textarea
o contenteditable
. Questo metodo rimuove qualsiasi testo esistente nell'input prima di impostare il nuovo valore.
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}
// oppure puoi accedervi direttamente sul locator
await input.fill('foo'); // input.value == foo
});
Questo metodo imposta il focus sull'elemento, lo riempie e genera un evento input
dopo aver completato l'operazione. Puoi usare una stringa vuota per cancellare il campo.
TIP
Questa API è più veloce rispetto all'uso di userEvent.type
o userEvent.keyboard
, ma non supporta la sintassi keyboard
di user-event (ad esempio, {Shift}{selectall}
).
È consigliabile utilizzare questa API al posto di userEvent.type
in situazioni in cui non è necessario inserire caratteri speciali o avere un controllo granulare sugli eventi di pressione dei tasti.
Riferimenti:
userEvent.keyboard
function keyboard(text: string): Promise<void>;
userEvent.keyboard
consente di attivare gli input da tastiera. Se un input ha il focus, digita i caratteri in quell'input. Altrimenti, attiva gli eventi della tastiera sull'elemento attualmente a fuoco (che è document.body
se nessun elemento specifico ha il focus).
Questa API supporta la sintassi keyboard
di 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}'); // preme 'a' senza rilasciarlo e genera 5 eventi keydown
await userEvent.keyboard('{a>5/}'); // preme 'a' per 5 eventi keydown e poi lo rilascia
});
Riferimenti:
userEvent.tab
function tab(options?: UserEventTabOptions): Promise<void>;
Invia un evento di pressione del tasto Tab
. Questa è una scorciatoia per 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();
});
Riferimenti:
userEvent.type
function type(
element: Element | Locator,
text: string,
options?: UserEventTypeOptions
): Promise<void>;
WARNING
Se non ti affidi a caratteri speciali (ad esempio, {shift}
o {selectall}
), si consiglia di utilizzare userEvent.fill
invece per prestazioni migliori.
Il metodo type
implementa l'utility type
di @testing-library/user-event
(type
) basata sull'API keyboard
.
Questa funzione consente di digitare caratteri in un elemento input
/textarea
/contenteditable
. Supporta la sintassi keyboard
di user-event.
Se hai solo bisogno di premere caratteri senza un input, usa l'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
Vitest non espone il metodo .type
sul locator come input.type
perché esiste solo per compatibilità con la libreria userEvent
. Considera l'utilizzo di .fill
invece, poiché è più veloce.
Riferimenti:
userEvent.clear
function clear(
element: Element | Locator,
options?: UserEventClearOptions
): Promise<void>;
Questo metodo consente di cancellare il contenuto di un elemento input.
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);
// oppure puoi accedervi direttamente sul locator
await input.clear();
expect(input).toHaveValue('');
});
Riferimenti:
userEvent.selectOptions
function selectOptions(
element: Element | Locator,
values: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
options?: UserEventSelectOptions
): Promise<void>;
userEvent.selectOptions
consente di selezionare un valore in un elemento <select>
.
WARNING
Se l'elemento select non ha l'attributo multiple
, Vitest selezionerà solo il primo elemento nell'array.
Vitest al momento non supporta listbox, ma il supporto è previsto in futuro.
import { page, userEvent } from '@vitest/browser/context';
test('clears input', async () => {
const select = page.getByRole('select');
await userEvent.selectOptions(select, 'Option 1');
// oppure puoi accedervi direttamente sul 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
Il provider webdriverio
non supporta la selezione di più elementi perché non fornisce un'API per farlo.
Riferimenti:
- API
locator.selectOption
di Playwright - API
element.selectByIndex
di WebdriverIO - API
selectOptions
di testing-library
userEvent.hover
function hover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Questo metodo consente di spostare la posizione del cursore sull'elemento selezionato. Per una spiegazione dettagliata sul funzionamento di questo metodo, fare riferimento alla documentazione del proprio provider.
WARNING
Se si utilizza il provider webdriverio
, il cursore si sposterà al centro dell'elemento per impostazione predefinita.
Se si utilizza il provider playwright
, il cursore si sposta in "un" punto visibile dell'elemento.
import { page, userEvent } from '@vitest/browser/context';
test('hovers logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.hover(logo);
// oppure puoi accedervi direttamente sul locator
await logo.hover();
});
Riferimenti:
userEvent.unhover
function unhover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Questo metodo funziona in modo simile a userEvent.hover
, ma sposta il cursore sull'elemento document.body
.
WARNING
Per impostazione predefinita, la posizione del cursore è in un punto visibile (nel provider playwright
) o al centro (nel provider webdriverio
) del document.body
. Pertanto, se l'elemento attualmente in hover è già nella stessa posizione, questo metodo non avrà alcun effetto.
import { page, userEvent } from '@vitest/browser/context';
test('unhover logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.unhover(logo);
// oppure puoi accedervi direttamente sul locator
await logo.unhover();
});
Riferimenti:
userEvent.upload
function upload(
element: Element | Locator,
files: string[] | string | File[] | File,
options?: UserEventUploadOptions
): Promise<void>;
Consente di modificare un elemento di input file per assegnargli i file specificati.
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);
// oppure puoi accedervi direttamente sul locator
await input.upload(file);
// puoi anche usare percorsi relativi dei file rispetto alla root del progetto
await userEvent.upload(input, './fixtures/file.png');
});
WARNING
Il provider webdriverio
supporta questo comando solo nei browser Chrome
ed Edge
. Al momento supporta anche solo i tipi stringa.
Riferimenti:
- API
locator.setInputFiles
di Playwright - API
browser.uploadFile
di WebdriverIO - API
upload
di testing-library
userEvent.dragAndDrop
function dragAndDrop(
source: Element | Locator,
target: Element | Locator,
options?: UserEventDragAndDropOptions
): Promise<void>;
Consente di trascinare l'elemento sorgente sull'elemento target. È importante notare che l'elemento source
deve avere l'attributo draggable
impostato su 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);
// oppure puoi accedervi direttamente sul locator
await source.dropTo(target);
await expect.element(target).toHaveTextContent('Logo is processed');
});
WARNING
Questa API non è supportata dal provider preview
predefinito.
Riferimenti:
userEvent.copy
function copy(): Promise<void>;
Consente di copiare il testo selezionato negli appunti (clipboard).
import { page, userEvent } from '@vitest/browser/context';
test('copy and paste', async () => {
// scrivi nel campo 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// seleziona e copia il testo in 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.copy();
// incolla nel campo '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');
});
Riferimenti:
userEvent.cut
function cut(): Promise<void>;
Consente di tagliare il testo selezionato negli appunti.
import { page, userEvent } from '@vitest/browser/context';
test('copy and paste', async () => {
// scrivi nel campo 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// seleziona e taglia 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.cut();
// incolla nel campo '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');
});
Riferimenti:
userEvent.paste
function paste(): Promise<void>;
Consente di incollare il testo dagli appunti. Vedi userEvent.copy
e userEvent.cut
per esempi di utilizzo.
Riferimenti: