ФАЗА 48. E2E Ожидание и утверждения

Expect ждёт условие вместо фиксированной паузы

Web-first · Auto-wait · Timeout

Web-first assertions повторяют проверку до результата или таймаута, действия ждут готовность элемента, явное условие описывает асинхронность.

1. Sleep скрывает гонку

1 / 5

Фиксированная пауза не гарантирует ответ и тратит время при быстром сценарии, медленная среда всё равно падает.

  • Условие лучше времени.
  • Медленный тест не стабильнее.
  • Причина ожидания названа.
sleep(5000)expect условие

2. expect(locator) повторяет утверждение

2 / 5

toBeVisible, toHaveText и toHaveURL ждут нужное состояние до успеха или предела, ошибка содержит последнее фактическое.

  • Проверка конкретна.
  • Ожидание ограничено.
  • Отрицание toNot имеет смысл.
LocatorExpect conditionВидимый результат

3. Действие ждёт готовность элемента

3 / 5

click, fill и check ждут видимость, стабильность и доступность, после чего выполняют событие, навигация ожидается отдельно.

  • Disabled не кликается.
  • Перекрытие обрабатывается.
  • Force не используется без причины.
Элемент готовДействиеСледующее состояние

4. Сетевое и кастомное условие ждутся явно

4 / 5

waitForResponse связывает действие с конкретным запросом, expect.poll проверяет произвольное значение, ожидание идёт по контракту.

  • Response ожидаемый.
  • Функция условия проста.
  • Старый ответ не подменяет новый.
ДействиеResponse или pollAssertion

5. Таймаут настраивается по типу работы

5 / 5

Короткий UI получает быстрый предел, тяжёлый запрос отдельный больший, глобальное увеличение не скрывает зависание.

  • Ошибка содержит время.
  • Медленный сценарий известен.
  • Предел обоснован.
UI timeoutBackend timeout

Утверждения и ожидание в Playwright

← назад к разделу

Самый частый источник нестабильных тестов — тайминги: тест проверяет элемент раньше, чем тот появился на странице. Классическое «лечение» — добавить паузу — только ухудшает ситуацию. Playwright решает проблему иначе: его утверждения и действия умеют ждать самостоятельно.

Почему sleep не работает

Интерфейс асинхронен: после клика данные загружаются, элементы появляются с задержкой. Тест, который сразу проверяет результат, вступает в гонку с браузером — иногда успевает, иногда нет. Тест начинает падать «через раз».

Первый порыв — поставить паузу:

await page.waitForTimeout(1000); // подождём секунду...
await expect(page.getByText("Заказ оформлен")).toBeVisible();

Проблема двойная. На быстрой машине теряешь секунду зря — результат давно готов. На медленной или под нагрузкой секунды может не хватить — и тест снова нестабильный. Фиксированная пауза не привязана к реальному состоянию приложения.

Web-first утверждения

Playwright решает это иначе. Утверждение на локаторе повторяется автоматически до тех пор, пока не выполнится или не истечёт таймаут. Это не «проверить прямо сейчас», а «дождаться, что станет так».

пауза клик ждать 1000 мс проверить: иногда рано web-first клик expect повторяет до 5 с проверить, когда стало так

Фиксированная пауза не знает, готов ли интерфейс; web-first утверждение перепроверяет условие само и заканчивается, как только оно выполнилось.

await expect(page.getByText("Заказ оформлен")).toBeVisible();
await expect(page.getByRole("row")).toHaveCount(3);
await expect(page.getByLabel("Итого")).toHaveText("4990 ₽");

toBeVisible() будет перепроверять видимость элемента снова и снова, пока он не появится или не истечёт таймаут (по умолчанию 5 секунд). Не нужно ни паузы, ни ручного ожидания — утверждение само синхронизируется с интерфейсом.

Это и называется web-first: проверки знают про асинхронную природу браузера.

Часто используемые web-first утверждения:

УтверждениеЧто проверяет
toBeVisible()элемент виден на странице
toBeHidden()элемент скрыт или отсутствует
toHaveText(text)текстовое содержимое совпадает
toHaveValue(value)значение поля ввода совпадает
toHaveCount(n)количество найденных элементов
toBeEnabled() / toBeDisabled()кнопка активна или заблокирована
toBeChecked()чекбокс отмечен

Авто-ожидание действий

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

await page.getByRole("button", { name: "Оплатить" }).click();

Не нужно явно проверять «появилась ли кнопка» перед кликом — click() сделает это сам. Это убирает горы проверок-ожиданий, которые приходилось писать вручную в старых инструментах автоматизации.

Тот же принцип работает у fill(), selectOption(), check() и других действий.

Когда нужно явное ожидание

Авто-ожидание не покрывает всё. Иногда нужно дождаться чего-то, что не выражается через состояние элемента, — например, конкретного сетевого ответа.

await Promise.all([
  page.waitForResponse((r) => r.url().includes("/api/orders") && r.ok()),
  page.getByRole("button", { name: "Оформить" }).click(),
]);

Здесь тест ждёт именно условия (пришёл нужный ответ с кодом 200), а не фиксированного времени. Promise.all связывает клик и ожидание ответа вместе — это исключает гонку между отправкой запроса и началом ожидания.

Похожий приём — waitForURL(), когда нужно убедиться, что браузер перешёл на нужную страницу:

await page.getByRole("button", { name: "Войти" }).click();
await page.waitForURL("**/dashboard");

Настройка таймаута

По умолчанию web-first утверждения ждут 5 секунд. Это можно изменить глобально в конфигурации или для конкретного утверждения:

// для одного утверждения
await expect(page.getByText("Отчёт готов")).toBeVisible({ timeout: 15_000 });

Увеличивать таймаут стоит осторожно: если тест стабильно ждёт 10 секунд — возможно, проблема в самом приложении, а не в тесте.

Коротко

  • Фиксированные паузы (sleep, waitForTimeout) делают тесты медленными и всё равно нестабильными — их не должно быть в e2e.
  • Web-first утверждения (expect(locator).toXxx()) повторяются автоматически, пока условие не выполнится или не истечёт таймаут.
  • Действия (click, fill и др.) тоже ждут готовности элемента — явная проверка перед действием не нужна.
  • Для ожидания условий, не связанных с DOM, используй waitForResponse(), waitForURL() и подобные — ждут конкретного события, а не времени.
  • Если возник соблазн поставить паузу — значит, надо выразить ожидание через условие.

Что почитать дальше