Утро, кофе, открываешь GitLab, а CI красный. Классика. Лезешь в отчёт, а там портянка на 5 экранов и TimeoutError где-то в недрах клика по кнопке. Селектор нормальный, data-testid="checkout-submit". Чтобы понять, что именно сломалось (может отвалилась база или фронт не отрисовал кнопку, возможно, юзер тупит), нужно лезть в код теста и дебажить глазами.

Бесит нереально. Ты или твой коллега тратите время не на фикс багов, а на расследование.

Классический POM: Как мы жили раньше?

Обычно все начинается красиво. Вы пишете чистый Page Object.

import { Page } from '@playwright/test';
export class CartPage {
  // Добавляем конструктор
  constructor(private readonly page: Page) {}
  async clickCheckout() {
    // Атомарное действие. Никакой логики, только клик. 
    await this.page.getByTestId('checkout-submit').click();
  }
}

В коде все красиво. Вызовы методов, чистота, порядок. А в отчете - ад.

На небольшом примере все понятно и без усложнений, но масштабируйте до ваших тестов
На небольшом примере все понятно и без усложнений, но масштабируйте до ваших тестов

Как по такому списку понять контекст за 5 секунд? Это нереально. Разработчик лезет в код теста, читает его весь, матерится, восстанавливает контекст в голове. Время уходит.

Почему просто пихать test.step в тесты - это путь в ад?

Сначала вам посоветуют: "Да просто используй test.step в коде, чего паришься?". Не делайте так. На 3-х тестах это прокатит. На 100 вы закопаете проект.

  • Копипаста убьет все. Цепочка "Авторизация -> Корзина" будет в большинстве файлов. Поменялась логика логина? Поздравляю, нужно править 50 файлов руками.

  • Ад поддержки. Добавили в чекаут галочку "Согласен с офертой"? Идите и вставляйте await page.click(...) в сотню мест.

  • Код превратится в кашу. Тест на 10 строк раздуется до 50 из-за постоянных await test.step(...). Вы просто потеряете суть проверки за этим шумом.

Решение: Слой Flows (Бизнес-сценарии)

Выход один: нужна прослойка. Слой между "тупыми" страницами (POM) и тестами (Flows). Flow - это дирижер. Он вообще не знает про селекторы (.click(), .fill()). Он знает только про бизнес-процесс.

import { test, expect } from '@playwright/test';
import { CartPage, PaymentPage, OrderData } from './types'; 
export class CheckoutFlow {
  // Dependency Injection: Flow принимает готовые инстансы страниц
  constructor(private cartPage: CartPage, private paymentPage: PaymentPage) {}
  @Step('Пользователь оформляет заказ')
  async completePurchase(orderData: OrderData) {
    await test.step('Переход к оплате', async () => {
      await this.cartPage.clickCheckout();
      // Бизнес-проверка: перешли ли мы на нужную страницу?
      await expect(this.paymentPage.form).toBeVisible(); 
    });
    await test.step('Заполнение платежных данных', async () => {
      // Данные (orderData) приходят извне: из фабрик или фикстур.
      // Никакого хардкода карт или имен внутри Flow!
      await this.paymentPage.fillDetails(orderData.card);
      await this.paymentPage.submit();
    });
  }
}

Что в отчете?

Все. Теперь там действия юзера, а не браузера.

Удобнее читать, не правда ли?
Удобнее читать, не правда ли?

Тест упал? Разраб смотрит отчет. 30 секунд и он понял, на каком шаге бизнес-логики затык.

Как мы это закодили (никакой магии)?

Чтобы не писать тонны бойлерплейта (и не сойти с ума), внедрили две вещи:

  1. Dependency Injection (DI). Используем фикстуры Playwright как DI-контейнер. Flows регистрируем один раз в конфиге. Playwright сам создает страницы, кидает их в конструктор Flow и отдает готовый объект в тест. new CheckoutFlow(...) в каждом файле? Забудьте.

  2. Декоратор @Step. Та самая "магия". Оборачивает метод класса в test.step, имя берет из декоратора. Код Flows остается чистым.

Как написать такой декоратор и не сломать this с типизацией, тема для отдельной статьи. Там хардкор, на пальцах не объяснишь.

Итого

Автотесты пишутся один раз, а читаются постоянно разными людьми. Ассерты отвечают на вопрос «что именно сломалось», а Flows — на вопрос «на каком бизнес‑шаге это произошло».

Хотите, чтобы разрабы и менеджеры не игнорировали ваши отчеты? Сделайте их читаемыми. Разделение на POM и Flows — это не просто «красивая архитектура», это инструмент, который окупается в первый же месяц за счёт того, что вы перестаете тратить часы на расшифровку красных крестиков.