Lokatory
Lokator to reprezentacja pojedynczego elementu lub grupy elementów. Każdy lokator jest definiowany przez ciąg znaków, zwany selektorem. Vitest abstrahuje ten selektor, dostarczając wygodne metody, które generują go automatycznie.
API lokatorów wykorzystuje rozszerzenie lokatorów Playwrighta o nazwie Ivya. Jednak Vitest udostępnia to API wszystkim dostawcom przeglądarek, nie tylko Playwrightowi.
TIP
Ta strona opisuje użycie API lokatorów. Aby lepiej zrozumieć lokatory i ich zastosowanie, zapoznaj się z dokumentacją Playwrighta dotyczącą lokatorów.
getByRole
function getByRole(
role: ARIARole | string,
options?: LocatorByRoleOptions
): Locator;
Tworzy lokator umożliwiający znalezienie elementu na podstawie jego roli ARIA, atrybutów ARIA i nazwy dostępnościowej.
TIP
Jeśli wyszukujesz tylko jeden element za pomocą getByText('Nazwa')
, często lepszym rozwiązaniem jest użycie getByRole(expectedRole, { name: 'Nazwa' })
. Zapytanie o nazwę dostępnościową nie zastępuje innych zapytań, takich jak *ByAltText
czy *ByTitle
. Chociaż nazwa dostępnościowa może być równa tym atrybutom, nie pełni ona tej samej funkcji co te atrybuty.
Rozważ następującą strukturę DOM:
<h3>Sign up</h3>
<label>
Login
<input type="text" />
</label>
<label>
Password
<input type="password" />
</label>
<br />
<button>Submit</button>
Możesz zlokalizować każdy element na podstawie jego niejawnej roli:
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
Role są dopasowywane przez dokładne dopasowanie tekstowe, bez dziedziczenia z hierarchii ról ARIA. W rezultacie, zapytanie o rolę nadrzędną, taką jak checkbox
, nie będzie zawierać elementów z rolą podrzędną, taką jak switch
.
Domyślnie wiele elementów semantycznych w HTML ma przypisaną rolę; na przykład <input type="radio">
ma rolę "radio". Elementy niesemantyczne w HTML nie mają roli; <div>
i <span>
bez dodanych semantyk zwracają null
. Atrybut role
może dostarczyć semantyki.
Dostarczanie ról za pomocą atrybutów role
lub aria-*
do wbudowanych elementów, które już mają niejawną rolę, jest zdecydowanie odradzane przez wytyczne ARIA.
Opcje
exact: boolean
Określa, czy
name
ma być dopasowywane dokładnie: z uwzględnieniem wielkości liter i całego ciągu. Domyślnie wyłączone. Ta opcja jest ignorowana, jeśliname
jest wyrażeniem regularnym. Należy pamiętać, że dokładne dopasowanie nadal przycina białe znaki.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
Określa, czy zaznaczone elementy (ustawione przez
aria-checked
lub<input type="checkbox"/>
) mają być uwzględnione. Domyślnie filtr nie jest stosowany.Szczegółowe informacje znajdziesz w
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
Określa, czy wyłączone elementy mają być uwzględnione. Domyślnie filtr nie jest stosowany. Należy pamiętać, że w przeciwieństwie do innych atrybutów, stan
disabled
jest dziedziczony.Szczegółowe informacje znajdziesz w
aria-disabled
.tsx<input type="text" disabled />; page.getByRole('textbox', { disabled: true }); // ✅ page.getByRole('textbox', { disabled: false }); // ❌
expanded: boolean
Określa, czy rozwinięte elementy mają być uwzględnione. Domyślnie filtr nie jest stosowany.
Szczegółowe informacje znajdziesz w
aria-expanded
.tsx<a aria-expanded="true" href="example.com"> Link </a>; page.getByRole('link', { expanded: true }); // ✅ page.getByRole('link', { expanded: false }); // ❌
includeHidden: boolean
Określa, czy elementy, które są normalnie wykluczone z drzewa dostępności, powinny być wyszukiwane. Domyślnie tylko elementy widoczne są dopasowywane przez selektor roli.
Należy pamiętać, że role
none
ipresentation
są zawsze uwzględniane.tsx<button style="display: none" />; page.getByRole('button'); // ❌ page.getByRole('button', { includeHidden: false }); // ❌ page.getByRole('button', { includeHidden: true }); // ✅
level: number
Atrybut liczbowy, który zazwyczaj występuje dla ról
heading
,listitem
,row
,treeitem
i ma wartości domyślne dla elementów<h1>-<h6>
. Domyślnie filtr nie jest stosowany.Szczegółowe informacje znajdziesz w
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
Nazwa dostępnościowa. Domyślnie dopasowanie jest bez uwzględniania wielkości liter i dopasowuje podciągi. Użyj opcji
exact
, aby kontrolować to zachowanie.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
Określa, czy naciśnięte elementy mają być uwzględnione. Domyślnie filtr nie jest stosowany.
Szczegółowe informacje znajdziesz w
aria-pressed
.tsx<button aria-pressed="true">👍</button>; page.getByRole('button', { pressed: true }); // ✅ page.getByRole('button', { pressed: false }); // ❌
selected: boolean
Określa, czy wybrane elementy mają być uwzględnione. Domyślnie filtr nie jest stosowany.
Szczegółowe informacje znajdziesz w
aria-selected
.tsx<button role="tab" aria-selected="true"> Vue </button>; page.getByRole('button', { selected: true }); // ✅ page.getByRole('button', { selected: false }); // ❌
Zobacz także
getByAltText
function getByAltText(text: string | RegExp, options?: LocatorOptions): Locator;
Tworzy lokator zdolny do znalezienia elementu z atrybutem alt
, który pasuje do podanego tekstu. W przeciwieństwie do implementacji testing-library, Vitest dopasuje każdy element, który ma pasujący atrybut alt
.
<img alt="Incredibles 2 Poster" src="/incredibles-2.png" />;
page.getByAltText(/incredibles.*? poster/i); // ✅
page.getByAltText('non existing alt text'); // ❌
Opcje
exact: boolean
Określa, czy
text
ma być dopasowywany dokładnie: z uwzględnieniem wielkości liter i całego ciągu. Domyślnie wyłączone. Ta opcja jest ignorowana, jeślitext
jest wyrażeniem regularnym. Należy pamiętać, że dokładne dopasowanie nadal przycina białe znaki.
Zobacz także
getByLabelText
function getByLabelText(
text: string | RegExp,
options?: LocatorOptions
): Locator;
Tworzy lokator zdolny do znalezienia elementu, który ma powiązaną etykietę.
Lokator page.getByLabelText('Username')
znajdzie wszystkie pola input w poniższym przykładzie:
// powiązanie for/htmlFor między etykietą a ID elementu formularza
<label for="username-input">Username</label>
<input id="username-input" />
// Atrybut aria-labelledby z elementami formularza
<label id="username-label">Username</label>
<input aria-labelledby="username-label" />
// Etykiety opakowujące
<label>Username <input /></label>
// Etykiety opakowujące, gdzie tekst etykiety znajduje się w innym elemencie potomnym
<label>
<span>Username</span>
<input />
</label>
// Atrybuty aria-label
// Należy pamiętać, że nie jest to etykieta widoczna dla użytkowników na stronie, dlatego przeznaczenie pola input musi być oczywiste dla użytkowników wizualnych.
<input aria-label="Username" />
Opcje
exact: boolean
Określa, czy
text
ma być dopasowywany dokładnie: z uwzględnieniem wielkości liter i całego ciągu. Domyślnie wyłączone. Ta opcja jest ignorowana, jeślitext
jest wyrażeniem regularnym. Należy pamiętać, że dokładne dopasowanie nadal przycina białe znaki.
Zobacz także
getByPlaceholder
function getByPlaceholder(
text: string | RegExp,
options?: LocatorOptions
): Locator;
Tworzy lokator zdolny do znalezienia elementu, który ma określony atrybut placeholder
. Vitest dopasuje każdy element, który ma pasujący atrybut placeholder
, nie ograniczając się tylko do input
.
<input placeholder="Username" />;
page.getByPlaceholder('Username'); // ✅
page.getByPlaceholder('not found'); // ❌
WARNING
Generalnie lepiej jest polegać na etykiecie za pomocą getByLabelText
niż na placeholderze.
Opcje
exact: boolean
Określa, czy
text
ma być dopasowywany dokładnie: z uwzględnieniem wielkości liter i całego ciągu. Domyślnie wyłączone. Ta opcja jest ignorowana, jeślitext
jest wyrażeniem regularnym. Należy pamiętać, że dokładne dopasowanie nadal przycina białe znaki.
Zobacz także
getByText
function getByText(text: string | RegExp, options?: LocatorOptions): Locator;
Tworzy lokator zdolny do znalezienia elementu, który zawiera określony tekst. Tekst będzie dopasowywany do nodeValue
węzła tekstowego lub wartości elementu input, jeśli typ to button
lub reset
. Dopasowywanie tekstu zawsze normalizuje białe znaki, nawet przy dokładnym dopasowaniu. Na przykład, wiele spacji jest zamienianych na jedną, znaki nowej linii na spacje, a początkowe i końcowe białe znaki są ignorowane.
<a href="/about">About ℹ️</a>;
page.getByText(/about/i); // ✅
page.getByText('about', { exact: true }); // ❌
TIP
Ten lokator jest przydatny do lokalizowania elementów nieinteraktywnych. Jeśli musisz zlokalizować element interaktywny, taki jak przycisk lub pole tekstowe, preferuj getByRole
.
Opcje
exact: boolean
Określa, czy
text
ma być dopasowywany dokładnie: z uwzględnieniem wielkości liter i całego ciągu. Domyślnie wyłączone. Ta opcja jest ignorowana, jeślitext
jest wyrażeniem regularnym. Należy pamiętać, że dokładne dopasowanie nadal przycina białe znaki.
Zobacz także
getByTitle
function getByTitle(text: string | RegExp, options?: LocatorOptions): Locator;
Tworzy lokator zdolny do znalezienia elementu, który ma określony atrybut title
. W przeciwieństwie do getByTitle
z testing-library, Vitest nie może znaleźć elementów title
wewnątrz SVG.
<span title="Delete" id="2"></span>;
page.getByTitle('Delete'); // ✅
page.getByTitle('Create'); // ❌
Opcje
exact: boolean
Określa, czy
text
ma być dopasowywany dokładnie: z uwzględnieniem wielkości liter i całego ciągu. Domyślnie wyłączone. Ta opcja jest ignorowana, jeślitext
jest wyrażeniem regularnym. Należy pamiętać, że dokładne dopasowanie nadal przycina białe znaki.
Zobacz także
getByTestId
function getByTestId(text: string | RegExp): Locator;
Tworzy lokator zdolny do znalezienia elementu, który pasuje do określonego atrybutu testowego ID. Możesz skonfigurować nazwę atrybutu za pomocą browser.locators.testIdAttribute
.
<div data-testid="custom-element" />;
page.getByTestId('custom-element'); // ✅
page.getByTestId('non-existing-element'); // ❌
WARNING
Zaleca się używanie tego tylko wtedy, gdy inne lokatory nie spełniają Twoich potrzeb. Używanie atrybutów data-testid
nie odzwierciedla rzeczywistego sposobu interakcji użytkownika z oprogramowaniem i należy tego unikać, jeśli to możliwe.
Opcje
exact: boolean
Określa, czy
text
ma być dopasowywany dokładnie: z uwzględnieniem wielkości liter i całego ciągu. Domyślnie wyłączone. Ta opcja jest ignorowana, jeślitext
jest wyrażeniem regularnym. Należy pamiętać, że dokładne dopasowanie nadal przycina białe znaki.
Zobacz także
nth
function nth(index: number): Locator;
Ta metoda zwraca nowy lokator, który dopasowuje tylko element o określonym indeksie w wyniku zapytania wieloelementowego. Jest to indeksowanie zerowe, nth(0)
wybiera pierwszy element. W przeciwieństwie do elements()[n]
, lokator nth
będzie ponawiał próbę, dopóki element nie będzie obecny.
<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
Zanim użyjesz nth
, możesz uznać za przydatne użycie połączonych lokatorów, aby zawęzić wyszukiwanie. Czasami nie ma lepszego sposobu na rozróżnienie niż pozycja elementu; chociaż może to prowadzić do niestabilnych testów, jest to lepsze niż nic.
page.getByLabel('two').getByRole('input'); // ✅ lepsza alternatywa dla page.getByRole('textbox').nth(3)
page.getByLabel('one').getByRole('input'); // ❌ zbyt niejednoznaczne
page.getByLabel('one').getByRole('input').nth(1); // ✅ pragmatyczny kompromis
first
function first(): Locator;
Ta metoda zwraca nowy lokator, który dopasowuje tylko pierwszy element z wyniku zapytania wieloelementowego. Jest to skrót dla nth(0)
.
<input /> <input /> <input />
page.getByRole('textbox').first(); // ✅
last
function last(): Locator;
Ta metoda zwraca nowy lokator, który dopasowuje tylko ostatni element z wyniku zapytania wieloelementowego. Jest to skrót dla nth(-1)
.
<input /> <input /> <input />
page.getByRole('textbox').last(); // ✅
and
function and(locator: Locator): Locator;
Ta metoda tworzy nowy lokator, który dopasowuje elementy spełniające kryteria zarówno lokatora nadrzędnego, jak i dostarczonego. Poniższy przykład znajduje przycisk z określonym tytułem:
page.getByRole('button').and(page.getByTitle('Subscribe'));
or
function or(locator: Locator): Locator;
Ta metoda tworzy nowy lokator, który dopasowuje elementy spełniające kryteria jednego lub obu lokatorów.
WARNING
Zauważ, że jeśli lokator dopasowuje więcej niż jeden element, wywołanie innej metody może rzucić wyjątek, jeśli oczekuje ona pojedynczego elementu:
<>
<button>Click me</button>
<a href="https://vitest.dev">Error happened!</a>
</>;
page.getByRole('button').or(page.getByRole('link')).click(); // ❌ dopasowuje wiele elementów
filter
function filter(options: LocatorOptions): Locator;
Ta metoda zawęża lokator zgodnie z podanymi opcjami, takimi jak filtrowanie według tekstu. Można ją łączyć kaskadowo, aby zastosować wiele filtrów.
has
- Typ:
Locator
Ta opcja zawęża selektor, aby dopasować elementy, które zawierają inne elementy zgodne z dostarczonym lokatorem. Na przykład, z tym HTML:
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Możemy zawęzić lokator, aby znaleźć tylko article
z tekstem Vitest
w środku:
page.getByRole('article').filter({ has: page.getByText('Vitest') }); // ✅
WARNING
Dostarczony lokator (page.getByText('Vitest')
w przykładzie) musi być względny względem lokatora nadrzędnego (page.getByRole('article')
w przykładzie). Wyszukiwanie rozpocznie się od lokatora nadrzędnego, a nie od korzenia dokumentu.
Oznacza to, że nie możesz przekazać lokatora, który wyszukuje element poza zakresem lokatora nadrzędnego:
page.getByText('Vitest').filter({ has: page.getByRole('article') }); // ❌
Ten przykład zakończy się niepowodzeniem, ponieważ element article
znajduje się poza zakresem elementu zawierającego tekst Vitest
.
TIP
Tę metodę można łączyć kaskadowo, aby jeszcze bardziej zawęzić element:
page
.getByRole('article')
.filter({ has: page.getByRole('button', { name: 'delete row' }) })
.filter({ has: page.getByText('Vitest') });
hasNot
- Typ:
Locator
Ta opcja zawęża selektor, aby dopasować elementy, które nie zawierają innych elementów pasujących do dostarczonego lokatora. Na przykład, z tym HTML:
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Możemy zawęzić lokator, aby znaleźć tylko article
, który nie ma w sobie Rolldown
.
page.getByRole('article').filter({ hasNot: page.getByText('Rolldown') }); // ✅
page.getByRole('article').filter({ hasNot: page.getByText('Vitest') }); // ❌
WARNING
Zauważ, że dostarczony lokator jest wyszukiwany względem elementu nadrzędnego, a nie korzenia dokumentu, tak jak opcja has
.
hasText
- Typ:
string | RegExp
Ta opcja zawęża selektor, aby dopasować tylko elementy, które zawierają podany tekst gdzieś w środku. Gdy przekazany jest string
, dopasowanie jest bez uwzględniania wielkości liter i dopasowuje podciągi.
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Oba lokatory znajdą ten sam element, ponieważ wyszukiwanie jest bez uwzględniania wielkości liter:
page.getByRole('article').filter({ hasText: 'Vitest' }); // ✅
page.getByRole('article').filter({ hasText: 'Vite' }); // ✅
hasNotText
- Typ:
string | RegExp
Ta opcja zawęża selektor, aby dopasować tylko elementy, które nie zawierają podanego tekstu gdzieś w środku. Gdy przekazany jest string
, dopasowanie jest bez uwzględniania wielkości liter i dopasowuje podciągi.
Metody
Wszystkie metody są asynchroniczne i wymagają użycia await
. Od Vitest 3, testy zakończą się niepowodzeniem, jeśli metoda nie zostanie wywołana z await
.
click
function click(options?: UserEventClickOptions): Promise<void>;
Kliknij na element. Możesz użyć opcji, aby ustawić pozycję kursora.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).click();
dblClick
function dblClick(options?: UserEventDoubleClickOptions): Promise<void>;
Wyzwala zdarzenie podwójnego kliknięcia na wskazanym elemencie. Możesz użyć opcji, aby ustawić pozycję kursora.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).dblClick();
tripleClick
function tripleClick(options?: UserEventTripleClickOptions): Promise<void>;
Wyzwala zdarzenie potrójnego kliknięcia na elemencie. Ponieważ w API przeglądarki nie ma tripleclick
, ta metoda wywoła trzy zdarzenia kliknięcia kolejno.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).tripleClick();
clear
function clear(options?: UserEventClearOptions): Promise<void>;
Czyści zawartość pola input.
import { page } from '@vitest/browser/context';
await page.getByRole('textbox', { name: 'Full Name' }).clear();
hover
function hover(options?: UserEventHoverOptions): Promise<void>;
Przesuwa kursor na wybrany element.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).hover();
unhover
function unhover(options?: UserEventHoverOptions): Promise<void>;
Działa tak samo jak locator.hover
, ale przenosi kursor na element document.body
(czyli poza aktualnie najeżdżany element).
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).unhover();
fill
function fill(text: string, options?: UserEventFillOptions): Promise<void>;
Ustawia wartość bieżącego pola input
, textarea
lub elementu 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>;
Przeciąga bieżący element do wskazanego miejsca docelowego.
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>;
Wybierz jedną lub więcej wartości z elementu rozwijanego <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>;
Tworzy zrzut ekranu elementu znalezionego za pomocą selektora lokatora.
Możesz określić miejsce zapisu zrzutu ekranu za pomocą opcji path
, która jest względna względem bieżącego pliku testowego. Jeśli opcja path
nie jest ustawiona, Vitest domyślnie użyje browser.screenshotDirectory
(domyślnie __screenshot__
), a nazwy pliku i testu posłużą do określenia ścieżki zrzutu ekranu.
Jeśli potrzebujesz również zawartości zrzutu ekranu, możesz określić base64: true
, aby zwrócić ją wraz ze ścieżką pliku, w którym zrzut ekranu jest zapisany.
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, // zwróć również ciąg base64
});
// path - pełna ścieżka do zrzutu ekranu
// base64 - zrzut ekranu zakodowany w base64
OSTRZEŻENIE 3.2.0+
Zauważ, że screenshot
zawsze zwróci dane w formacie base64, jeśli save
jest ustawione na false
. W takim przypadku path
jest również ignorowane.
query
function query(): Element | null;
Ta metoda zwraca pojedynczy element pasujący do selektora lokatora lub null
, jeśli nie znaleziono żadnego elementu.
Jeśli wiele elementów pasuje do selektora, ta metoda rzuci wyjątek. Użyj .elements()
, gdy potrzebujesz wszystkich pasujących elementów DOM, lub .all()
, jeśli potrzebujesz tablicy lokatorów odpowiadających selektorowi.
Rozważ następującą strukturę DOM:
<div>Hello <span>World</span></div>
<div>Hello</div>
Te lokatory nie rzucą wyjątku:
page.getByText('Hello World').query(); // ✅ HTMLDivElement
page.getByText('Hello Germany').query(); // ✅ null
page.getByText('World').query(); // ✅ HTMLSpanElement
page.getByText('Hello', { exact: true }).query(); // ✅ HTMLSpanElement
Te lokatory rzucą wyjątek:
// zwraca wiele elementów
page.getByText('Hello').query(); // ❌
page.getByText(/^Hello/).query(); // ❌
element
function element(): Element;
Ta metoda zwraca pojedynczy element pasujący do selektora lokatora.
Jeśli żaden element nie pasuje do selektora, wyrzucany jest błąd. Rozważ użycie .query()
, gdy potrzebujesz tylko sprawdzić, czy element istnieje.
Jeśli wiele elementów pasuje do selektora, wyrzucany jest błąd. Użyj .elements()
, gdy potrzebujesz wszystkich pasujących elementów DOM, lub .all()
, jeśli potrzebujesz tablicy lokatorów odpowiadających selektorowi.
TIP
Ta metoda może być przydatna, jeśli musisz przekazać ją do zewnętrznej biblioteki. Jest ona wywoływana automatycznie, gdy lokator jest używany z expect.element
za każdym razem, gdy asercja jest ponawiana:
await expect.element(page.getByRole('button')).toBeDisabled();
Rozważ następującą strukturę DOM:
<div>Hello <span>World</span></div>
<div>Hello Germany</div>
<div>Hello</div>
Te lokatory nie rzucą wyjątku:
page.getByText('Hello World').element(); // ✅
page.getByText('Hello Germany').element(); // ✅
page.getByText('World').element(); // ✅
page.getByText('Hello', { exact: true }).element(); // ✅
Te lokatory rzucą wyjątek:
// zwraca wiele elementów
page.getByText('Hello').element(); // ❌
page.getByText(/^Hello/).element(); // ❌
// nie zwraca żadnych elementów
page.getByText('Hello USA').element(); // ❌
elements
function elements(): Element[];
Ta metoda zwraca tablicę elementów pasujących do selektora lokatora.
Ta funkcja nigdy nie wyrzuca błędu. Jeśli nie ma elementów pasujących do selektora, ta metoda zwróci pustą tablicę.
Rozważ następującą strukturę DOM:
<div>Hello <span>World</span></div>
<div>Hello</div>
Te lokatory zawsze zakończą się sukcesem:
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[];
Ta metoda zwraca tablicę nowych lokatorów, które pasują do selektora.
Wewnętrznie, ta metoda wywołuje .elements
i tworzy lokator dla każdego elementu za pomocą page.elementLocator
.
Właściwości
selector
selector
to ciąg znaków, który zostanie użyty do zlokalizowania elementu przez dostawcę przeglądarki. Playwright użyje swojej składni lokatora, podczas gdy preview
i webdriverio
użyją CSS.
DANGER
Nie należy używać tego ciągu w swoim kodzie testowym. Ciąg selector
jest przeznaczony wyłącznie do użytku podczas pracy z API poleceń:
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 automatycznie skonwertuje to na ciąg znaków
await commands.test(page.getByText('Hello')); // ✅
});
Niestandardowe lokatory 3.2.0+ zaawansowane
Możesz rozszerzyć wbudowane API lokatorów, definiując obiekt zawierający fabryki lokatorów. Te metody będą dostępne jako metody na obiekcie page
i każdym utworzonym lokatorze.
Te lokatory mogą być przydatne, jeśli wbudowane lokatory nie są wystarczające. Na przykład, gdy używasz niestandardowego frameworka dla swojego interfejsu użytkownika.
Fabryka lokatorów musi zwracać ciąg znaków selektora lub sam lokator.
TIP
Składnia selektora jest identyczna z lokatorami Playwrighta. Zapoznaj się z ich przewodnikiem, aby lepiej zrozumieć, jak z nimi pracować.
import { locators } from '@vitest/browser/context';
locators.extend({
getByArticleTitle(title) {
return `[data-title="${title}"]`;
},
getByArticleCommentsCount(count) {
return `.comments :text("${count} comments")`;
},
async previewComments() {
// masz dostęp do bieżącego lokatora za pomocą "this"
// należy pamiętać, że jeśli metoda została wywołana na `page`, `this` będzie `page`,
// a nie lokatorem!
if (this !== page) {
await this.click();
}
// ...
},
});
// jeśli używasz typescriptu, możesz rozszerzyć interfejs LocatorSelectors
// aby mieć autouzupełnianie w metodach locators.extend, page.* i locator.*
declare module '@vitest/browser/context' {
interface LocatorSelectors {
// jeśli niestandardowa metoda zwraca ciąg znaków, zostanie on przekonwertowany na lokator
// jeśli zwraca coś innego, zostanie zwrócony bez zmian
getByArticleTitle(title: string): Locator;
getByArticleCommentsCount(count: number): Locator;
// Vitest zwróci obiekt Promise i nie będzie próbował przekonwertować go na lokator
previewComments(this: Locator): Promise<void>;
}
}
Jeśli metoda jest wywoływana na globalnym obiekcie page
, selektor zostanie zastosowany do całej strony. W poniższym przykładzie getByArticleTitle
znajdzie wszystkie elementy z atrybutem data-title
, których wartość wynosi title
. Jednakże, jeśli metoda jest wywoływana na lokatorze, jej zakres zostanie ograniczony do tego lokatora.
<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(); // ❌