Skip to content
Vitest 3
Main Navigation 指南 & API配置浏览器模式高级 API
3.2.0
2.1.9
1.6.1
0.34.6

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

简体中文

English
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

主题

Sidebar Navigation

简介

为什么选择浏览器模式

浏览器模式

配置

浏览器配置

配置 Playwright

配置 WebdriverIO

API

Context API

交互 API

定位器

断言 API

命令 API

指南

多实例设置

配置参考

测试 API 参考

高级 API

页面导航

交互 API ​

Vitest 利用 Chrome DevTools Protocol 或 Webdriver 协议,实现了 @testing-library/user-event 的部分 API。与模拟事件不同,这种实现方式能更可靠地模拟浏览器行为,使其与用户实际操作页面的方式保持一致。

ts
import { userEvent } from '@vitest/browser/context';

await userEvent.click(document.querySelector('.button'));

几乎所有 userEvent 方法都支持其底层提供者的选项。若要在 IDE 中查看所有可用选项,请根据您使用的提供者(webdriver 或 playwright),将相应的类型定义添加到您的设置文件或配置文件中(具体取决于 tsconfig.json 中 included 的配置):

ts
/// <reference types="@vitest/browser/providers/playwright" />
ts
/// <reference types="@vitest/browser/providers/webdriverio" />

userEvent.setup ​

ts
function setup(): UserEvent;

创建新的用户事件实例。如果您需要保持键盘按键状态,以便正确模拟按压和释放操作,这非常有用。

WARNING

与 @testing-library/user-event 不同,来自 @vitest/browser/context 的默认 userEvent 实例只创建一次,而不是每次调用其方法时都创建!您可以通过以下代码片段了解其工作方式的差异:

ts
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 ​

ts
function click(
  element: Element | Locator,
  options?: UserEventClickOptions
): Promise<void>;

点击元素。此方法支持提供者(provider)的选项。有关此方法如何工作的详细说明,请参阅您的提供者文档。

ts
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();
});

参考:

  • Playwright locator.click API
  • WebdriverIO element.click API
  • testing-library click API

userEvent.dblClick ​

ts
function dblClick(
  element: Element | Locator,
  options?: UserEventDoubleClickOptions
): Promise<void>;

触发元素的双击事件。

有关此方法如何工作的详细说明,请参阅您的提供者文档。

ts
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();
});

参考:

  • Playwright locator.dblclick API
  • WebdriverIO element.doubleClick API
  • testing-library dblClick API

userEvent.tripleClick ​

ts
function tripleClick(
  element: Element | Locator,
  options?: UserEventTripleClickOptions
): Promise<void>;

在元素上触发三次点击事件。由于浏览器 API 中没有 tripleclick,此方法将连续触发三次点击事件。因此,您需要检查 click 事件详情来过滤事件,即 evt.detail === 3。

有关此方法如何工作的详细说明,请参阅您的提供者文档。

ts
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 ​

ts
function fill(element: Element | Locator, text: string): Promise<void>;

为 input/textarea/contenteditable 字段设置值。此操作会在设置新值之前清除输入框中已有的文本。

ts
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 更快,但它不支持 user-event keyboard 语法(例如,{Shift}{selectall})。

如果您不需要输入特殊字符或对按键事件进行精细控制,我们建议使用此 API 而不是 userEvent.type。

参考:

  • Playwright locator.fill API
  • WebdriverIO element.setValue API
  • testing-library type API

userEvent.keyboard ​

ts
function keyboard(text: string): Promise<void>;

userEvent.keyboard 可用于触发键盘按键操作。如果任何输入框处于焦点状态,它会将字符输入到该输入框中。否则,它将在当前聚焦的元素上触发键盘事件(如果没有聚焦元素,则在 document.body 上触发)。

此 API 支持 user-event keyboard 语法。

ts
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 然后释放
});

参考:

  • Playwright Keyboard API
  • WebdriverIO action('key') API
  • testing-library type API

userEvent.tab ​

ts
function tab(options?: UserEventTabOptions): Promise<void>;

触发 Tab 键事件。这是 userEvent.keyboard('{tab}') 的简写。

ts
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();
});

参考:

  • Playwright Keyboard API
  • WebdriverIO action('key') API
  • testing-library tab API

userEvent.type ​

ts
function type(
  element: Element | Locator,
  text: string,
  options?: UserEventTypeOptions
): Promise<void>;

WARNING

如果您不依赖特殊字符(例如,{shift} 或 {selectall}),建议使用 userEvent.fill 以获得更好的性能。

type 方法实现了 @testing-library/user-event 的 type 工具,该工具基于 keyboard API 构建。

此函数允许您将字符输入到 input/textarea/contenteditable 元素中。它支持 user-event keyboard 语法。

如果您只需要按下字符而不需要在输入框中输入,请使用 userEvent.keyboard API。

ts
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,因为它速度更快。

参考:

  • Playwright locator.press API
  • WebdriverIO action('key') API
  • testing-library type API

userEvent.clear ​

ts
function clear(
  element: Element | Locator,
  options?: UserEventClearOptions
): Promise<void>;

该方法用于清除输入元素的内容。

ts
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('');
});

参考:

  • Playwright locator.clear API
  • WebdriverIO element.clearValue API
  • testing-library clear API

userEvent.selectOptions ​

ts
function selectOptions(
  element: Element | Locator,
  values: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
  options?: UserEventSelectOptions
): Promise<void>;

userEvent.selectOptions 可用于在 <select> 元素中选择值。

WARNING

如果 select 元素没有 multiple 属性,Vitest 将只选择传入 values 数组中的第一个元素。

与 @testing-library 不同,Vitest 目前不支持 listbox,但我们计划在未来添加对其的支持。

ts
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 ​

ts
function hover(
  element: Element | Locator,
  options?: UserEventHoverOptions
): Promise<void>;

此方法会将光标移动到指定元素的位置。有关此方法如何工作的详细说明,请参阅您的提供者文档。

WARNING

如果您使用的是 webdriverio,光标将默认移动到元素的中心。

如果您使用的是 playwright,光标会移动到元素的“某个”可见点。

ts
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();
});

参考:

  • Playwright locator.hover API
  • WebdriverIO element.moveTo API
  • testing-library hover API

userEvent.unhover ​

ts
function unhover(
  element: Element | Locator,
  options?: UserEventHoverOptions
): Promise<void>;

此方法的工作方式与 userEvent.hover 相同,但会将光标移动到 document.body 元素。

WARNING

默认情况下,光标位置在 body 元素的“某个”可见位置(对于 playwright)或中心(对于 webdriverio)。因此,如果当前悬停的元素已经处于相同位置,此方法将不起作用。

ts
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();
});

参考:

  • Playwright locator.hover API
  • WebdriverIO element.moveTo API
  • testing-library hover API

userEvent.upload ​

ts
function upload(
  element: Element | Locator,
  files: string[] | string | File[] | File,
  options?: UserEventUploadOptions
): Promise<void>;

更改文件输入元素,使其包含指定的文件。

ts
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 浏览器中的此命令。它也只支持字符串类型。

参考:

  • Playwright locator.setInputFiles API
  • WebdriverIO browser.uploadFile API
  • testing-library upload API

userEvent.dragAndDrop ​

ts
function dragAndDrop(
  source: Element | Locator,
  target: Element | Locator,
  options?: UserEventDragAndDropOptions
): Promise<void>;

将源元素拖动到目标元素上方。请注意,source 元素必须将 draggable 属性设置为 true。

ts
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

默认的 preview 不支持此 API。

参考:

  • Playwright frame.dragAndDrop API
  • WebdriverIO element.dragAndDrop API

userEvent.copy ​

ts
function copy(): Promise<void>;

将已选中的文本复制到剪贴板。

js
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');
});

参考:

  • testing-library copy API

userEvent.cut ​

ts
function cut(): Promise<void>;

将已选中的文本剪切到剪贴板。

js
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');
});

参考:

  • testing-library cut API

userEvent.paste ​

ts
function paste(): Promise<void>;

从剪贴板粘贴文本。有关使用示例,请参阅 userEvent.copy 和 userEvent.cut。

参考:

  • testing-library paste API
Pager
上一页Context API
下一页定位器

基于 MIT 许可证 发布。

版权所有 (c) 2021-Present Vitest Team

https://vitest.dev/guide/browser/interactivity-api

基于 MIT 许可证 发布。

版权所有 (c) 2021-Present Vitest Team