Расширение локаторов

Playwright предоставляет мощный механизм работы с локаторами, который выходит далеко за рамки простого поиска элементов на странице. Локаторы позволяют не только точно находить элементы, но и выполнять над ними сложные действия с высокой стабильностью и предсказуемостью. Расширение локаторов — это процесс создания более универсальных и гибких средств для взаимодействия с элементами, что критично при тестировании сложных веб-приложений.


Основные принципы локаторов

Локатор в 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=Вход')
  • Сложные CSS-селекторы: locator('div.user-card[data-id="123"]')
  • XPath-селекторы: 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

При использовании 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);
    }
}

Типизация улучшает автодополнение и снижает вероятность ошибок при использовании локаторов в больших тестовых наборах.


Итоговые рекомендации по расширению локаторов

  • Использовать цепочки локаторов для точного выделения элементов.
  • Создавать кастомные методы и библиотеки локаторов для повторного использования.
  • Включать проверки состояния элементов перед действиями.
  • Применять динамические и сложные селекторы для стабильности тестов.
  • Интегрировать локаторы с TypeScript для безопасного и удобного кода.

Расширение локаторов в Playwright обеспечивает надежность тестов, читаемость кода и легкость поддержки, особенно в крупных и динамичных веб-приложениях.