Interactivity 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
(в зависимости от используемого провайдера) в ваш файл настройки или файл конфигурации (в зависимости от того, что указано в included
в вашем tsconfig.json
):
/// <reference types="@vitest/browser/providers/playwright" />
/// <reference 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
, этот метод будет вызывать три события клика подряд. Для фильтрации события необходимо проверить детали события клика: 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: реализовано через 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
(type
), построенную на основе API keyboard
.
Эта функция позволяет вводить символы в элемент input
/textarea
/contenteditable
. Она поддерживает синтаксис 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,
options?: UserEventClearOptions
): 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,
options?: UserEventUploadOptions
): 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
по умолчанию.
Ссылки:
userEvent.copy
function copy(): Promise<void>;
Копирует выделенный текст в буфер обмена.
import { page, userEvent } from '@vitest/browser/context';
test('copy and paste', async () => {
// ввести в 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// выделить и скопировать содержимое 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.copy();
// вставить в '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');
});
Ссылки:
userEvent.cut
function cut(): Promise<void>;
Вырезает выделенный текст в буфер обмена.
import { page, userEvent } from '@vitest/browser/context';
test('copy and paste', async () => {
// ввести в 'source'
await userEvent.click(page.getByPlaceholder('source'));
await userEvent.keyboard('hello');
// выделить и вырезать содержимое 'source'
await userEvent.dblClick(page.getByPlaceholder('source'));
await userEvent.cut();
// вставить в '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');
});
Ссылки:
userEvent.paste
function paste(): Promise<void>;
Вставляет текст из буфера обмена. Примеры использования приведены в разделах userEvent.copy
и userEvent.cut
.
Ссылки: