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 實作了 @testing-library/user-event API 的子集。它透過使用 Chrome 開發者工具協定 (Chrome DevTools Protocol) 或 webdriver,而非模擬事件,使得瀏覽器行為更為可靠,並與使用者在頁面上的實際互動方式保持一致。

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>;

點擊一個元素。此方法繼承提供者的選項。有關此方法的詳細運作說明,請參閱您的提供者文件。

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 事件,此方法將連續觸發三次點擊事件。因此,您必須檢查 點擊事件詳細資訊 以過濾事件: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 將只選擇陣列中的第一個元素。

與 @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