インタラクティビティ API
Vitest は、イベントを偽装する代わりに Chrome DevTools Protocol または webdriver を使用して、@testing-library/user-event
API のサブセットを実装しています。これにより、ブラウザの動作はより信頼性が高く、ユーザーがページと操作する方法と一貫性のあるものになります。
import { userEvent } from '@vitest/browser/context';
await userEvent.click(document.querySelector('.button'));
ほとんどすべての userEvent
メソッドは、そのプロバイダーのオプションを継承します。IDE で利用可能な全オプションを確認するには、セットアップファイルまたは設定ファイル(tsconfig.json
の included
の内容に応じて異なります)に webdriver
または playwright
の型(プロバイダーによって異なります)を追加します。
/// <reference types="@vitest/browser/providers/playwright" />
/// <reference types="@vitest/browser/providers/webdriverio" />
userEvent.setup
function setup(): UserEvent;
新しいユーザーイベントのインスタンスを作成します。これは、キーボードの状態を保持し、ボタンの押下と解放を正確に制御する必要がある場合に便利です。
WARNING
@testing-library/user-event
とは異なり、@vitest/browser/context
からのデフォルトの userEvent
インスタンスは、メソッドが呼び出されるたびにではなく、一度だけ作成されます。このスニペットでその動作の違いを確認できます。
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
が存在しないため、このメソッドは3つのクリックイベントを連続して発生させます。そのため、イベントをフィルタリングするには、クリックイベントの詳細(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:clickCount: 3
を指定したclick
で実装されます。 - WebdriverIO
browser.action
API:move
と3つの連続したdown + up + pause
イベントを含むアクション API を用いて実装されます。 - 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
を使用するよりも高速ですが、user-event keyboard
構文 (例: {Shift}{selectall}
) はサポートされていません。
特殊文字の入力やキー押下イベントの細かな制御が不要な場合は、userEvent.type
よりもこの API を使用することをお勧めします。
参照:
userEvent.keyboard
function keyboard(text: string): Promise<void>;
userEvent.keyboard
を使用すると、キーボードストロークをトリガーできます。入力にフォーカスがある場合、その入力に文字が入力されます。それ以外の場合は、現在フォーカスされている要素(フォーカスされている要素がない場合は document.body
)に対してキーボードイベントがトリガーされます。
この API は user-event keyboard
構文をサポートしています。
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 回キーダウンイベントをトリガー
await userEvent.keyboard('{a>5/}'); // a を 5 回キーダウンした後、離す
});
参照:
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
メソッドは、keyboard
API の上に構築された @testing-library/user-event
の type
ユーティリティを実装しています。
この関数を使用すると、input
/textarea
/contenteditable
要素に文字を入力できます。これは user-event keyboard
構文をサポートしています。
入力なしで文字を押すだけの場合は、userEvent.keyboard
API を使用してください。
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 は、input.type
のようにロケーターに .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
プロバイダーの場合)にあるため、現在ホバーされている要素がすでに同じ位置にある場合、このメソッドは効果を発揮しません。
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>;
ソース要素をターゲット要素の上にドラッグします。source
要素に 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
を参照してください。
参照: