API interakcji użytkownika
Vitest implementuje podzbiór API @testing-library/user-event
, wykorzystując Chrome DevTools Protocol lub webdriver zamiast symulowania zdarzeń. Dzięki temu zachowanie przeglądarki jest bardziej niezawodne i spójne z tym, jak użytkownicy wchodzą w interakcję ze stroną internetową.
import { userEvent } from '@vitest/browser/context';
await userEvent.click(document.querySelector('.button'));
Prawie każda metoda userEvent
dziedziczy opcje od swojego dostawcy. Aby zobaczyć wszystkie dostępne opcje w swoim IDE, dodaj typy webdriver
lub playwright
(w zależności od używanego dostawcy) do pliku tsconfig.json
:
{
"compilerOptions": {
"types": ["@vitest/browser/providers/playwright"]
}
}
{
"compilerOptions": {
"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 prawidłowo symulować naciskanie i zwalnianie klawiszy.
WARNING
W przeciwieństwie do @testing-library/user-event
, domyślna instancja userEvent
z @vitest/browser/context
jest tworzona raz, a nie za każdym razem, gdy wywoływane są jej metody! Różnicę w działaniu można zobaczyć w poniższym przykładzie:
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}'); // zwalnia Shift
await originalUserEvent.keyboard('{Shift}'); // naciśnij Shift bez zwalniania
await originalUserEvent.keyboard('{/Shift}'); // NIE zwolnił klawisza Shift, ponieważ stan jest inny
To zachowanie jest bardziej użyteczne, ponieważ nie emulujemy klawiatury, faktycznie symulujemy naciśnięcie klawisza Shift, więc zachowanie oryginalnego zachowania spowodowałoby nieoczekiwane problemy podczas wpisywania w polu tekstowym.
userEvent.click
function click(
element: Element | Locator,
options?: UserEventClickOptions
): Promise<void>;
Kliknij na element. Dziedziczy opcje od dostawcy. Szczegółowe wyjaśnienie działania tej metody można znaleźć w dokumentacji używanego 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
await logo.click();
});
Referencje:
userEvent.dblClick
function dblClick(
element: Element | Locator,
options?: UserEventDoubleClickOptions
): Promise<void>;
Wyzwala zdarzenie podwójnego kliknięcia na danym elemencie.
Szczegółowe wyjaśnienie działania tej metody można znaleźć w dokumentacji używanego 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
await logo.dblClick();
});
Referencje:
userEvent.tripleClick
function tripleClick(
element: Element | Locator,
options?: UserEventTripleClickOptions
): Promise<void>;
Wyzwala zdarzenie potrójnego kliknięcia na danym elemencie. Ponieważ w API przeglądarki nie ma 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 można znaleźć w dokumentacji używanego 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
await logo.tripleClick();
expect(tripleClickFired).toBe(true);
});
Referencje:
- Playwright
locator.click
API: zaimplementowane za pomocą metodyclick
z opcjąclickCount: 3
. - WebdriverIO
browser.action
API: zaimplementowane za pomocą API akcji z operacjąmove
plus trzy zdarzeniadown + up + pause
z rzędu - testing-library
tripleClick
API
userEvent.fill
function fill(element: Element | Locator, text: string): Promise<void>;
Ustawia wartość w polu input
, textarea
lub conteneditable
. Spowoduje to usunięcie 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
await input.fill('foo'); // input.value == foo
});
Ta metoda ustawia fokus na elemencie, wypełnia go i wyzwala zdarzenie input
po zakończeniu wypełniania. Możesz użyć pustego ciągu znaków, aby wyczyścić pole tekstowe.
TIP
To API jest szybsze niż użycie userEvent.type
lub userEvent.keyboard
, ale nie obsługuje 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 naciśnięcia klawiszy.
Referencje:
userEvent.keyboard
function keyboard(text: string): Promise<void>;
userEvent.keyboard
pozwala symulować naciśnięcia klawiszy. Jeśli jakiekolwiek pole wejściowe ma fokus, wpisze znaki do tego pola. W przeciwnym razie wyzwoli zdarzenia klawiatury na aktualnie skupionym elemencie (lub na 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'); // co przekłada się na: f, o, o
await userEvent.keyboard('{{a[['); // co przekłada się na: {, a, [
await userEvent.keyboard('{Shift}{f}{o}{o}'); // co przekłada się na: Shift, f, o, o
await userEvent.keyboard('{a>5}'); // naciśnij klawisz 'a' bez zwalniania i wyzwól 5 zdarzeń `keydown`
await userEvent.keyboard('{a>5/}'); // naciśnij klawisz 'a' przez 5 zdarzeń `keydown`, a następnie zwolnij
});
Referencje:
userEvent.tab
function tab(options?: UserEventTabOptions): Promise<void>;
Wysyła zdarzenie naciśnięcia klawisza Tab
. Jest to skrót dla wywołania 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 biblioteki @testing-library/user-event
, zbudowane na podstawie API keyboard
.
Ta funkcja pozwala wpisywać znaki do elementu input
, textarea
lub conteneditable
. Obsługuje składnię keyboard
user-event.
Jeśli potrzebujesz tylko symulować naciśnięcia klawiszy bez pola wejściowego, 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
na lokatorze, takiej jak input.type
, ponieważ istnieje tylko dla kompatybilności z biblioteką userEvent
. Rozważ użycie metody .fill
zamiast tego, ponieważ jest szybsza.
Referencje:
userEvent.clear
function clear(element: Element | Locator): Promise<void>;
Ta metoda czyści zawartość elementu 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);
// lub możesz uzyskać dostęp bezpośrednio poprzez lokator
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 podanej tablicy.
W przeciwieństwie do @testing-library
, Vitest nie obsługuje obecnie listbox, ale planujemy dodać wsparcie 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
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 API do tego celu.
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 pozycję kursora do wybranego elementu. Szczegółowe wyjaśnienie działania tej metody można znaleźć w dokumentacji używanego 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 "jakiegoś" 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
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 "jakimś" widocznym miejscu (w przypadku dostawcy playwright
) lub w środku (w przypadku dostawcy webdriverio
) elementu body, więc jeśli aktualnie najeżdżany element 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
await logo.unhover();
});
Referencje:
userEvent.upload
function upload(
element: Element | Locator,
files: string[] | string | File[] | File
): Promise<void>;
Zmienia element input
typu file
, aby zawierał 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 możesz uzyskać dostęp bezpośrednio poprzez lokator
await input.upload(file);
// możesz również użyć ścieżek plików względnych w stosunku do pliku testowego
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 typy string
.
Referencje:
userEvent.dragAndDrop
function dragAndDrop(
source: Element | Locator,
target: Element | Locator,
options?: UserEventDragAndDropOptions
): Promise<void>;
Przeciąga element źródłowy na element docelowy. Nie zapomnij, że element source
musi mieć ustawiony atrybut draggable
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 możesz uzyskać dostęp bezpośrednio poprzez lokator
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: