상호작용 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
파일에 webdriver
또는 playwright
유형(제공자에 따라 다름)을 추가하세요.
{
"compilerOptions": {
"types": ["@vitest/browser/providers/playwright"]
}
}
{
"compilerOptions": {
"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
이 없으므로 이 메서드는 세 번의 클릭 이벤트를 연속으로 발생시킵니다. 따라서 이벤트를 필터링하려면 클릭 이벤트 상세 정보에서 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
와 세 번의down + up + pause
이벤트를 연속으로 사용하여 액션 API를 통해 구현됩니다. - testing-library
tripleClick
API
userEvent.fill
function fill(element: Element | Locator, text: string): Promise<void>;
input/textarea/conteneditable
필드에 값을 설정합니다. 이렇게 하면 새 값을 설정하기 전에 입력 필드의 기존 텍스트가 모두 제거됩니다.
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번의 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
메서드는 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): 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
기본적으로 커서 위치는 body 요소의 "어떤" 보이는 위치(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
): 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
제공자에서 지원되지 않습니다.
참고 자료: