Рефакторинг локаторов Playwright

Перевод статьи «Refactor Playwright Locators Like a Boss».

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

В этой статье мы рассмотрим несколько отличных методов рефакторинга локаторов Playwright. Причем каждая описанная ниже идея быстра и проста в реализации. В конце концов, нет смысла предлагать оптимизацию, которая сделает жизнь разработчика автотестов мучительной.

Давайте начнем!

Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ

Расширение Playwright с помощью пользовательских селекторов

Playwright открывает мир возможностей с помощью своего механизма селекторов. При этом он не только поддерживает широкий спектр встроенных селекторов, но и позволяет пользователям определять свои собственные.

Пользовательские селекторы можно адаптировать к уникальным требованиям приложения. Это поможет сделать скрипты более читаемыми и поддерживать сложную логику выбора, выходящую за рамки базовой.

Вот несколько идей:

  1. data-state: выбор элементов на основе атрибута данных, представляющего состояние (например,  data-state=\"active\"), чтобы нацелить их на определенные состояния пользовательского интерфейса в одностраничном приложении.
  2. closest: выбор ближайшего предка элемента, соответствующего определенному селектору, по аналогии с методом Element.closest() в JavaScript.
  3. shadow: таргетирование элементов, находящихся в определенном теневом DOM.
  4. rotation: выбор элемента, ротированного в определенном диапазоне.

Чтобы продемонстрировать возможности пользовательских селекторов в Playwright, давайте создадим селектор data-state, описанный выше.

import * as pw from 'playwright';

// register custom selector
await pw.selectors.register('data-state', () => ({
  query(root: Node, selector: string) {
    return root.querySelector(`[data-state="${selector}"]`);
  },

  queryAll(root: Node, selector: string) {
    return Array.from(
      root.querySelectorAll(`[data-state="${selector}"]`)
    );
  },
}));

const browser = await pw.firefox.launch();
const page = await browser.newPage();
await page.goto('https://example.com');

// use the custom selector
const $active = page.locator('data-state=active');
await $active.click();

Как видите, для создания пользовательского селектора требуется совсем немного. Поскольку Playwright предоставляет вам доступ к базовому DOM, вы можете использовать все его возможности.

Использование объектной модели страницы

Объектная модель страницы (POM) — это шаблон проектирования, широко используемый в автоматизации для повышения удобства обслуживания и уменьшения количества дубликатов. Объект страницы инкапсулирует поведение и элементы конкретной страницы на вашем целевом сайте, что позволяет легко обновлять код по мере необходимости.

Вот пример того, как может выглядеть объект страницы для страницы входа в систему. Обратите внимание, как он собирает общие локаторы:

import {Page} from 'playwright';

export class LoginPage {
  page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  get $usernameInput() {
    return this.page.getByLabel('Username');
  }

  get $passwordInput() {
    return this.page.getByLabel('Password');
  }

  get $loginButton() {
    return this.page.getByText('Login');
  }

  public async login(username: string, password: string) {
    await this.$usernameInput.fill(username);
    await this.$passwordInput.fill(password);
    await this.$loginButton.click();
  }
}

Вот как вы можете использовать POM LoginPage:

import * as pw from 'playwright';
import {LoginPage} from './LoginPage';

const browser = await pw.webkit.launch();
const page = await browser.newPage();
await page.goto('https://app.browsercat.com/sign-in');

const loginPage = new LoginPage(page);
await loginPage.login('user@example.com', 'p1a2s3s4word!');
await loginPage.$loginButton.click();

Составление объектов страницы из компонентов

Я обнаружил, что смысл объектной модели страницы можно расширить еще больше. Подумайте о том, что большинство веб-сайтов построено из переиспользуемых компонентов, которые повторяются на нескольких страницах. Вместо того чтобы разрабатывать POM с нуля, можно составлять их из компонентов, встречающихся на каждой странице.

Вот как мы можем использовать объекты компонентов для AppMenuBarChatWidget и AppFooter:

// Define the separate components
class AppMenuBar {
  constructor(public page: Page) {}
  get $logo () {}
  get $menuButton () {}
}

class ChatWidget {
  constructor(public page: Page) {}
  get $chatToggle () {}
  get $messageInput () {}
  get $sendButton () {}

  async sendMessage(message: string) {}
}

class AppFooter {
  constructor(public page: Page) {}
  get $uptimeStatus () {}
}

// Compose these components into a complete page object
class ContactPage {
  menuBar: AppMenuBar;
  chatWidget: ChatWidget;
  footer: AppFooter;

  constructor(public page: Page) {
    this.menuBar = new AppMenuBar(page);
    this.chatWidget = new ChatWidget(page);
    this.footer = new AppFooter(page);
  }
}

// Use the composed POM
const browser = await pw.chromium.launch();
const page = await browser.newPage();
await page.goto('https://www.browsercat.com/contact');
const contactPage = new ContactPage(page);

// Use the component methods
await contactPage.menuBar.$menuButton.click();
await contactPage.chatWidget.sendMessage('Hello there!');

В приведенном выше примере составленный объект ContactPage становится мощной и удобной абстракцией, объединяющей взаимодействия для строки меню, виджета чата и нижнего колонтитула приложения, которые затем используются в повторяющихся взаимодействиях. Такой подход позволяет избежать лишних повторов в ваших скриптах и легко обновлять взаимодействия компонентов при изменении UI без необходимости изменения каждого скрипта.

Следующие шаги

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

Экспериментирование — это лучший способ обучения.

Счастливой автоматизации!

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *