Interaktywne API
Vitest implementuje podzbiór API @testing-library/user-event
, wykorzystując Protokół Chrome DevTools lub webdriver zamiast symulowania zdarzeń. Dzięki temu interakcje z przeglądarką są bardziej niezawodne i spójne z rzeczywistym zachowaniem użytkowników.
import { userEvent } from '@vitest/browser/context';
await userEvent.click(document.querySelector('.button'));
Prawie każda metoda userEvent
dziedziczy opcje od swojego dostawcy. Aby uzyskać dostęp do wszystkich dostępnych opcji w swoim IDE, dodaj typy webdriver
lub playwright
(w zależności od używanego dostawcy) do swojego pliku konfiguracyjnego uruchomieniowego lub pliku konfiguracyjnego, w zależności od tego, co jest zawarte w sekcji included
w twoim tsconfig.json
:
/// <reference types="@vitest/browser/providers/playwright" />
/// <reference types="@vitest/browser/providers/webdriverio" />
userEvent.setup
function setup(): UserEvent;
Tworzy nową instancję zdarzenia użytkownika. Jest to przydatne, gdy musisz zachować stan klawiatury, aby poprawnie symulować naciśnięcia i zwolnienia klawiszy.
WARNING
W przeciwieństwie do @testing-library/user-event
, domyślna instancja userEvent
z @vitest/browser/context
jest tworzona jednorazowo, a nie przy każdym wywołaniu jej metod! Różnicę w działaniu można zaobserwować w poniższym fragmencie:
import { userEvent as vitestUserEvent } from '@vitest/browser/context';
import { userEvent as originalUserEvent } from '@testing-library/user-event';
await vitestUserEvent.keyboard('{Shift}'); // naciśnij Shift bez zwalniania
await vitestUserEvent.keyboard('{/Shift}'); // zwolnij Shift
await originalUserEvent.keyboard('{Shift}'); // naciśnij Shift bez zwalniania
await originalUserEvent.keyboard('{/Shift}'); // NIE zwolnił Shift, ponieważ stan jest inny
Takie zachowanie jest bardziej użyteczne, ponieważ nie emulujemy klawiatury, lecz faktycznie naciskamy klawisz Shift. Utrzymanie oryginalnego zachowania mogłoby prowadzić do nieoczekiwanych problemów podczas wpisywania tekstu w polu.
userEvent.click
function click(
element: Element | Locator,
options?: UserEventClickOptions
): Promise<void>;
Kliknij element. Dziedziczy opcje dostawcy. Szczegóły dotyczące działania tej metody znajdziesz w dokumentacji swojego dostawcy.
import { page, userEvent } from '@vitest/browser/context';
test('clicks on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.click(logo);
// lub użyj metody bezpośrednio na lokatorze
await logo.click();
});
Referencje:
userEvent.dblClick
function dblClick(
element: Element | Locator,
options?: UserEventDoubleClickOptions
): Promise<void>;
Wyzwala zdarzenie podwójnego kliknięcia na elemencie.
Szczegółowe wyjaśnienie działania tej metody znajdziesz w dokumentacji swojego dostawcy.
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);
// lub użyj metody bezpośrednio na lokatorze
await logo.dblClick();
});
Referencje:
userEvent.tripleClick
function tripleClick(
element: Element | Locator,
options?: UserEventTripleClickOptions
): Promise<void>;
Wyzwala zdarzenie potrójnego kliknięcia na elemencie. Ponieważ w API przeglądarki nie ma natywnego zdarzenia tripleclick
, ta metoda wywoła trzy zdarzenia kliknięcia z rzędu. Aby odfiltrować to zdarzenie, musisz sprawdzić szczegóły zdarzenia kliknięcia: evt.detail === 3
.
Szczegółowe wyjaśnienie działania tej metody znajdziesz w dokumentacji swojego dostawcy.
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);
// lub użyj metody bezpośrednio na lokatorze
await logo.tripleClick();
expect(tripleClickFired).toBe(true);
});
Referencje:
- Playwright
locator.click
API: zaimplementowane poprzezclick
z parametremclickCount: 3
. - WebdriverIO
browser.action
API: zaimplementowane za pomocą API akcji, obejmującegomove
oraz trzy kolejne zdarzeniadown + up + pause
. - testing-library
tripleClick
API
userEvent.fill
function fill(element: Element | Locator, text: string): Promise<void>;
Ustawia wartość w polu input
/textarea
/contenteditable
. Spowoduje to usunięcie wszelkiego istniejącego tekstu w polu przed ustawieniem nowej wartości.
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}
// lub użyj metody bezpośrednio na lokatorze
await input.fill('foo'); // input.value == foo
});
Ta metoda ustawia fokus na elemencie, wypełnia go i wyzwala zdarzenie input
po wypełnieniu. Możesz użyć pustego ciągu znaków, aby wyczyścić pole.
TIP
To API jest szybsze niż użycie userEvent.type
lub userEvent.keyboard
, ale nie obsługuje specjalnej składni keyboard
user-event (np. {Shift}{selectall}
).
Zalecamy korzystanie z tego API zamiast userEvent.type
, gdy nie potrzebujesz wprowadzać znaków specjalnych ani mieć szczegółowej kontroli nad zdarzeniami klawiatury.
Referencje:
userEvent.keyboard
function keyboard(text: string): Promise<void>;
userEvent.keyboard
pozwala wyzwalać naciśnięcia klawiszy. Jeśli jakiekolwiek pole wejściowe jest aktywne (ma fokus), wpisze znaki do tego pola. W przeciwnym razie wyzwoli zdarzenia klawiatury na aktualnie skupionym elemencie (document.body
, jeśli nie ma skupionych elementów).
To API obsługuje składnię keyboard
user-event.
import { userEvent } from '@vitest/browser/context';
test('trigger keystrokes', async () => {
await userEvent.keyboard('foo'); // generuje sekwencję: f, o, o
await userEvent.keyboard('{{a[['); // generuje sekwencję: {, a, [
await userEvent.keyboard('{Shift}{f}{o}{o}'); // generuje sekwencję: Shift, f, o, o
await userEvent.keyboard('{a>5}'); // naciśnij 'a' bez zwalniania i wygeneruj 5 zdarzeń naciśnięcia klawisza
await userEvent.keyboard('{a>5/}'); // naciśnij 'a' przez 5 zdarzeń naciśnięcia klawisza, a następnie zwolnij
});
Referencje:
userEvent.tab
function tab(options?: UserEventTabOptions): Promise<void>;
Wysyła zdarzenie klawisza Tab
. Jest to skrót dla 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();
});
Referencje:
userEvent.type
function type(
element: Element | Locator,
text: string,
options?: UserEventTypeOptions
): Promise<void>;
WARNING
Jeśli nie polegasz na znakach specjalnych (np. {shift}
lub {selectall}
), zaleca się użycie userEvent.fill
zamiast tego dla lepszej wydajności.
Metoda type
implementuje narzędzie type
z @testing-library/user-event
, oparte na API keyboard
.
Ta funkcja pozwala wpisywać znaki do elementu input
/textarea
/contenteditable
. Obsługuje składnię keyboard
user-event.
Jeśli potrzebujesz tylko symulować naciśnięcia klawiszy bez interakcji z polem wejściowym, użyj 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 nie udostępnia metody .type
bezpośrednio na lokatorze (np. input.type
), ponieważ istnieje ona tylko dla kompatybilności z biblioteką userEvent
. Rozważ użycie .fill
zamiast tego, ponieważ jest szybsze.
Referencje:
userEvent.clear
function clear(
element: Element | Locator,
options?: UserEventClearOptions
): Promise<void>;
Ta metoda czyści zawartość elementu wejściowego.
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);
// lub użyj metody bezpośrednio na lokatorze
await input.clear();
expect(input).toHaveValue('');
});
Referencje:
userEvent.selectOptions
function selectOptions(
element: Element | Locator,
values: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
options?: UserEventSelectOptions
): Promise<void>;
userEvent.selectOptions
pozwala wybrać wartość w elemencie <select>
.
WARNING
Jeśli element <select>
nie ma atrybutu multiple
, Vitest wybierze tylko pierwszy element z tablicy.
W przeciwieństwie do @testing-library
, Vitest nie obsługuje obecnie listboxów, ale planujemy dodać ich obsługę w przyszłości.
import { page, userEvent } from '@vitest/browser/context';
test('clears input', async () => {
const select = page.getByRole('select');
await userEvent.selectOptions(select, 'Option 1');
// lub użyj metody bezpośrednio na lokatorze
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
Dostawca webdriverio
nie obsługuje wybierania wielu elementów, ponieważ nie udostępnia do tego odpowiedniego API.
Referencje:
- Playwright
locator.selectOption
API - WebdriverIO
element.selectByIndex
API - testing-library
selectOptions
API
userEvent.hover
function hover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Ta metoda przesuwa kursor nad wybrany element. Szczegółowe wyjaśnienie działania tej metody znajdziesz w dokumentacji swojego dostawcy.
WARNING
Jeśli używasz dostawcy webdriverio
, kursor domyślnie przesunie się do środka elementu.
Jeśli używasz dostawcy playwright
, kursor przesuwa się do dowolnego widocznego punktu elementu.
import { page, userEvent } from '@vitest/browser/context';
test('hovers logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.hover(logo);
// lub użyj metody bezpośrednio na lokatorze
await logo.hover();
});
Referencje:
userEvent.unhover
function unhover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Działa tak samo jak userEvent.hover
, ale przesuwa kursor do elementu document.body
.
WARNING
Domyślnie pozycja kursora znajduje się w dowolnym widocznym miejscu (w dostawcy playwright
) lub w centrum (w dostawcy webdriverio
) elementu body
. Jeśli element pod wskaźnikiem myszy znajduje się już w tej samej pozycji, ta metoda nie będzie miała żadnego efektu.
import { page, userEvent } from '@vitest/browser/context';
test('unhover logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.unhover(logo);
// lub użyj metody bezpośrednio na lokatorze
await logo.unhover();
});
Referencje:
userEvent.upload
function upload(
element: Element | Locator,
files: string[] | string | File[] | File,
options?: UserEventUploadOptions
): Promise<void>;
Zmienia zawartość pola wejściowego typu plik, aby zawierało określone pliki.
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);
// lub użyj metody bezpośrednio na lokatorze
await input.upload(file);
// możesz również użyć ścieżek plików względnych do katalogu głównego projektu
await userEvent.upload(input, './fixtures/file.png');
});
WARNING
Dostawca webdriverio
obsługuje to polecenie tylko w przeglądarkach chrome
i edge
. Obecnie obsługuje również tylko ścieżki plików (typy string).
Referencje:
userEvent.dragAndDrop
function dragAndDrop(
source: Element | Locator,
target: Element | Locator,
options?: UserEventDragAndDropOptions
): Promise<void>;
Przeciąga element źródłowy nad element docelowy. Należy pamiętać, że element source
musi mieć atrybut draggable
ustawiony na 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);
// lub użyj metody bezpośrednio na lokatorze
await source.dropTo(target);
await expect.element(target).toHaveTextContent('Logo is processed');
});
WARNING
To API nie jest obsługiwane przez domyślnego dostawcę preview
.
Referencje:
userEvent.copy
function copy(): Promise<void>;
Kopiuje zaznaczony tekst do schowka.
import { page, userEvent } from '@vitest/browser/context';
test('copy and paste', async () => {
// wpisz do pola 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// zaznacz tekst i skopiuj zawartość pola 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.copy();
// wklej tekst do pola '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');
});
Referencje:
userEvent.cut
function cut(): Promise<void>;
Wytnij zaznaczony tekst do schowka.
import { page, userEvent } from '@vitest/browser/context';
test('copy and paste', async () => {
// wpisz do pola 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// zaznacz tekst i wytnij zawartość pola 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.cut();
// wklej tekst do pola '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');
});
Referencje:
userEvent.paste
function paste(): Promise<void>;
Wklej tekst ze schowka. Przykłady użycia znajdziesz w userEvent.copy
i userEvent.cut
.
Referencje: