Почему зелёный линтер не гарантирует надежность Playwright-тестов: строгий ESLint и правила код-ревью
В автоматизированном тестировании веб-приложений успешное прохождение CI-пайплайна не всегда гарантирует надежность тестов. Фреймворк Playwright предоставляет встроенные механизмы автоожидания (auto-waiting) и веб-проверок с повторными попытками (web-first assertions). Однако на практике разработчики часто обходят эти защитные барьеры, внедряя в код слепые паузы, принудительные клики и одноразовые проверки состояния.
Главная проблема заключается в том, что стандартный рекомендованный пресет плагина eslint-plugin-playwright маркирует ключевые антипаттерны как предупреждения (warning). В CI-сборщике предупреждения не блокируют мёрж Pull Request, если в проекте не установлен флаг --max-warnings 0. В результате код со скрытыми гонками попадает в основную ветку, а тесты начинают «флакать» (случайно падать) при ночных прогонах.
В этом материале показано, как перевести статический анализ Playwright-тестов в строгий режим контроля через ESLint Flat Config и внедрить практический чек-лист для код-ревью.
Три уровня защиты тестового пайплайна
Для построения устойчивого тестового пайплайна необходимо разделять зоны ответственности на трех уровнях:
- Базовый статический анализ: стандартный ESLint не знает специфики тестовых фреймворков. Он не отличит оставленный в коде отладочный
test.only, вызовpage.pause(), пропущенныйawaitили явную задержку времени. - Мягкая политика линтинга (Recommended Preset): плагин
eslint-plugin-playwrightустановлен, но работает в стандартном режиме. Авторы плагина оставляют часть правил на уровнеwarning, чтобы избежать ложных срабатываний в специфичных проектах. Без локального переопределения правил опасные паттерны пропускаются в репозиторий. - Смысловая логика сценария: статический анализ не способен проверить бизнес-логику. Линтер не проверяет, объявлен ли перехватчик сети
page.waitForResponse()до клика по кнопке или вызвана ли проверка после полной стабилизации DOM-дерева.
Статический анализ служит автоматическим фильтром синтаксических ошибок, а смысловую корректность и изоляцию данных обеспечивают правила код-ревью и архитектура тестов.
Механика Playwright: автоожидание и веб-проверки
Перед выполнением каждого действия (например, locator.click()) Playwright автоматически выполняет серию проверок доступности (actionability checks):
- Привязка к селектору: элемент присутствует в DOM-дереве.
- Видимость: элемент отображается на экране, не скрыт
display: noneи имеет размер. - Стабильность: элемент завершил анимацию и не перемещается.
- Получение событий: элемент не перекрыт оверлеем или спиннером.
- Включенность: у элемента отсутствует атрибут
disabled.
Если условие не выполняется, Playwright повторно проверяет элемент до истечения таймаута (по умолчанию 30 секунд).
Передача опции { force: true } принудительно отключает эти проверки. Клиентский клик отправляется по координатам элемента, даже если поверх него находится оверлей. Если за этим кликом следует пауза page.waitForTimeout(3000), тест становится хрупким.
Применение вызова expect(await locator.isVisible()).toBe(true) ломает логику фреймворка. Это одноразовый замер (non-retrying assertion). Если элемент отрисуется через 50 миллисекунд после вызова, проверка сразу вернет false. Взамен следует применять асинхронные проверки await expect(locator).toBeVisible(), которые повторно запрашивают состояние до таймаута.
Настройка строгой конфигурации ESLint Flat Config
Для проектов с современным ESLint 9+ на основе Flat Config рекомендуется явно задать правила для тестовых директорий.
Пример конфигурации eslint.config.js:
import playwright from "eslint-plugin-playwright";
export default [
...playwright.configs["flat/recommended"],
{
files: ["tests/**/*.{ts,tsx}", "e2e/**/*.{ts,tsx}"],
rules: {
"playwright/no-wait-for-timeout": "error",
"playwright/no-force-option": "error",
"playwright/expect-expect": "error",
"playwright/missing-playwright-await": "error",
"playwright/prefer-web-first-assertions": "error",
"playwright/no-focused-test": "error",
"playwright/no-skipped-test": "warn",
},
},
];
Проверьте применимость правил через команду npx eslint --print-config tests/example.spec.ts. Если в тесте необходимо отступить от правила, используйте точечный комментарий // eslint-disable-next-line playwright/no-force-option с текстовым пояснением причины.
Порядок внедрения и валидации
Порядок перевода тестового проекта на строгие стандарты:
- Изоляция области: ограничьте применение плагина
eslint-plugin-playwrightтолько файлами.spec.tsили.test.ts. - Базовый аудит: запустите
npx eslint tests/и зафиксируйте список текущих предупреждений. - Рефакторинг кода:
- Замените
page.waitForTimeout(N)на веб-проверкуawait expect(locator).toBeVisible()или ожидание сетиpage.waitForResponse(). - Замените
expect(await locator.isVisible()).toBe(true)наawait expect(locator).toBeVisible(). - Удалите опцию
{ force: true }, скорректировав селекторы.
- Замените
- Включение ошибок: переведите правила в статус
'error'вeslint.config.js. - Проверка CI: создайте тестовый файл с паузой
page.waitForTimeout(1000)и убедитесь, что CI блокирует сборку.
Спецификации доступны в руководстве Playwright Best Practices, разделах Actionability, Test Assertions и в репозитории eslint-plugin-playwright.
Сравнение подходов: хрупкий vs устойчивый код
Пример рефакторинга сценария отправки формы.
Ненадёжный подход:
test("Отправка формы (ненадёжно)", async ({ page }) => {
await page.goto("/contact");
await page.getByRole("button", { name: "Отправить" }).click({ force: true });
await page.waitForTimeout(3000);
const isSuccess = await page.getByText("Заявка принята").isVisible();
expect(isSuccess).toBe(true);
});
Устойчивый подход:
test("Отправка формы (надёжно)", async ({ page }) => {
await page.goto("/contact");
const responsePromise = page.waitForResponse(
(resp) => resp.url().includes("/api/contact") && resp.status() === 200
);
await page.getByRole("button", { name: "Отправить" }).click();
await responsePromise;
await expect(page.getByRole("status")).toHaveText("Заявка принята");
});
Устойчивый вариант декларирует целевой результат: при сетевой задержке в 4 секунды веб-проверка дождет ответа в рамках таймаута.
Чек-лист для проведения код-ревью
Блокирующие ошибки (Blocker / Error)
- Наличие
page.waitForTimeout()без диагностического обоснования. - Использование принудительных кликов
click({ force: true })без комментария. - Отсутствие
awaitперед веб-ассертамиexpect(locator).... - Попадание в коммит отладочных инструкций
test.only()илиpage.pause(). - Тесты с действиями над UI, но без проверок
expect.
Критические замечания (Major)
- Использование одноразовых проверок
expect(await locator.isVisible()).toBe(true)вместоawait expect(locator).toBeVisible(). - Объявление
page.waitForResponse()после выполнения клика. - Использование
try/catchдля скрытия падений шагов. - Хрупкие CSS-селекторы вместо ролей
getByRole()или атрибутовgetByTestId().
Рекомендации (Minor)
- Использование
test.skip()без ссылки на задачу в трекере. - Отсутствие сброса тестовой базы данных перед запуском сценария.
- Дублирование логики авторизации вместо использования сохраненной сессии (storage state).
Заключение
Автоматизация приносит пользу только при доверии к результатам CI. Перевод правил eslint-plugin-playwright в статус ошибок устраняет опасные паттерны на этапе написания кода, а чек-лист код-ревью защищает тесты от скрытых гонок. Это снижает расходы на поддержку автотестов и избавляет инженеров от ручных перезапусков падающих сборок.

![Node.JS [ru]](/api/digests/it_development/daily/20260726/assets/sources/we-use-js.jpg)