Playwright auto-wait: почему вам не нужны explicit waits
Если ты пришёл в Playwright из Selenium — первое инстинктивное движение писать await page.waitForSelector(...) перед каждым действием. В 90% случаев это лишняя работа: Playwright уже ждёт за тебя. Но многие об этом не знают и тащат привычки из старого мира.
Что такое auto-wait
Каждое действие на Locator (click, fill, type, check) перед выполнением проводит серию actionability checks — проверяет, что элемент:
- attached в DOM
- visible
- stable (не двигается, не анимируется)
- receives events (не перекрыт другим элементом)
- enabled
Если условие не выполнено — Playwright ждёт до 30 секунд по дефолту, потом фейлит. Никакого waitFor писать не надо.
Что сломает auto-wait
— Использование старого page.click('selector') вместо page.locator('selector').click(). Работает, но без полного набора actionability checks. Всегда через Locator API.
— Custom-overlay (loading spinner с pointer-events: none) перекрывает кнопку, и Playwright «думает» что элемент кликабельный. Auto-wait не помогает — нужно явно: await page.locator('.spinner').waitFor({ state: 'hidden' }).
— Анимация делает элемент «нестабильным» дольше 30 секунд. Решение: animation: none !important в test environment, либо локальный { timeout: 60_000 }.
Что использовать вместо waitFor
— expect(locator).toBeVisible() — встроенный retry до timeout. Чище чем waitForSelector.
— expect(locator).toHaveText('...') — ждёт пока текст совпадёт. Polling вручную не нужен.
— page.waitForResponse(/api\/data/) — ждать конкретный сетевой ответ.
— page.waitForURL('/dashboard') — ждать навигации.
Антипаттерны
❌ page.waitForTimeout(2000) — это Thread.sleep, только в Playwright. В CI всегда либо мало, либо много. Используй только для дебага, удаляй перед коммитом.
❌ Кастомный poll-loop через setInterval — Playwright делает это нативно.
❌ expect(await locator.textContent()).toBe(...) — это синхронная проверка без retry. Заменяй на await expect(locator).toHaveText(...).
Что делать прямо сейчас
✅ Прогрепай проект на waitForSelector и waitForTimeout( — это кандидаты на удаление или замену.
✅ Переходи на web-first assertions (expect(locator)) везде, где есть expect(await locator.x()).
✅ Включи trace: 'on-first-retry' в playwright.config.ts — даёт offline-отладку с timeline всех auto-wait.
Подробнее: Playwright — Auto-waiting, Best Practices.