Localizadores
Un localizador representa uno o varios elementos en el DOM. Cada localizador se define mediante una cadena llamada selector. Vitest simplifica el uso de selectores al proporcionar métodos convenientes que los generan automáticamente.
La API de localizadores de Vitest utiliza una adaptación de los localizadores de Playwright denominada Ivya. Sin embargo, Vitest extiende esta API a todos los proveedores de navegador, no solo a Playwright.
TIP
Esta página describe el uso de la API de localizadores. Para una comprensión más profunda de los localizadores y su aplicación, consulta la documentación de Playwright sobre "Localizadores".
getByRole
function getByRole(
role: ARIARole | string,
options?: LocatorByRoleOptions
): Locator;
Crea un localizador para encontrar un elemento basándose en su rol ARIA, atributos ARIA y nombre accesible.
TIP
Si solo necesitas consultar un único elemento con getByText('El nombre')
, a menudo es más apropiado usar getByRole(rolEsperado, { name: 'El nombre' })
. La consulta por nombre accesible no reemplaza otras consultas como *ByAltText
o *ByTitle
. Aunque el nombre accesible pueda coincidir con dichos atributos, no sustituye su funcionalidad.
Considera la siguiente estructura DOM:
<h3>Sign up</h3>
<label>
Login
<input type="text" />
</label>
<label>
Password
<input type="password" />
</label>
<br />
<button>Submit</button>
Puedes localizar cada elemento por su rol implícito:
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
Los roles se comparan por igualdad de cadena, sin heredar de la jerarquía de roles ARIA. Por lo tanto, consultar un rol de superclase como checkbox
no incluirá elementos con un rol de subclase como switch
.
Por defecto, muchos elementos semánticos en HTML tienen un rol; por ejemplo, <input type="radio">
tiene el rol de "radio". Los elementos no semánticos en HTML no tienen un rol; <div>
y <span>
sin semántica adicional devuelven null
. El atributo role
puede añadir semántica.
Proporcionar roles a través de atributos role
o aria-*
a elementos integrados que ya tienen un rol implícito está fuertemente desaconsejado por las directrices ARIA.
Opciones
exact: boolean
Indica si el
name
debe coincidir exactamente: sensible a mayúsculas y minúsculas y cadena completa. Deshabilitado por defecto. Esta opción se ignora siname
es una expresión regular. Ten en cuenta que la coincidencia exacta aún recorta los espacios en blanco.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
Indica si se deben incluir los elementos marcados (establecidos por
aria-checked
o<input type="checkbox"/>
). Por defecto, el filtro no se aplica.Consulta
aria-checked
para más información.tsx<> <button role="checkbox" aria-checked="true" /> <input type="checkbox" checked /> </>; page.getByRole('checkbox', { checked: true }); // ✅ page.getByRole('checkbox', { checked: false }); // ❌
disabled: boolean
Indica si se deben incluir los elementos deshabilitados. Por defecto, el filtro no se aplica. Ten en cuenta que, a diferencia de otros atributos, el estado
disabled
se hereda.Consulta
aria-disabled
para más información.tsx<input type="text" disabled />; page.getByRole('textbox', { disabled: true }); // ✅ page.getByRole('textbox', { disabled: false }); // ❌
expanded: boolean
Indica si se deben incluir los elementos expandidos. Por defecto, el filtro no se aplica.
Consulta
aria-expanded
para más información.tsx<a aria-expanded="true" href="example.com"> Link </a>; page.getByRole('link', { expanded: true }); // ✅ page.getByRole('link', { expanded: false }); // ❌
includeHidden: boolean
Indica si se deben consultar los elementos que normalmente se excluyen del árbol de accesibilidad. Por defecto, solo los elementos no ocultos coinciden con el selector de rol.
Ten en cuenta que los roles
none
ypresentation
siempre se incluyen.tsx<button style="display: none" />; page.getByRole('button'); // ❌ page.getByRole('button', { includeHidden: false }); // ❌ page.getByRole('button', { includeHidden: true }); // ✅
level: number
Un atributo numérico que suele estar presente para los roles
heading
,listitem
,row
,treeitem
con valores predeterminados para los elementos<h1>-<h6>
. Por defecto, el filtro no se aplica.Consulta
aria-level
para más información.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
Un nombre accesible. Por defecto, la coincidencia es insensible a mayúsculas y minúsculas y busca una subcadena. Usa la opción
exact
para controlar este comportamiento.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
Indica si se deben incluir los elementos presionados. Por defecto, el filtro no se aplica.
Consulta
aria-pressed
para más información.tsx<button aria-pressed="true">👍</button>; page.getByRole('button', { pressed: true }); // ✅ page.getByRole('button', { pressed: false }); // ❌
selected: boolean
Indica si se deben incluir los elementos seleccionados. Por defecto, el filtro no se aplica.
Consulta
aria-selected
para más información.tsx<button role="tab" aria-selected="true"> Vue </button>; page.getByRole('button', { selected: true }); // ✅ page.getByRole('button', { selected: false }); // ❌
Ver también
getByAltText
function getByAltText(text: string | RegExp, options?: LocatorOptions): Locator;
Crea un localizador capaz de encontrar un elemento con un atributo alt
que coincida con el texto. A diferencia de la implementación de Testing Library, Vitest encontrará cualquier elemento que tenga un atributo alt
coincidente.
<img alt="Incredibles 2 Poster" src="/incredibles-2.png" />;
page.getByAltText(/incredibles.*? poster/i); // ✅
page.getByAltText('non existing alt text'); // ❌
Opciones
exact: boolean
Indica si el
text
debe coincidir exactamente: sensible a mayúsculas y minúsculas y cadena completa. Deshabilitado por defecto. Esta opción se ignora sitext
es una expresión regular. Ten en cuenta que la coincidencia exacta aún recorta los espacios en blanco.
Ver también
getByLabelText
function getByLabelText(
text: string | RegExp,
options?: LocatorOptions
): Locator;
Crea un localizador capaz de encontrar un elemento que tenga una etiqueta asociada.
El localizador page.getByLabelText('Username')
encontrará todos los elementos input
en el siguiente ejemplo:
// Relación for/htmlFor entre la etiqueta y el ID del elemento de formulario
<label for="username-input">Username</label>
<input id="username-input" />
// El atributo aria-labelledby en elementos de formulario
<label id="username-label">Username</label>
<input aria-labelledby="username-label" />
// Etiquetas contenedoras
<label>Username <input /></label>
// Etiquetas contenedoras donde el texto de la etiqueta está en otro elemento hijo
<label>
<span>Username</span>
<input />
</label>
// Atributos aria-label
// ¡Atención! Esta no es una etiqueta visible para los usuarios en la página, por lo que el propósito de tu campo de entrada debe ser obvio para los usuarios visuales.
<input aria-label="Username" />
Opciones
exact: boolean
Indica si el
text
debe coincidir exactamente: sensible a mayúsculas y minúsculas y cadena completa. Deshabilitado por defecto. Esta opción se ignora sitext
es una expresión regular. Ten en cuenta que la coincidencia exacta aún recorta los espacios en blanco.
Ver también
getByPlaceholder
function getByPlaceholder(
text: string | RegExp,
options?: LocatorOptions
): Locator;
Crea un localizador capaz de encontrar un elemento que tenga el atributo placeholder
especificado. Vitest encontrará cualquier elemento que tenga un atributo placeholder
coincidente, no solo input
.
<input placeholder="Username" />;
page.getByPlaceholder('Username'); // ✅
page.getByPlaceholder('not found'); // ❌
WARNING
Generalmente es preferible usar una etiqueta con getByLabelText
en lugar de un marcador de posición.
Opciones
exact: boolean
Indica si el
text
debe coincidir exactamente: sensible a mayúsculas y minúsculas y cadena completa. Deshabilitado por defecto. Esta opción se ignora sitext
es una expresión regular. Ten en cuenta que la coincidencia exacta aún recorta los espacios en blanco.
Ver también
getByText
function getByText(text: string | RegExp, options?: LocatorOptions): Locator;
Crea un localizador capaz de encontrar un elemento que contenga el texto especificado. El texto se comparará con el nodeValue
de un TextNode
o el valor de un input
si el tipo es button
o reset
. La búsqueda por texto siempre normaliza los espacios en blanco, incluso con coincidencia exacta. Por ejemplo, convierte múltiples espacios en uno solo, transforma los saltos de línea en espacios e ignora los espacios en blanco iniciales y finales.
<a href="/about">About ℹ️</a>;
page.getByText(/about/i); // ✅
page.getByText('about', { exact: true }); // ❌
TIP
Este localizador es útil para localizar elementos no interactivos. Si necesitas localizar un elemento interactivo, como un botón o un campo de entrada, prefiere getByRole
.
Opciones
exact: boolean
Indica si el
text
debe coincidir exactamente: sensible a mayúsculas y minúsculas y cadena completa. Deshabilitado por defecto. Esta opción se ignora sitext
es una expresión regular. Ten en cuenta que la coincidencia exacta aún recorta los espacios en blanco.
Ver también
getByTitle
function getByTitle(text: string | RegExp, options?: LocatorOptions): Locator;
Crea un localizador capaz de encontrar un elemento que tenga el atributo title
especificado. A diferencia de getByTitle
de testing-library, Vitest no puede encontrar elementos con atributo title
dentro de un SVG.
<span title="Delete" id="2"></span>;
page.getByTitle('Delete'); // ✅
page.getByTitle('Create'); // ❌
Opciones
exact: boolean
Indica si el
text
debe coincidir exactamente: sensible a mayúsculas y minúsculas y cadena completa. Deshabilitado por defecto. Esta opción se ignora sitext
es una expresión regular. Ten en cuenta que la coincidencia exacta aún recorta los espacios en blanco.
Ver también
getByTestId
function getByTestId(text: string | RegExp): Locator;
Crea un localizador capaz de encontrar un elemento que coincida con el atributo de ID de prueba especificado. Puedes configurar el nombre del atributo con browser.locators.testIdAttribute
.
<div data-testid="custom-element" />;
page.getByTestId('custom-element'); // ✅
page.getByTestId('non-existing-element'); // ❌
WARNING
Se recomienda usar esto solo después de que los otros localizadores no funcionen para tu caso de uso. El uso de atributos data-testid
no refleja cómo se utiliza tu software y debe evitarse si es posible.
Opciones
exact: boolean
Indica si el
text
debe coincidir exactamente: sensible a mayúsculas y minúsculas y cadena completa. Deshabilitado por defecto. Esta opción se ignora sitext
es una expresión regular. Ten en cuenta que la coincidencia exacta aún recorta los espacios en blanco.
Ver también
nth
function nth(index: number): Locator;
Este método devuelve un nuevo localizador que coincide solo con un índice específico dentro de un resultado de consulta de múltiples elementos. Se basa en un índice de cero, donde nth(0)
selecciona el primer elemento. A diferencia de elements()[n]
, el localizador nth
se volverá a intentar hasta que el elemento esté presente.
<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
Antes de recurrir a nth
, te puede resultar útil usar localizadores encadenados para acotar tu búsqueda. A veces no hay mejor manera de distinguir los elementos que por su posición; aunque esto puede llevar a resultados inconsistentes, es mejor que nada.
page.getByLabel('two').getByRole('input'); // ✅ mejor alternativa a page.getByRole('textbox').nth(3)
page.getByLabel('one').getByRole('input'); // ❌ demasiado ambiguo
page.getByLabel('one').getByRole('input').nth(1); // ✅ compromiso pragmático
first
function first(): Locator;
Este método devuelve un nuevo localizador que coincide solo con el primer índice de un resultado de consulta de múltiples elementos. Es una forma abreviada de nth(0)
.
<input /> <input /> <input />
page.getByRole('textbox').first(); // ✅
last
function last(): Locator;
Este método devuelve un nuevo localizador que coincide solo con el último índice de un resultado de consulta de múltiples elementos. Es una forma abreviada de nth(-1)
.
<input /> <input /> <input />
page.getByRole('textbox').last(); // ✅
and
function and(locator: Locator): Locator;
Este método crea un nuevo localizador que coincide tanto con el localizador padre como con el localizador proporcionado. El siguiente ejemplo encuentra un botón con un título específico:
page.getByRole('button').and(page.getByTitle('Subscribe'));
or
function or(locator: Locator): Locator;
Este método crea un nuevo localizador que coincide con uno o ambos localizadores.
WARNING
Ten en cuenta que si el localizador encuentra más de un elemento, llamar a otro método podría lanzar un error si este espera un solo elemento:
<>
<button>Click me</button>
<a href="https://vitest.dev">Error happened!</a>
</>;
page.getByRole('button').or(page.getByRole('link')).click(); // ❌ coincide con múltiples elementos
filter
function filter(options: LocatorOptions): Locator;
Este método acota el localizador según las opciones, como filtrar por texto. Se puede encadenar para aplicar múltiples filtros.
has
- Tipo:
Locator
Esta opción acota el selector para que coincida con elementos que contengan otros elementos que también coincidan con el localizador proporcionado. Por ejemplo, con este HTML:
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Podemos acotar el localizador para encontrar solo el article
con el texto Vitest
dentro:
page.getByRole('article').filter({ has: page.getByText('Vitest') }); // ✅
WARNING
El localizador proporcionado (page.getByText('Vitest')
en el ejemplo) debe ser relativo al localizador padre (page.getByRole('article')
en el ejemplo). La consulta se realizará a partir del localizador padre, no de la raíz del documento.
Es decir, no puedes pasar un localizador que busque un elemento fuera del ámbito del localizador padre:
page.getByText('Vitest').filter({ has: page.getByRole('article') }); // ❌
Este ejemplo fallará porque el elemento article
está fuera del elemento que contiene el texto Vitest
.
TIP
Este método se puede encadenar para refinar aún más la búsqueda del elemento:
page
.getByRole('article')
.filter({ has: page.getByRole('button', { name: 'delete row' }) })
.filter({ has: page.getByText('Vitest') });
hasNot
- Tipo:
Locator
Esta opción acota el selector para que coincida con elementos que NO contengan otros elementos que también coincidan con el localizador proporcionado. Por ejemplo, con este HTML:
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Podemos acotar el localizador para encontrar solo el article
que no tenga Rolldown
dentro.
page.getByRole('article').filter({ hasNot: page.getByText('Rolldown') }); // ✅
page.getByRole('article').filter({ hasNot: page.getByText('Vitest') }); // ❌
WARNING
Ten en cuenta que el localizador proporcionado se consulta en relación con el padre, no con la raíz del documento, al igual que la opción has
.
hasText
- Tipo:
string | RegExp
Esta opción acota el selector para que solo coincida con elementos que contengan el texto proporcionado en su interior. Cuando se pasa un string
, la coincidencia es insensible a mayúsculas y minúsculas y busca una subcadena.
<article>
<div>Vitest</div>
</article>
<article>
<div>Rolldown</div>
</article>
Ambos localizadores encontrarán el mismo elemento porque la búsqueda no distingue entre mayúsculas y minúsculas:
page.getByRole('article').filter({ hasText: 'Vitest' }); // ✅
page.getByRole('article').filter({ hasText: 'Vite' }); // ✅
hasNotText
- Tipo:
string | RegExp
Esta opción acota el selector para que solo coincida con elementos que NO contengan el texto proporcionado en su interior. Cuando se pasa un string
, la coincidencia es insensible a mayúsculas y minúsculas y busca una subcadena.
Métodos
Todos los métodos son asíncronos y requieren el uso de await
. Desde Vitest 3, las pruebas fallarán si un método no se espera (no se usa await
).
click
function click(options?: UserEventClickOptions): Promise<void>;
Haz clic en un elemento. Puedes usar las opciones para establecer la posición del cursor.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).click();
dblClick
function dblClick(options?: UserEventDoubleClickOptions): Promise<void>;
Activa un evento de doble clic en un elemento. Puedes usar las opciones para establecer la posición del cursor.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).dblClick();
tripleClick
function tripleClick(options?: UserEventTripleClickOptions): Promise<void>;
Activa un evento de triple clic en un elemento. Dado que no existe tripleclick
en la API del navegador, este método activará tres eventos de clic seguidos.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).tripleClick();
clear
function clear(options?: UserEventClearOptions): Promise<void>;
Borra el contenido del campo de entrada.
import { page } from '@vitest/browser/context';
await page.getByRole('textbox', { name: 'Full Name' }).clear();
hover
function hover(options?: UserEventHoverOptions): Promise<void>;
Mueve la posición del cursor al elemento seleccionado.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).hover();
unhover
function unhover(options?: UserEventHoverOptions): Promise<void>;
Esto funciona igual que locator.hover
, pero mueve el cursor al elemento document.body
en su lugar.
import { page } from '@vitest/browser/context';
await page.getByRole('img', { name: 'Rose' }).unhover();
fill
function fill(text: string, options?: UserEventFillOptions): Promise<void>;
Establece el valor del elemento input
, textarea
o contenteditable
actual.
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>;
Arrastra el elemento actual a la ubicación de destino.
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>;
Selecciona uno o más valores de un elemento <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>;
Crea una captura de pantalla del elemento que coincide con el selector del localizador.
Puedes especificar la ubicación de guardado para la captura de pantalla usando la opción path
, que es relativa al archivo de prueba actual. Si la opción path
no está configurada, Vitest utilizará por defecto browser.screenshotDirectory
(__screenshot__
por defecto), junto con los nombres del archivo y la prueba para determinar la ruta del archivo de la captura de pantalla.
Si también necesitas el contenido de la captura de pantalla, puedes especificar base64: true
para obtenerlo junto con la ruta del archivo donde se guarda la captura de pantalla.
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, // también devuelve la cadena base64
});
// path - ruta completa a la captura de pantalla
// base64 - cadena codificada en base64 de la captura de pantalla
ADVERTENCIA 3.2.0+
Ten en cuenta que screenshot
siempre devolverá una cadena base64
si save
se establece en false
. La path
también se ignora en ese caso.
query
function query(): Element | null;
Este método devuelve un único elemento que coincide con el selector del localizador o null
si no se encuentra ningún elemento.
Si varios elementos coinciden con el selector, este método lanzará un error. Usa .elements()
cuando necesites todos los elementos DOM coincidentes o .all()
si necesitas un array de localizadores que coincidan con el selector.
Considera la siguiente estructura DOM:
<div>Hello <span>World</span></div>
<div>Hello</div>
Estos localizadores no lanzarán un error:
page.getByText('Hello World').query(); // ✅ HTMLDivElement
page.getByText('Hello Germany').query(); // ✅ null
page.getByText('World').query(); // ✅ HTMLSpanElement
page.getByText('Hello', { exact: true }).query(); // ✅ HTMLSpanElement
Estos localizadores lanzarán un error:
// devuelve múltiples elementos
page.getByText('Hello').query(); // ❌
page.getByText(/^Hello/).query(); // ❌
element
function element(): Element;
Este método devuelve un único elemento que coincide con el selector del localizador.
Si ningún elemento coincide con el selector, se lanza un error. Considera usar .query()
cuando solo necesites verificar si el elemento existe.
Si múltiples elementos coinciden con el selector, se lanza un error. Usa .elements()
cuando necesites todos los elementos DOM coincidentes o .all()
si necesitas un array de localizadores que coincidan con el selector.
TIP
Este método puede ser útil si necesitas pasarlo a una biblioteca externa. Se llama automáticamente cuando el localizador se usa con expect.element
cada vez que se reintenta la aserción:
await expect.element(page.getByRole('button')).toBeDisabled();
Considera la siguiente estructura DOM:
<div>Hello <span>World</span></div>
<div>Hello Germany</div>
<div>Hello</div>
Estos localizadores no lanzarán un error:
page.getByText('Hello World').element(); // ✅
page.getByText('Hello Germany').element(); // ✅
page.getByText('World').element(); // ✅
page.getByText('Hello', { exact: true }).element(); // ✅
Estos localizadores lanzarán un error:
// devuelve múltiples elementos
page.getByText('Hello').element(); // ❌
page.getByText(/^Hello/).element(); // ❌
// no devuelve elementos
page.getByText('Hello USA').element(); // ❌
elements
function elements(): Element[];
Este método devuelve un array de elementos que coinciden con el selector del localizador.
Esta función nunca lanza un error. Si no hay elementos que coincidan con el selector, este método devolverá un array vacío.
Considera la siguiente estructura DOM:
<div>Hello <span>World</span></div>
<div>Hello</div>
Estos localizadores siempre tendrán éxito:
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[];
Este método devuelve un array de nuevos localizadores que coinciden con el selector.
Internamente, este método llama a .elements
y envuelve cada elemento usando page.elementLocator
.
Propiedades
selector
El selector
es una cadena que será utilizada por el proveedor del navegador para localizar el elemento. Playwright utilizará una sintaxis de localizador de playwright
mientras que preview
y webdriverio
usarán CSS.
DANGER
No debes usar esta cadena directamente en tu código de prueba. La cadena del selector
solo debe usarse cuando se trabaja con la API de Comandos:
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 lo convertirá automáticamente en una cadena
await commands.test(page.getByText('Hello')); // ✅
});
Localizadores Personalizados 3.2.0+ avanzado
Puedes extender la API de localizadores incorporados definiendo un objeto de fábricas de localizadores. Estos métodos existirán como métodos en el objeto page
y en cualquier localizador creado.
Estos localizadores pueden ser útiles si los localizadores incorporados no son suficientes. Por ejemplo, cuando usas un framework personalizado para tu UI.
La fábrica de localizadores debe devolver una cadena de selector o un localizador en sí mismo.
TIP
La sintaxis del selector es idéntica a la de los localizadores de Playwright. Por favor, lee su guía para comprender mejor cómo trabajar con ellos.
import { locators } from '@vitest/browser/context';
locators.extend({
getByArticleTitle(title) {
return `[data-title="${title}"]`;
},
getByArticleCommentsCount(count) {
return `.comments :text("${count} comments")`;
},
async previewComments() {
// tienes acceso al localizador actual a través de "this"
// ¡Atención! Si el método fue llamado en `page`, `this` será `page`,
// no el localizador!
if (this !== page) {
await this.click();
}
// ...
},
});
// si estás usando typescript, puedes extender la interfaz LocatorSelectors
// para tener autocompletado en locators.extend, page.* y locator.* methods
declare module '@vitest/browser/context' {
interface LocatorSelectors {
// si el método personalizado devuelve una cadena, se convertirá en un localizador
// si devuelve cualquier otra cosa, se devolverá como de costumbre
getByArticleTitle(title: string): Locator;
getByArticleCommentsCount(count: number): Locator;
// Vitest devolverá una promesa y no intentará convertirla en un localizador
previewComments(this: Locator): Promise<void>;
}
}
Si el método se llama en el objeto global page
, el selector se aplicará a toda la página. En el ejemplo siguiente, getByArticleTitle
encontrará todos los elementos con un atributo data-title
cuyo valor sea title
. Sin embargo, si el método se llama en el localizador, su alcance se restringirá a ese localizador.
<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(); // ❌