Skip to content
Vitest 2
Main Navigation 指南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

Why Browser Mode?

Getting Started

Context API

Interactivity API

Locators

Assertion API

Commands 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 方法都支持其提供者(provider)的选项。要在 IDE 中查看所有可用选项,请根据您使用的提供者,将 webdriver 或 playwright 类型添加到您的 tsconfig.json 文件中:

json
{
  "compilerOptions": {
    "types": ["@vitest/browser/providers/playwright"]
  }
}
json
{
  "compilerOptions": {
    "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>;

对一个元素进行点击。支持提供者的选项。有关此方法工作原理的详细说明,请参阅您的提供者文档。

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): 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 将只选择数组中的第一个元素。

与 @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
): 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
Pager
上一页Context API
下一页Locators

基于 MIT 许可证 发布。

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

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

基于 MIT 许可证 发布。

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