Кастомные матчеры

В Playwright тесты строятся на взаимодействии с элементами страницы, проверке их состояния и содержимого. Часто стандартных методов проверки (toHaveText, toBeVisible, toHaveAttribute) недостаточно для сложных сценариев. Для таких случаев используются кастомные матчеры, которые позволяют создавать гибкие, повторно используемые условия для ассертов.


Основы кастомных матчеров

Кастомный матчер — это функция, которая определяет, соответствует ли объект заданному условию. В Playwright для тестов на JavaScript (с использованием Playwright Test) матчеры создаются через метод expect.extend.

Пример базового кастомного матчера:

import { expect } from '@playwright/test';

expect.extend({
  async toHaveCssClass(received, className) {
    const element = await received;
    const classList = await element.getAttribute('class') || '';
    const pass = classList.split(' ').includes(className);

    if (pass) {
      return {
        message: () => `Ожидание выполнено: элемент содержит класс "${className}"`,
        pass: true,
      };
    } else {
      return {
        message: () => `Элемент не содержит класс "${className}". Существующие классы: ${classList}`,
        pass: false,
      };
    }
  },
});

Использование:

await expect(page.locator('#submit')).toHaveCssClass('active');

Здесь создается проверка на наличие CSS-класса у элемента. Ключевой момент: кастомный матчер всегда возвращает объект с двумя полями: pass (логическое значение) и message (функция с описанием ошибки).


Асинхронные проверки и Playwright

Большинство операций с элементами в Playwright асинхронные, поэтому кастомные матчеры должны поддерживать async/await. Ошибки синхронного выполнения могут привести к ложным результатам или незавершенным промисам.

Пример проверки видимости нескольких элементов:

expect.extend({
  async toAllBeVisible(received) {
    const elements = await received.elementHandles();
    const invisible = [];

    for (const el of elements) {
      if (!(await el.isVisible())) {
        invisible.push(await el.textContent());
      }
    }

    const pass = invisible.length === 0;

    return {
      pass,
      message: () => pass 
        ? 'Все элементы видимы' 
        : `Следующие элементы не видимы: ${invisible.join(', ')}`,
    };
  },
});

Использование:

await expect(page.locator('.item')).toAllBeVisible();

Этот пример демонстрирует работу с коллекциями элементов и обработку асинхронных состояний.


Параметризация и повторное использование матчеров

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

Пример матчера для текста, соответствующего регулярному выражению:

expect.extend({
  async toMatchTextRegex(received, regex) {
    const element = await received;
    const text = await element.textContent();
    const pass = regex.test(text);

    return {
      pass,
      message: () => pass
        ? `Текст "${text}" соответствует регулярному выражению ${regex}`
        : `Текст "${text}" не соответствует регулярному выражению ${regex}`,
    };
  },
});

Использование:

await expect(page.locator('#price')).toMatchTextRegex(/^\$\d+\.\d{2}$/);

Такая конструкция обеспечивает гибкость: один матчинг-метод может работать с любыми элементами и условиями.


Отладка кастомных матчеров

Чтобы отлаживать кастомные матчеры:

  1. Проверять возвращаемое поле pass.
  2. Добавлять информативные сообщения в message.
  3. Логировать промежуточные значения, например текст элемента или список классов.
  4. Использовать elementHandles() для работы с коллекциями элементов.

Пример полезного логирования:

console.log('Содержимое элемента:', await element.textContent());

Лог помогает быстро понять, почему матчинг не проходит, особенно на динамических страницах с задержками загрузки контента.


Совмещение кастомных матчеров со стандартными

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

const button = page.locator('#submit');
await expect(button).toBeVisible(); // стандартный матчинг
await expect(button).toHaveCssClass('enabled'); // кастомный матчинг

Такой подход повышает стабильность тестов и упрощает диагностику ошибок.


Рекомендации по написанию кастомных матчеров

  • Ясность сообщений: message должен максимально точно описывать причину провала.
  • Асинхронность: всегда использовать async/await при работе с элементами.
  • Повторное использование: аргументы матчеров делают их универсальными.
  • Минимализм логики: избегать сложных условий внутри матчера — лучше делить проверки на отдельные функции.
  • Тестирование самого матчера: писать юнит-тесты для кастомного матчера, чтобы быть уверенным в корректной работе.

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