Playwright предоставляет мощный механизм работы с локаторами, который выходит далеко за рамки простого поиска элементов на странице. Локаторы позволяют не только точно находить элементы, но и выполнять над ними сложные действия с высокой стабильностью и предсказуемостью. Расширение локаторов — это процесс создания более универсальных и гибких средств для взаимодействия с элементами, что критично при тестировании сложных веб-приложений.
Локатор в Playwright представляет собой объект, описывающий стратегию поиска элемента. Ключевые моменты:
Пример базового локатора:
const loginButton = page.locator('button#login');
await loginButton.click();
Для повышения читаемости и повторного использования тестов создаются кастомные локаторы. Это методы, возвращающие локаторы с дополнительными условиями поиска или поведением.
class PageElements {
constructor(page) {
this.page = page;
}
getLoginButton() {
return this.page.locator('button#login').first();
}
getUserCard(name) {
return this.page.locator(`.user-card:has-text("${name}")`);
}
}
const elements = new PageElements(page);
await elements.getLoginButton().click();
await elements.getUserCard('Иван').hover();
Ключевое преимущество: повторное использование сложных стратегий поиска без дублирования кода.
Playwright позволяет комбинировать локаторы для точного выделения элементов. Основные методы:
.locator(selector) — поиск внутри
родительского локатора..first(), .last(),
.nth(index) — выбор конкретного элемента из
множества..filter({ hasText, has }) — фильтрация
по тексту или вложенному элементу..locator('selector1 >> selector2')
— цепочка селекторов через >>.Пример:
const userCard = page.locator('.user-list').locator('.user-card').filter({ hasText: 'Иван' });
await userCard.getByRole('button', { name: 'Удалить' }).click();
Такая комбинация гарантирует, что действие будет выполнено именно над нужным элементом, даже если структура страницы сложная и динамическая.
Локаторы можно обогащать дополнительными ожиданиями перед действием. Playwright поддерживает асинхронные цепочки:
const submitButton = page.locator('button[type="submit"]');
await submitButton.waitFor({ state: 'visible' });
await submitButton.click();
Можно создавать обёртки для стандартных действий, включающие проверку состояния:
async function clickWhenReady(locator) {
await locator.waitFor({ state: 'visible' });
await locator.click();
}
Для веб-приложений с динамическим контентом критично использовать динамические селекторы. Playwright поддерживает:
locator('text=Вход')locator('div.user-card[data-id="123"]')locator('//button[contains(text(), "Удалить")]')Комбинация этих методов позволяет создавать стабильные локаторы даже для элементов, которые появляются после AJAX-запросов или интерактивных действий пользователя.
Для крупных проектов выгодно создавать библиотеки локаторов. Каждая страница или компонент получает свой объект локаторов:
class LoginPage {
constructor(page) {
this.page = page;
}
usernameInput() {
return this.page.locator('#username');
}
passwordInput() {
return this.page.locator('#password');
}
loginButton() {
return this.page.locator('button[type="submit"]');
}
}
Преимущества:
Playwright позволяет создавать пользовательские функции фильтрации, расширяя стандартные локаторы. Например:
const activeUserCard = page.locator('.user-card').filter({
has: page.locator('.status', { hasText: 'Активен' })
});
Это позволяет выделять элементы по сложным критериям, недоступным через базовые селекторы.
При использовании TypeScript можно добавить типизацию для локаторов:
class DashboardPage {
readonly page: Page;
readonly welcomeMessage: Locator;
constructor(page: Page) {
this.page = page;
this.welcomeMessage = page.locator('#welcome');
}
async assertWelcomeText(expected: string) {
await expect(this.welcomeMessage).toHaveText(expected);
}
}
Типизация улучшает автодополнение и снижает вероятность ошибок при использовании локаторов в больших тестовых наборах.
Расширение локаторов в Playwright обеспечивает надежность тестов, читаемость кода и легкость поддержки, особенно в крупных и динамичных веб-приложениях.