Web-first assertions

Проверки в Playwright тоже умеют ждать — они повторяют попытку, пока условие не станет верным.

Web-first assertion — это проверка через expect(locator), которая автоматически повторяется до тех пор, пока ожидаемое условие не выполнится или не выйдет таймаут.

Обычная проверка против web-first

Если бы проверка выполнялась мгновенно, она бы упала на асинхронном вебе: текст ещё не подгрузился. Рассмотрим неправильный и правильный варианты.

// ПЛОХО: получаем текст «прямо сейчас», без ожидания
const text = await page.getByRole('heading').textContent();
expect(text).toBe('Привет');  // упадёт, если текст ещё не появился

// ХОРОШО: web-first assertion ждёт нужный текст
await expect(page.getByRole('heading')).toHaveText('Привет');

Во втором случае Playwright будет периодически перепроверять элемент, пока в нём не появится «Привет» (или не истечёт таймаут). Это убирает целый класс флаки-тестов, которые возникают из-за чтения ещё не готового состояния.

Важная деталь: await

Web-first assertions всегда с await, потому что они асинхронные (ждут). А вот проверки обычных значений (чисел, строк, которые уже у вас в руках) — без await. Перепутать легко, а ошибка коварная: без await проверка «как будто проходит», но на деле не дожидается результата.

await expect(locator).toBeVisible();   // ждёт → await
expect(2 + 2).toBe(4);                 // мгновенно → без await

Главные проверки

ПроверкаЧто утверждает
toBeVisible()элемент виден на странице
toBeHidden()элемент скрыт или отсутствует
toHaveText('...')текст элемента точно равен
toContainText('...')текст содержит подстроку
toHaveValue('...')значение поля ввода
toBeEnabled() / toBeDisabled()элемент активен / неактивен
toBeChecked()чекбокс отмечен
toHaveCount(n)столько-то элементов в наборе
toHaveURL('...')адрес страницы

Примеры из жизни

// после отправки формы видно сообщение об успехе
await page.getByRole('button', { name: 'Отправить' }).click();
await expect(page.getByText('Спасибо за заявку')).toBeVisible();

// поле email содержит введённое значение
await expect(page.getByLabel('Email')).toHaveValue('user@example.com');

// кнопка отправки заблокирована, пока форма не заполнена
await expect(page.getByRole('button', { name: 'Отправить' })).toBeDisabled();

Отрицание через not

Любую проверку можно инвертировать через .not — например, убедиться, что баннер с ошибкой исчез:

await expect(page.getByText('Ошибка')).not.toBeVisible();

Почему это снижает флаки

Web-first assertions — это второй (после авто-ожиданий действий) столп устойчивости Playwright. Вместо «считай состояние сейчас и сравни» они говорят «дождись, пока состояние станет таким». Большинство нестабильных тестов в других инструментах ломаются именно на мгновенных проверках текста, которого ещё нет. Правило простое: предпочитайте expect(locator).что-то() ручному чтению значений через textContent() и сравнению.

Итог

  • Web-first assertion (expect(locator)) повторяет проверку, пока условие не станет верным.
  • Такие проверки всегда с await; проверки готовых значений — без.
  • Ключевые: toBeVisible, toHaveText, toHaveValue, toBeEnabled, toHaveURL.
  • Они убирают флаки, возникающие из-за мгновенного чтения ещё не готового состояния.
Проверьте себя
1. Чем web-first assertion отличается от обычного сравнения значений?
AОна выполняется мгновенно и не ждёт
BОна работает только с числами
CОна автоматически повторяется, пока условие не станет верным или не выйдет таймаут
DОна не требует локатора
2. Почему перед expect(locator).toBeVisible() ставят await?
AЭто синтаксическая формальность без смысла
BБез await тест выполнится дважды
Cawait ускоряет проверку
DПроверка асинхронная — она ждёт нужного состояния
3. Какая проверка убедится, что текст элемента точно равен 'Привет'?
Aexpect(locator).toHaveText('Привет')
Bexpect(locator).toBeVisible()
Cexpect(locator).toHaveCount('Привет')
Dexpect(locator).toBeEnabled()