API взаимодействия
Vitest реализует подмножество API @testing-library/user-event
, используя Chrome DevTools Protocol или webdriver вместо имитации событий. Это обеспечивает более надежное поведение в браузере и лучше соответствует тому, как пользователи взаимодействуют со страницей.
import { userEvent } from '@vitest/browser/context';
await userEvent.click(document.querySelector('.button'));
Практически каждый метод userEvent
наследует параметры от своего провайдера. Чтобы увидеть все доступные параметры в вашей IDE, добавьте типы webdriver
или playwright
(в зависимости от используемого провайдера) в файл tsconfig.json
:
{
"compilerOptions": {
"types": ["@vitest/browser/providers/playwright"]
}
}
{
"compilerOptions": {
"types": ["@vitest/browser/providers/webdriverio"]
}
}
userEvent.setup
function setup(): UserEvent;
Создает новый экземпляр пользовательского события. Это полезно, если необходимо сохранить состояние клавиатуры для корректной имитации нажатия и отпускания клавиш.
WARNING
В отличие от @testing-library/user-event
, экземпляр userEvent
по умолчанию из @vitest/browser/context
создается один раз, а не при каждом вызове его методов! Разницу в поведении можно увидеть в следующем примере:
import { userEvent as vitestUserEvent } from '@vitest/browser/context';
import { userEvent as originalUserEvent } from '@testing-library/user-event';
await vitestUserEvent.keyboard('{Shift}'); // нажать Shift без отпускания
await vitestUserEvent.keyboard('{/Shift}'); // отпустить Shift
await originalUserEvent.keyboard('{Shift}'); // нажать Shift без отпускания
await originalUserEvent.keyboard('{/Shift}'); // НЕ ОТПУСТИЛ Shift, потому что состояние другое
Такое поведение более полезно, поскольку мы не эмулируем клавиатуру, а фактически нажимаем Shift. Сохранение исходного поведения могло бы вызвать неожиданные проблемы при вводе текста в поле.
userEvent.click
function click(
element: Element | Locator,
options?: UserEventClickOptions
): Promise<void>;
Выполняет щелчок по элементу. Наследует параметры провайдера. Подробное описание работы этого метода см. в документации вашего провайдера.
import { page, userEvent } from '@vitest/browser/context';
test('clicks on an element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.click(logo);
// или можно получить доступ к нему непосредственно с помощью локатора
await logo.click();
});
Ссылки на документацию:
userEvent.dblClick
function dblClick(
element: Element | Locator,
options?: UserEventDoubleClickOptions
): Promise<void>;
Вызывает событие двойного щелчка на элементе.
Подробное описание работы этого метода см. в документации вашего провайдера.
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);
// или можно получить доступ к нему непосредственно с помощью локатора
await logo.dblClick();
});
Ссылки на документацию:
userEvent.tripleClick
function tripleClick(
element: Element | Locator,
options?: UserEventTripleClickOptions
): Promise<void>;
Вызывает событие тройного щелчка на элементе. Поскольку в API браузера отсутствует tripleclick
, этот метод вызовет три события щелчка подряд. Для обработки тройного щелчка следует проверять свойство detail
события click: evt.detail === 3
.
Подробное описание работы этого метода см. в документации вашего провайдера.
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);
// или можно получить доступ к нему непосредственно с помощью локатора
await logo.tripleClick();
expect(tripleClickFired).toBe(true);
});
Ссылки на документацию:
- Playwright
locator.click
API: реализовано черезclick
сclickCount: 3
. - WebdriverIO
browser.action
API: реализовано через actions api сmove
плюс три событияdown + up + pause
подряд. - testing-library
tripleClick
API
userEvent.fill
function fill(element: Element | Locator, text: string): Promise<void>;
Устанавливает значение в поле input
, textarea
или contenteditable
. Перед установкой нового значения существующий текст в поле ввода будет удален.
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}
// или можно получить доступ к нему непосредственно с помощью локатора
await input.fill('foo'); // input.value == foo
});
Этот метод устанавливает фокус на элемент, заполняет его и вызывает событие input
после заполнения. Для очистки поля можно использовать пустую строку.
TIP
Этот API быстрее, чем использование userEvent.type
или userEvent.keyboard
, но он не поддерживает синтаксис keyboard
user-event (например, {Shift}{selectall}
).
Рекомендуется использовать этот API вместо userEvent.type
в ситуациях, когда не требуется вводить специальные символы или иметь детальный контроль над событиями нажатия клавиш.
Ссылки на документацию:
userEvent.keyboard
function keyboard(text: string): Promise<void>;
userEvent.keyboard
позволяет имитировать нажатия клавиш. Если какой-либо элемент ввода имеет фокус, символы будут вводиться в этот элемент. В противном случае события клавиатуры будут вызываться на текущем сфокусированном элементе (document.body
, если сфокусированных элементов нет).
Этот API поддерживает синтаксис keyboard
из user-event.
import { userEvent } from '@vitest/browser/context';
test('trigger keystrokes', async () => {
await userEvent.keyboard('foo'); // соответствует последовательности: f, o, o
await userEvent.keyboard('{{a[['); // соответствует последовательности: {, a, [
await userEvent.keyboard('{Shift}{f}{o}{o}'); // соответствует последовательности: Shift, f, o, o
await userEvent.keyboard('{a>5}'); // нажать a без отпускания и вызвать 5 событий keydown
await userEvent.keyboard('{a>5/}'); // нажать a для 5 событий keydown, а затем отпустить
});
Ссылки на документацию:
userEvent.tab
function tab(options?: UserEventTabOptions): Promise<void>;
Отправляет событие нажатия клавиши Tab
. Это является сокращением для 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();
});
Ссылки на документацию:
userEvent.type
function type(
element: Element | Locator,
text: string,
options?: UserEventTypeOptions
): Promise<void>;
WARNING
Если вы не полагаетесь на специальные символы (например, {shift}
или {selectall}
), рекомендуется использовать userEvent.fill
вместо этого для лучшей производительности.
Метод type
реализует утилиту type
из @testing-library/user-event
, построенную на основе API keyboard
.
Эта функция позволяет вводить символы в элемент input, textarea или conteneditable. Она поддерживает синтаксис keyboard
user-event.
Если вам просто нужно имитировать нажатие символов без ввода, используйте 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 не предоставляет метод .type
на локаторе, как input.type
, поскольку он существует только для совместимости с библиотекой userEvent
. Рассмотрите возможность использования .fill
вместо него, поскольку он быстрее.
Ссылки на документацию:
userEvent.clear
function clear(element: Element | Locator): Promise<void>;
Этот метод очищает содержимое элемента ввода.
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);
// или можно получить доступ к нему непосредственно с помощью локатора
await input.clear();
expect(input).toHaveValue('');
});
Ссылки на документацию:
userEvent.selectOptions
function selectOptions(
element: Element | Locator,
values: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
options?: UserEventSelectOptions
): Promise<void>;
userEvent.selectOptions
позволяет выбрать значение в элементе <select>
.
WARNING
Если элемент select не имеет атрибута multiple
, Vitest выберет только первый элемент в массиве.
В отличие от @testing-library
, Vitest в настоящее время не поддерживает listbox, но мы планируем добавить его поддержку в будущем.
import { page, userEvent } from '@vitest/browser/context';
test('clears input', async () => {
const select = page.getByRole('select');
await userEvent.selectOptions(select, 'Option 1');
// или можно получить доступ к нему непосредственно с помощью локатора
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
Провайдер webdriverio
не поддерживает выбор нескольких элементов, поскольку он не предоставляет API для этого.
Ссылки на документацию:
- Playwright
locator.selectOption
API - WebdriverIO
element.selectByIndex
API - testing-library
selectOptions
API
userEvent.hover
function hover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Этот метод перемещает курсор к выбранному элементу. Подробное описание работы этого метода см. в документации вашего провайдера.
WARNING
Если вы используете провайдер webdriverio
, курсор по умолчанию переместится в центр элемента.
Если вы используете провайдер playwright
, курсор перемещается в "некоторую" видимую точку элемента.
import { page, userEvent } from '@vitest/browser/context';
test('hovers logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.hover(logo);
// или можно получить доступ к нему непосредственно с помощью локатора
await logo.hover();
});
Ссылки на документацию:
userEvent.unhover
function unhover(
element: Element | Locator,
options?: UserEventHoverOptions
): Promise<void>;
Этот метод работает так же, как userEvent.hover
, но вместо этого перемещает курсор к элементу document.body
.
WARNING
По умолчанию положение курсора находится в "некотором" видимом месте (в провайдере playwright
) или в центре (в провайдере webdriverio
) элемента body. Поэтому, если текущий наведенный элемент уже находится в том же положении, этот метод не будет иметь никакого эффекта.
import { page, userEvent } from '@vitest/browser/context';
test('unhover logo element', async () => {
const logo = page.getByRole('img', { name: /logo/ });
await userEvent.unhover(logo);
// или можно получить доступ к нему непосредственно с помощью локатора
await logo.unhover();
});
Ссылки на документацию:
userEvent.upload
function upload(
element: Element | Locator,
files: string[] | string | File[] | File
): Promise<void>;
Изменяет элемент ввода файла, чтобы он содержал указанные файлы.
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);
// или можно получить доступ к нему непосредственно с помощью локатора
await input.upload(file);
// также можно использовать пути к файлам относительно тестового файла
await userEvent.upload(input, '../fixtures/file.png');
});
WARNING
Провайдер webdriverio
поддерживает эту команду только в браузерах chrome
и edge
. В настоящее время он также поддерживает только строковые типы.
Ссылки на документацию:
userEvent.dragAndDrop
function dragAndDrop(
source: Element | Locator,
target: Element | Locator,
options?: UserEventDragAndDropOptions
): Promise<void>;
Перетаскивает исходный элемент на целевой элемент. Не забывайте, что исходный элемент должен иметь атрибут draggable
, установленный в 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);
// или можно получить доступ к нему непосредственно с помощью локатора
await source.dropTo(target);
await expect.element(target).toHaveTextContent('Logo is processed');
});
WARNING
Этот API не поддерживается провайдером preview
по умолчанию.
Ссылки на документацию: