로케이터
로케이터는 단일 요소 또는 여러 요소를 나타내는 개념입니다. 모든 로케이터는 '셀렉터'라고 불리는 문자열로 정의됩니다. Vitest는 이러한 셀렉터를 내부적으로 생성하는 편리한 메서드를 제공하여 추상화합니다.
로케이터 API는 Playwright의 로케이터를 포크(fork)한 Ivya를 사용합니다. 그러나 Vitest는 이 API를 Playwright뿐만 아니라 모든 프로바이더에서도 제공합니다.
TIP
이 페이지에서는 API 사용법을 다룹니다. 로케이터와 그 사용법을 더 잘 이해하려면 Playwright의 "로케이터" 문서를 읽어보세요.
getByRole
function getByRole(
role: ARIARole | string,
options?: LocatorByRoleOptions
): Locator;
ARIA 역할, ARIA 속성 및 접근성 이름을 사용하여 요소를 찾을 수 있는 로케이터를 생성합니다.
TIP
getByText('The name')
으로 단일 요소를 쿼리할 때, getByRole(expectedRole, { name: 'The name' })
을 사용하는 것이 더 나은 경우가 많습니다. 접근성 이름 쿼리는 *ByAltText
또는 *ByTitle
과 같은 다른 쿼리를 대체하지 않습니다. 접근성 이름이 이러한 속성과 동일할 수 있지만, 이 속성들의 기능을 대체하지는 않습니다.
다음 DOM 구조를 고려해 보세요.
<h3>Sign up</h3>
<label>
Login
<input type="text" />
</label>
<label>
Password
<input type="password" />
</label>
<br />
<button>Submit</button>
각 요소를 암시적 역할로 찾을 수 있습니다.
await expect
.element(page.getByRole('heading', { name: 'Sign up' }))
.toBeVisible();
await page.getByRole('textbox', { name: 'Login' }).fill('admin');
await page.getByRole('textbox', { name: 'Password' }).fill('admin');
await page.getByRole('button', { name: /submit/i }).click();
WARNING
역할은 ARIA 역할 계층을 상속하지 않고 문자열 일치로 매칭됩니다. 결과적으로 checkbox
와 같은 상위 역할(superclass role)을 쿼리해도 switch
와 같은 하위 역할(subclass role)을 가진 요소는 포함되지 않습니다.
기본적으로 HTML의 많은 시맨틱 요소는 역할을 가지고 있습니다. 예를 들어, <input type="radio">
는 "radio" 역할을 가집니다. HTML의 비시맨틱 요소는 역할을 가지지 않습니다. <div>
와 <span>
은 추가된 시맨틱이 없으면 null
을 반환합니다. role
속성은 시맨틱을 제공할 수 있습니다.
ARIA 가이드라인은 이미 암시적 역할을 가진 내장 요소에 role
또는 aria-*
속성을 통해 역할을 제공하는 것을 강력히 권장하지 않습니다.
옵션
exact: boolean
name
이 정확히 일치하는지 여부: 대소문자 구분 및 전체 문자열 일치. 기본적으로 비활성화되어 있습니다.name
이 정규 표현식인 경우 이 옵션은 무시됩니다. 정확히 일치해도 공백은 제거됩니다.tsx<button>Hello World</button>; page.getByRole('button', { name: 'hello world' }); // ✅ page.getByRole('button', { name: 'hello world', exact: true }); // ❌ page.getByRole('button', { name: 'Hello World', exact: true }); // ✅
checked: boolean
체크된 요소(
aria-checked
또는<input type="checkbox"/>
로 설정된)를 포함할지 여부. 기본적으로 필터는 적용되지 않습니다.자세한 내용은
aria-checked
를 참조하세요.tsx<> <button role="checkbox" aria-checked="true" /> <input type="checkbox" checked /> </>; page.getByRole('checkbox', { checked: true }); // ✅ page.getByRole('checkbox', { checked: false }); // ❌
disabled: boolean
비활성 상태인 요소를 포함할지 여부. 기본적으로 필터는 적용되지 않습니다. 다른 속성과 달리
disabled
상태는 상속됩니다.자세한 내용은
aria-disabled
를 참조하세요.tsx<input type="text" disabled />; page.getByRole('textbox', { disabled: true }); // ✅ page.getByRole('textbox', { disabled: false }); // ❌
expanded: boolean
확장된 요소를 포함할지 여부. 기본적으로 필터는 적용되지 않습니다.
자세한 내용은
aria-expanded
를 참조하세요.tsx<a aria-expanded="true" href="example.com"> Link </a>; page.getByRole('link', { expanded: true }); // ✅ page.getByRole('link', { expanded: false }); // ❌
includeHidden: boolean
접근성 트리에서 일반적으로 제외되는 요소를 쿼리할지 여부. 기본적으로 숨김 처리되지 않은 요소만 역할 셀렉터에 의해 일치됩니다.
none
및presentation
역할은 항상 포함됩니다.tsx<button style="display: none" />; page.getByRole('button'); // ❌ page.getByRole('button', { includeHidden: false }); // ❌ page.getByRole('button', { includeHidden: true }); // ✅
level: number
heading
,listitem
,row
,treeitem
역할에 대해 일반적으로 존재하는 숫자 속성으로,<h1>-<h6>
요소에 대한 기본값이 있습니다. 기본적으로 필터는 적용되지 않습니다.자세한 내용은
aria-level
을 참조하세요.tsx<> <h1>Heading Level One</h1> <div role="heading" aria-level="1"> Second Heading Level One </div> </>; page.getByRole('heading', { level: 1 }); // ✅ page.getByRole('heading', { level: 2 }); // ❌
name: string | RegExp
접근성 이름. 기본적으로 일치는 대소문자를 구분하지 않으며 부분 문자열과 일치합니다. 이 동작을 제어하려면
exact
옵션을 사용하세요.tsx<button>Click Me!</button>; page.getByRole('button', { name: 'Click Me!' }); // ✅ page.getByRole('button', { name: 'click me!' }); // ✅ page.getByRole('button', { name: 'Click Me?' }); // ❌
pressed: boolean
눌린 요소를 포함할지 여부. 기본적으로 필터는 적용되지 않습니다.
자세한 내용은
aria-pressed
를 참조하세요.tsx<button aria-pressed="true">👍</button>; page.getByRole('button', { pressed: true }); // ✅ page.getByRole('button', { pressed: false }); // ❌
selected: boolean
선택된 요소를 포함할지 여부. 기본적으로 필터는 적용되지 않습니다.
자세한 내용은
aria-selected
를 참조하세요.tsx<button role="tab" aria-selected="true"> Vue </button>; page.getByRole('button', { selected: true }); // ✅ page.getByRole('button', { selected: false }); // ❌
참고
getByAltText
function getByAltText(text: string | RegExp, options?: LocatorOptions): Locator;
텍스트와 일치하는 alt
속성을 가진 요소를 찾을 수 있는 로케이터를 생성합니다. testing-library의 구현과 달리 Vitest는 일치하는 alt
속성을 가진 모든 요소를 찾습니다.
<img alt="Incredibles 2 Poster" src="/incredibles-2.png" />;
page.getByAltText(/incredibles.*? poster/i); // ✅
page.getByAltText('non existing alt text'); // ❌
옵션
exact: boolean
text
가 정확히 일치하는지 여부: 대소문자 구분 및 전체 문자열 일치. 기본적으로 비활성화되어 있습니다.text
가 정규 표현식인 경우 이 옵션은 무시됩니다. 정확히 일치해도 공백은 제거됩니다.
참고
getByLabelText
function getByLabelText(
text: string | RegExp,
options?: LocatorOptions
): Locator;
연결된 레이블을 가진 요소를 찾을 수 있는 로케이터를 생성합니다.
page.getByLabelText('Username')
로케이터는 아래 예시의 모든 input 요소를 찾습니다.
// label과 form 요소 id 간의 for/htmlFor 관계
<label for="username-input">Username</label>
<input id="username-input" />
// form 요소와 함께 사용되는 aria-labelledby 속성
<label id="username-label">Username</label>
<input aria-labelledby="username-label" />
// 래퍼 레이블
<label>Username <input /></label>
// 레이블 텍스트가 다른 자식 요소 내에 있는 래퍼 레이블
<label>
<span>Username</span>
<input />
</label>
// aria-label 속성
// 사용자가 페이지에서 볼 수 있는 레이블이 아니므로, 시각적 사용자에게 입력의 목적이 명확해야 합니다.
<input aria-label="Username" />
옵션
exact: boolean
text
가 정확히 일치하는지 여부: 대소문자 구분 및 전체 문자열 일치. 기본적으로 비활성화되어 있습니다.text
가 정규 표현식인 경우 이 옵션은 무시됩니다. 정확히 일치해도 공백은 제거됩니다.
참고
getByPlaceholder
function getByPlaceholder(
text: string | RegExp,
options?: LocatorOptions
): Locator;
지정된 placeholder
속성을 가진 요소를 찾을 수 있는 로케이터를 생성합니다. Vitest는 input
뿐만 아니라 일치하는 placeholder
속성을 가진 모든 요소를 찾습니다.
<input placeholder="Username" />;
page.getByPlaceholder('Username'); // ✅
page.getByPlaceholder('not found'); // ❌
WARNING
일반적으로 플레이스홀더(placeholder)보다는 getByLabelText
를 사용하여 레이블에 의존하는 것이 더 좋습니다.
옵션
exact: boolean
text
가 정확히 일치하는지 여부: 대소문자 구분 및 전체 문자열 일치. 기본적으로 비활성화되어 있습니다.text
가 정규 표현식인 경우 이 옵션은 무시됩니다. 정확히 일치해도 공백은 제거됩니다.
참고
getByText
function getByText(text: string | RegExp, options?: LocatorOptions): Locator;
지정된 텍스트를 포함하는 요소를 찾을 수 있는 로케이터를 생성합니다. 텍스트는 TextNode의 nodeValue
또는 타입이 button
이나 reset
인 경우 input의 값과 일치됩니다. 텍스트 일치는 정확히 일치하는 경우에도 항상 공백을 표준화합니다. 예를 들어, 여러 공백을 하나로 바꾸고, 개행을 공백으로 바꾸며, 선행 및 후행 공백을 무시합니다.
<a href="/about">About ℹ️</a>;
page.getByText(/about/i); // ✅
page.getByText('about', { exact: true }); // ❌
TIP
이 로케이터는 비대화형 요소를 찾는 데 유용합니다. 버튼이나 입력과 같은 인터랙티브 요소를 찾아야 하는 경우 getByRole
을 선호하세요.
옵션
exact: boolean
text
가 정확히 일치하는지 여부: 대소문자 구분 및 전체 문자열 일치. 기본적으로 비활성화되어 있습니다.text
가 정규 표현식인 경우 이 옵션은 무시됩니다. 정확히 일치해도 공백은 제거됩니다.
참고
getByTitle
function getByTitle(text: string | RegExp, options?: LocatorOptions): Locator;
지정된 title
속성을 가진 요소를 찾을 수 있는 로케이터를 생성합니다. testing-library의 getByTitle
과 달리 Vitest는 SVG 내의 title
요소를 찾지 못합니다.
<span title="Delete" id="2"></span>;
page.getByTitle('Delete'); // ✅
page.getByTitle('Create'); // ❌
옵션
exact: boolean
text
가 정확히 일치하는지 여부: 대소문자 구분 및 전체 문자열 일치. 기본적으로 비활성화되어 있습니다.text
가 정규 표현식인 경우 이 옵션은 무시됩니다. 정확히 일치해도 공백은 제거됩니다.
참고
getByTestId
function getByTestId(text: string | RegExp): Locator;
지정된 테스트 ID 속성과 일치하는 요소를 찾을 수 있는 로케이터를 생성합니다. browser.locators.testIdAttribute
를 사용하여 속성 이름을 구성할 수 있습니다.
<div data-testid="custom-element" />;
page.getByTestId('custom-element'); // ✅
page.getByTestId('non-existing-element'); // ❌
WARNING
다른 로케이터가 사용 사례에 적합하지 않을 때만 이 방법을 사용하는 것이 좋습니다. data-testid
속성을 사용하는 것은 소프트웨어가 실제로 사용되는 방식과 유사하지 않으므로 가능하면 피해야 합니다.
옵션
exact: boolean
text
가 정확히 일치하는지 여부: 대소문자 구분 및 전체 문자열 일치. 기본적으로 비활성화되어 있습니다.text
가 정규 표현식인 경우 이 옵션은 무시됩니다. 정확히 일치해도 공백은 제거됩니다.
참고
nth
function nth(index: number): Locator;
이 메서드는 다중 요소 쿼리 결과 내에서 특정 인덱스만 일치하는 새 로케이터를 반환합니다. 0부터 시작하며, nth(0)
은 첫 번째 요소를 선택합니다. elements()[n]
과 달리 nth
로케이터는 요소가 나타날 때까지 재시도(retry)됩니다.
<div aria-label="one"><input /><input /><input /></div>
<div aria-label="two"><input /></div>
page.getByRole('textbox').nth(0); // ✅
page.getByRole('textbox').nth(4); // ❌
TIP
nth
를 사용하기 전에 검색 범위를 좁히기 위해 체인 로케이터를 사용하는 것이 유용할 수 있습니다. 때로는 요소 위치로 구분하는 것 외에 더 나은 방법이 없을 수도 있습니다. 이는 불안정할 수 있지만, 없는 것보다는 낫습니다.
page.getByLabel('two').getByRole('input'); // ✅ page.getByRole('textbox').nth(3)보다 더 나은 대안
page.getByLabel('one').getByRole('input'); // ❌ 너무 모호함
page.getByLabel('one').getByRole('input').nth(1); // ✅ 실용적인 타협
first
function first(): Locator;
이 메서드는 다중 요소 쿼리 결과의 첫 번째 인덱스만 일치하는 새 로케이터를 반환합니다. nth(0)
의 줄임 표현입니다.
<input /> <input /> <input />
page.getByRole('textbox').first(); // ✅
last
function last(): Locator;
이 메서드는 다중 요소 쿼리 결과의 마지막 인덱스만 일치하는 새 로케이터를 반환합니다. nth(-1)
의 줄임 표현입니다.
<input /> <input /> <input />
page.getByRole('textbox').last(); // ✅
and
function and(locator: Locator): Locator;
이 메서드는 부모 로케이터와 제공된 로케이터 모두와 일치하는 새 로케이터를 생성합니다. 다음 예시는 특정 제목을 가진 버튼을 찾습니다.
page.getByRole('button').and(page.getByTitle('Subscribe'));
or
function or(locator: Locator): Locator;
이 메서드는 두 로케이터 중 하나 또는 둘 다와 일치하는 새 로케이터를 생성합니다.
WARNING
로케이터가 단일 요소 이상을 일치시키는 경우, 단일 요소를 예상하는 다른 메서드를 호출하면 오류가 발생할 수 있습니다.
<>
<button>Click me</button>
<a href="https://vitest.dev">Error happened!</a>
</>;
page.getByRole('button').or(page.getByRole('link')).click(); // ❌ 여러 요소와 일치
filter
function filter(options: LocatorOptions): Locator;
이 메서드는 텍스트로 필터링하는 등의 옵션에 따라 로케이터를 좁힙니다. 여러 필터를 적용하기 위해 체인으로 연결할 수 있습니다.
has
- 유형:
Locator
이 옵션은 제공된 로케이터와 일치하는 다른 요소를 포함하는 요소만 찾도록 셀렉터를 좁힙니다. 예를 들어, 다음 HTML에서:
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Vitest
텍스트를 포함하는 article
만 찾도록 로케이터를 좁힐 수 있습니다.
page.getByRole('article').filter({ has: page.getByText('Vitest') }); // ✅
WARNING
제공된 로케이터(예시에서 page.getByText('Vitest')
)는 부모 로케이터(예시에서 page.getByRole('article')
)에 상대적이어야 합니다. 문서 루트가 아닌 부모 로케이터를 기준으로 쿼리됩니다.
즉, 부모 로케이터 외부의 요소를 쿼리하는 로케이터를 전달할 수 없습니다.
page.getByText('Vitest').filter({ has: page.getByRole('article') }); // ❌
이 예시는 article
요소가 Vitest
텍스트를 가진 요소 외부에 있기 때문에 실패합니다.
TIP
이 메서드는 요소를 더 좁히기 위해 체인으로 연결할 수 있습니다.
page
.getByRole('article')
.filter({ has: page.getByRole('button', { name: 'delete row' }) })
.filter({ has: page.getByText('Vitest') });
hasNot
- 유형:
Locator
이 옵션은 제공된 로케이터와 일치하는 다른 요소를 포함하지 않는 요소만 찾도록 셀렉터를 좁힙니다. 예를 들어, 다음 HTML에서:
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Rolldown
을 포함하지 않는 article
만 찾도록 로케이터를 좁힐 수 있습니다.
page.getByRole('article').filter({ hasNot: page.getByText('Rolldown') }); // ✅
page.getByRole('article').filter({ hasNot: page.getByText('Vitest') }); // ❌
WARNING
제공된 로케이터는 has
옵션과 마찬가지로 문서 루트가 아닌 부모에 대해 쿼리됩니다.
hasText
- 유형:
string | RegExp
이 옵션은 셀렉터를 제공된 텍스트를 어딘가에 포함하는 요소만 찾도록 좁힙니다. string
이 전달되면 일치는 대소문자를 구분하지 않으며 부분 문자열을 검색합니다.
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
검색이 대소문자를 구분하지 않으므로 두 로케이터 모두 동일한 요소를 찾습니다.
page.getByRole('article').filter({ hasText: 'Vitest' }); // ✅
page.getByRole('article').filter({ hasText: 'Vite' }); // ✅
hasNotText
- 유형:
string | RegExp
이 옵션은 셀렉터를 제공된 텍스트를 어딘가에 포함하지 않는 요소만 일치하도록 좁힙니다. string
이 전달되면 일치는 대소문자를 구분하지 않으며 부분 문자열을 검색합니다.
메서드
모든 메서드는 비동기이며 await
해야 합니다. Vitest 3부터는 메서드가 await
되지 않으면 테스트가 실패합니다.
click
function click(options?: UserEventClickOptions): Promise<void>;
요소를 클릭합니다. 옵션을 사용하여 마우스 커서 위치를 설정할 수 있습니다.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).click();
dblClick
function dblClick(options?: UserEventDoubleClickOptions): Promise<void>;
요소에 더블 클릭 이벤트를 트리거합니다. 옵션을 사용하여 마우스 커서 위치를 설정할 수 있습니다.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).dblClick();
tripleClick
function tripleClick(options?: UserEventTripleClickOptions): Promise<void>;
요소에 트리플 클릭 이벤트를 트리거합니다. 브라우저 API에는 tripleclick
이 없으므로 이 메서드는 세 번의 클릭 이벤트를 연속으로 발생시킵니다.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).tripleClick();
clear
function clear(options?: UserEventClearOptions): Promise<void>;
입력 요소의 내용을 지웁니다.
import { page } from '@vitest/browser/context';
await page.getByRole('textbox', { name: 'Full Name' }).clear();
hover
function hover(options?: UserEventHoverOptions): Promise<void>;
마우스 커서 위치를 선택된 요소로 이동합니다.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).hover();
unhover
function unhover(options?: UserEventHoverOptions): Promise<void>;
이것은 locator.hover
와 동일하게 작동하지만, 커서를 document.body
요소로 이동합니다.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).unhover();
fill
function fill(text: string, options?: UserEventFillOptions): Promise<void>;
현재 input
, textarea
또는 contenteditable
요소의 값을 설정합니다.
import { page } from '@vitest/browser/context';
await page.getByRole('input', { name: 'Full Name' }).fill('Mr. Bean');
dropTo
function dropTo(
target: Locator,
options?: UserEventDragAndDropOptions
): Promise<void>;
현재 요소를 대상 위치로 드래그 앤 드롭합니다.
import { page } from '@vitest/browser/context';
const paris = page.getByText('Paris');
const france = page.getByText('France');
await paris.dropTo(france);
selectOptions
function selectOptions(
values: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[],
options?: UserEventSelectOptions
): Promise<void>;
<select>
요소에서 하나 이상의 값을 선택합니다.
import { page } from '@vitest/browser/context';
const languages = page.getByRole('select', { name: 'Languages' });
await languages.selectOptions('EN');
await languages.selectOptions(['ES', 'FR']);
await languages.selectOptions([
languages.getByRole('option', { name: 'Spanish' }),
languages.getByRole('option', { name: 'French' }),
]);
screenshot
function screenshot(
options: LocatorScreenshotOptions & { save: false }
): Promise<string>;
function screenshot(
options: LocatorScreenshotOptions & { base64: true }
): Promise<{
path: string;
base64: string;
}>;
function screenshot(
options?: LocatorScreenshotOptions & { base64?: false }
): Promise<string>;
로케이터의 셀렉터와 일치하는 요소의 스크린샷을 생성합니다.
path
옵션을 사용하여 스크린샷 저장 위치를 지정할 수 있으며, 이는 현재 테스트 파일에 상대적입니다. path
옵션이 설정되지 않은 경우, Vitest는 기본적으로 browser.screenshotDirectory
(__screenshot__
이 기본값)와 파일 및 테스트 이름을 사용하여 스크린샷의 파일 경로를 결정합니다.
스크린샷의 내용도 필요한 경우, base64: true
를 지정하여 스크린샷이 저장된 파일 경로와 함께 반환받을 수 있습니다.
import { page } from '@vitest/browser/context';
const button = page.getByRole('button', { name: 'Click Me!' });
const path = await button.screenshot();
const { path, base64 } = await button.screenshot({
path: './button-click-me.png',
base64: true, // base64 문자열도 반환
});
// path - 스크린샷의 전체 경로
// bas64 - 스크린샷의 base64 인코딩 문자열
경고 3.2.0+
save
가 false
로 설정된 경우 screenshot
은 항상 base64 문자열을 반환합니다. 이때 path
는 무시됩니다.
query
function query(): Element | null;
이 메서드는 로케이터의 셀렉터와 일치하는 단일 요소를 반환하며, 요소가 없으면 null
을 반환합니다.
여러 요소가 셀렉터와 일치하는 경우, 이 메서드는 오류를 발생시킵니다. 일치하는 모든 DOM 요소를 필요로 한다면 .elements()
를 사용하고, 셀렉터와 일치하는 로케이터 배열이 필요하다면 .all()
을 사용하세요.
다음 DOM 구조를 고려해 보세요.
<div>Hello <span>World</span></div>
<div>Hello</div>
이 로케이터는 오류를 발생시키지 않습니다.
page.getByText('Hello World').query(); // ✅ HTMLDivElement
page.getByText('Hello Germany').query(); // ✅ null
page.getByText('World').query(); // ✅ HTMLSpanElement
page.getByText('Hello', { exact: true }).query(); // ✅ HTMLSpanElement
이 로케이터는 오류를 발생시킵니다.
// 여러 요소를 반환
page.getByText('Hello').query(); // ❌
page.getByText(/^Hello/).query(); // ❌
element
function element(): Element;
이 메서드는 로케이터의 셀렉터와 일치하는 단일 요소를 반환합니다.
셀렉터와 일치하는 요소가 없는 경우 오류가 발생합니다. 요소가 존재하는지 확인하기만 하면 되는 경우 .query()
를 사용하는 것을 고려하세요.
셀렉터와 일치하는 _여러 요소_가 있는 경우 오류가 발생합니다. 일치하는 모든 DOM 요소를 필요로 한다면 .elements()
를 사용하고, 셀렉터와 일치하는 로케이터 배열이 필요하다면 .all()
을 사용하세요.
TIP
이 메서드는 외부 라이브러리에 전달해야 할 때 유용할 수 있습니다. 로케이터가 expect.element
와 함께 사용될 때마다 어설션이 재시도(retry)될 때 자동으로 호출됩니다.
await expect.element(page.getByRole('button')).toBeDisabled();
다음 DOM 구조를 고려해 보세요.
<div>Hello <span>World</span></div>
<div>Hello Germany</div>
<div>Hello</div>
이 로케이터는 오류를 발생시키지 않습니다.
page.getByText('Hello World').element(); // ✅
page.getByText('Hello Germany').element(); // ✅
page.getByText('World').element(); // ✅
page.getByText('Hello', { exact: true }).element(); // ✅
이 로케이터는 오류를 발생시킵니다.
// 여러 요소를 반환
page.getByText('Hello').element(); // ❌
page.getByText(/^Hello/).element(); // ❌
// 요소를 반환하지 않음
page.getByText('Hello USA').element(); // ❌
elements
function elements(): Element[];
이 메서드는 로케이터의 셀렉터와 일치하는 요소 배열을 반환합니다.
이 함수는 절대 오류를 발생시키지 않습니다. 셀렉터와 일치하는 요소가 없으면 이 메서드는 빈 배열을 반환합니다.
다음 DOM 구조를 고려해 보세요.
<div>Hello <span>World</span></div>
<div>Hello</div>
이 로케이터는 항상 성공합니다.
page.getByText('Hello World').elements(); // ✅ [HTMLElement]
page.getByText('World').elements(); // ✅ [HTMLElement]
page.getByText('Hello', { exact: true }).elements(); // ✅ [HTMLElement]
page.getByText('Hello').element(); // ✅ [HTMLElement, HTMLElement]
page.getByText('Hello USA').elements(); // ✅ []
all
function all(): Locator[];
이 메서드는 셀렉터와 일치하는 새 로케이터 배열을 반환합니다.
내부적으로 이 메서드는 .elements
를 호출하고 page.elementLocator
를 사용하여 모든 요소를 래핑합니다.
속성
selector
selector
는 브라우저 프로바이더가 요소를 찾는 데 사용하는 문자열입니다. Playwright는 playwright
로케이터 구문을 사용하며, preview
및 webdriverio
는 CSS를 사용합니다.
DANGER
이 문자열은 테스트 코드에서 직접 사용해서는 안 됩니다. selector
문자열은 Commands API와 함께 사용할 때만 사용해야 합니다.
import type { BrowserCommand } from 'vitest/node';
const test: BrowserCommand<string> = function test(context, selector) {
// playwright
await context.iframe.locator(selector).click();
// webdriverio
await context.browser.$(selector).click();
};
import { test } from 'vitest';
import { commands, page } from '@vitest/browser/context';
test('works correctly', async () => {
await commands.test(page.getByText('Hello').selector); // ✅
// Vitest는 자동으로 문자열로 언래핑(unwrap)합니다.
await commands.test(page.getByText('Hello')); // ✅
});
사용자 정의 로케이터 3.2.0+ 고급
로케이터 팩토리(factory) 객체를 정의하여 기본 제공 로케이터 API를 확장할 수 있습니다. 이 메서드들은 page
객체와 생성된 모든 로케이터의 메서드로 존재합니다.
이러한 로케이터는 기본 제공 로케이터만으로는 충분하지 않을 때 유용할 수 있습니다. 예를 들어, UI에 사용자 정의 프레임워크를 사용하는 경우에 그렇습니다.
로케이터 팩토리는 셀렉터 문자열 또는 로케이터 자체를 반환해야 합니다.
TIP
셀렉터 구문은 Playwright 로케이터와 동일합니다. 자세한 내용은 해당 가이드를 읽어보세요.
import { locators } from '@vitest/browser/context';
locators.extend({
getByArticleTitle(title) {
return `[data-title="${title}"]`;
},
getByArticleCommentsCount(count) {
return `.comments :text("${count} comments")`;
},
async previewComments() {
// "this"를 통해 현재 로케이터에 접근할 수 있습니다.
// 메서드가 `page`에서 호출된 경우, `this`는 로케이터가 아닌 `page`가 됩니다!
if (this !== page) {
await this.click();
}
// ...
},
});
// TypeScript를 사용하는 경우, LocatorSelectors 인터페이스를 확장하여
// locators.extend, page.* 및 locator.* 메서드에서 자동 완성을 사용할 수 있습니다.
declare module '@vitest/browser/context' {
interface LocatorSelectors {
// 사용자 정의 메서드가 문자열을 반환하면 로케이터로 변환됩니다.
// 다른 것을 반환하면 평소와 같이 반환됩니다.
getByArticleTitle(title: string): Locator;
getByArticleCommentsCount(count: number): Locator;
// Vitest는 프로미스(Promise)를 반환하며 로케이터로 변환을 시도하지 않습니다.
previewComments(this: Locator): Promise<void>;
}
}
메서드가 전역 page
객체에서 호출되면 셀렉터는 전체 페이지에 적용됩니다. 아래 예시에서 getByArticleTitle
은 data-title
속성 값이 title
인 모든 요소를 찾습니다. 하지만 메서드가 로케이터에서 호출되면 해당 로케이터로 범위가 지정됩니다.
<article data-title="Hello, World!">
Hello, World!
<button id="comments">2 comments</button>
</article>
<article data-title="Hello, Vitest!">
Hello, Vitest!
<button id="comments">0 comments</button>
</article>
const articles = page.getByRole('article');
const worldArticle = page.getByArticleTitle('Hello, World!'); // ✅
const commentsElement = worldArticle.getByArticleCommentsCount(2); // ✅
const wrongCommentsElement = worldArticle.getByArticleCommentsCount(0); // ❌
const wrongElement = page.getByArticleTitle('No Article!'); // ❌
await commentsElement.previewComments(); // ✅
await wrongCommentsElement.previewComments(); // ❌