Перейти к содержимому
Поиск
Разработка

Playwright: тестирование сайта — мастерская и 9 практических проверок

Проверяем сайт глазами пользователя: формы, поиск, файлы, мобильное меню и ошибки API. Включаем дефекты в мастерской, разбираем тесты и запускаем готовый проект Playwright.

Александр Некрасов
Playwright — проверяем сайт на практике: браузер, мобильная форма, проверки и отчёт об ошибке

Playwright позволяет управлять настоящим браузером кодом и проверять, что сайт работает так, как ожидает пользователь. Открыть страницу, заполнить форму, выбрать файл, подтвердить действие, дождаться результата и убедиться, что появилась нужная запись, — всё это можно оформить в повторяемый тест. Такой тест помогает обнаружить поломку после изменения интерфейса, обновления библиотеки или подключения нового API.

Здесь разберём Playwright Test на практических задачах веб-сервиса. Сначала объясним возможности и применение, затем попробуем девять интерфейсов с переключаемыми неисправностями. В каждом примере видно, какое поведение нужно проверить и почему одной надписи «Успешно» недостаточно. Исходники содержат настоящий тестовый проект: его можно скачать, запустить локально и адаптировать к своему приложению.

Что можно попробовать. Форма заявки, поиск, диалог подтверждения, выбор файла, CSV-выгрузка, мобильная навигация, сбой API, ожидание результата и изоляция настроек. На странице работает сам учебный интерфейс, а не движок Playwright. Автотесты запускаются из архива в Node.js или CI. Сохранённый результат прогона в мастерской подписан отдельно и не выдаётся за проверку вашего текущего клика.

Что умеет Playwright и что именно проверяет тест

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

Возможность Как выглядит проверка Где пригодится
Формы и элементы управления Заполнить поля, отправить форму, проверить итог Личный кабинет, CRM, заявка на услугу
Списки, таблицы и поиск Выбрать фильтр и сравнить найденные записи Каталог, заказы, сотрудники, история операций
Файлы и скачивания Выбрать файл, дождаться выгрузки и прочитать содержимое Импорт, документы, отчёты, фотографии
Сетевые ответы Проверить успешный ответ, ошибку и повтор операции Любой интерфейс, использующий API
Разные размеры и браузеры Повторить сценарий в нужной конфигурации Адаптивный сайт и веб-приложение
Отчёты и trace Увидеть шаг, на котором ожидание не выполнилось Разбор ошибки после локального или CI-прогона

Playwright Test объединяет запуск тестов, проверки expect, изолированные страницы и отчётность. Разработчик описывает пользовательский путь, а раннер подготавливает браузерное окружение и сообщает результат. Это удобно для регрессионных проверок: один раз зафиксировать важное поведение и повторять его после изменений.

Документация: Установка Playwright Test, Структура теста.

Где и зачем применять автотесты браузера

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

В интернет-магазине это может быть поиск товара, корзина и оформление тестового заказа. В CRM — создание заявки, смена статуса и просмотр результата в списке. В сервисе документов — загрузка учебного файла, обработка и получение готовой выгрузки. В административной панели — фильтры, права тестового пользователя и сохранение настройки. Проверяется не красота клика, а завершённая задача.

Автотесты полезны перед выпуском новой версии, в pull request, после обновления зависимостей и при исправлении ошибки. Если дефект удалось воспроизвести, его можно превратить в проверку: сначала тест падает на старом поведении, затем проходит после исправления. Так описание ошибки становится повторяемым способом убедиться, что она не вернулась.

Например, CRM «Сфера» на Vue содержит связанные пользовательские процессы, а Fastify показывает серверный контракт. Браузерный тест связывает эти стороны: выполняет действие через экран и проверяет результат, который действительно видит пользователь.

Документация: Структура теста, Запуск в CI.

Чем Playwright Test отличается от Puppeteer и ручной проверки

В статье о Puppeteer мы рассматривали браузер как программируемый инструмент: открыть сайт, пройти шаги, получить данные, PDF или скриншот. Playwright также умеет управлять браузером. Здесь акцент на Playwright Test: как превратить действия в независимые проверки и получить воспроизводимый ответ о качестве приложения.

Подход Основная задача Что остаётся за человеком
Ручная проверка Исследовать поведение и оценить удобство Замечать неожиданные проблемы и выбирать важные сценарии
Браузерная автоматизация Выполнить шаги и получить результат Определить, что делать и как использовать полученные данные
Playwright Test Повторить сценарий и проверить ожидаемое поведение Задать верные ожидания, данные и границы теста

Это не соревнование инструментов по одной характеристике. Для существующего проекта разумно учитывать уже написанные сценарии и опыт команды. Полезное отличие нашего учебного материала — явные утверждения, изоляция состояния, запуск нескольких конфигураций и разбор неудачного теста. Ручное исследование, проверка удобства и предметная экспертиза при этом сохраняют своё значение.

Документация: Установка Playwright Test, Изоляция браузерных контекстов, HTML и JSON отчёты.

Мини-мастерская: включите дефект и найдите потерянный результат

Выберите задачу, выполните предложенные действия в исправном режиме и сравните результат с ожидаемым. Затем включите неисправность и повторите те же шаги. Дефекты специально сделаны небольшими: интерфейс может выглядеть правдоподобно и даже сообщать об успехе, хотя итоговая операция не выполнена.

Открыть мастерскую на весь экран ↗Скачать проект с тестами ZIP ↓

Открыть мастерскую на весь экран ↗Скачать проект с тестами ZIP ↓

Рядом с каждым примером находятся шаги, смысл проверки и исходник теста. Переключатель неисправности меняет только учебную страницу. Он не ломает сайт, не удаляет рабочие записи и не отправляет пользовательские файлы на сервер. Для повторения используйте сброс сценария; тестовые данные не связаны с настоящими заказами.

На опубликованной странице вы взаимодействуете с обычным HTML и JavaScript. Playwright не запускается внутри WordPress или внутри кнопки мастерской. Настоящий раннер открывает этот интерфейс из скачанного проекта, выполняет действия и проверяет условия. Сохранённый отчёт позволяет изучить уже выполненный прогон; новый результат получают командой тестирования у себя.

Установка: проект, браузеры и первый запуск

Учебный проект закреплён на Playwright Test 1.65.0, проверенном 6 ноября 2026 года. Используйте поддерживаемую версию Node.js 22 или новее, совместимую с текущими требованиями Playwright. В нашем окружении применяется Node 24.19.0. Версия npm-пакета и версии управляемых браузеров связаны: после обновления Playwright браузеры нужно установить заново.

В корне распакованного проекта

node --version
npm --version
npm ci
npx playwright install chromium webkit
npm test

npm ci устанавливает зависимости из lock-файла. Следующая команда загружает браузерные сборки; они не включены в ZIP, поэтому первая установка требует сети и места на диске. В Linux для системных библиотек обычно используют npx playwright install --with-deps в подходящем окружении. Требования к ОС сверяйте с официальной инструкцией.

Один браузер, интерактивный режим и HTML-отчёт

npm run test:chromium
npm run test:ui
npm run report

Обычный тестовый запуск сам поднимает локальный стенд через настройку webServer. Если хотите сначала поиграть вручную, выполните npm start и откройте http://127.0.0.1:8794. Команды выполняются из папки с package.json. Ошибка о ненайденном исполняемом файле браузера обычно означает, что шаг playwright install ещё не выполнен для установленной версии пакета.

Если стенд уже запущен вручную, перед npm test остановите его через Ctrl+C. В архиве тестовый раннер специально создаёт свой сервер и не использует чужой процесс на том же порту. Это исключает незаметную проверку устаревшей копии приложения.

В архиве основной npm test запускает Chromium и WebKit. Firefox вынесен в отдельную команду: в нашей Windows-среде его официальная сборка не запустилась даже после переустановки, поэтому этот проект здесь не считается проверенным. Это ограничение конкретного прогона, а не отсутствие поддержки Firefox в Playwright; подробность сохранена в README и отчёте.

Дополнительная проверка Firefox в своём окружении

npx playwright install firefox
npm run test:firefox
# Все настроенные проекты, если браузеры запускаются в вашей среде:
npm run test:all

Документация: Установка Playwright Test, Запуск локального сервера.

Из чего состоит полезный тест: подготовка, действие, утверждение

  1. 1. ПодготовитьОткрыть нужный сценарий и задать известные учебные данные.
  2. 2. Выполнить действиеЗаполнить поле, нажать кнопку, выбрать файл или перейти в раздел.
  3. 3. Дождаться результатаПроверить запись, состояние, текст, URL либо содержимое файла.
  4. 4. Разобрать отклонениеПосмотреть ожидаемое и фактическое значение, шаг и trace.

Хорошее название теста описывает поведение: «отмена сохраняет черновик», «поиск оставляет только подходящие услуги», «после ошибки доступен повтор». Название «кнопка работает» не помогает понять, что именно сломалось. Ожидаемый результат лучше формулировать до написания локаторов: иначе тест может случайно закрепить ошибку текущей реализации.

Минимальный самостоятельный тест при настроенном baseURL

import { test, expect } from '@playwright/test';

test('страница мастерской объясняет назначение стенда', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveTitle(/Playwright/);
});

Здесь page предоставляет раннер. await нужен и для действия, и для ожидающей проверки. Успешное открытие страницы доказывает только доступность выбранного адреса. Оно не подтверждает сохранение заявки или правильность выгрузки; для каждой такой задачи нужны отдельные условия.

Документация: Структура теста, Fixtures и подготовка окружения.

Как находить элементы устойчиво: роль, подпись и контекст

Локатор описывает, какой элемент нужен тесту. Для кнопки удобно использовать роль и доступное имя, для поля — связанную подпись. Такой код ближе к тому, что видит человек, и меньше зависит от перестановки вложенных div или изменения декоративного CSS-класса. При каждом действии Playwright заново находит соответствующий элемент.

Пример сужения поиска до конкретной области

const form = page.getByRole('region', { name: 'Попробуйте интерфейс' });
const button = form.getByRole('button', { name: 'Создать заявку', exact: true });
await button.click();

Если одинаковых кнопок несколько, сначала найдите нужную карточку или диалог, затем элемент внутри него. Ошибка строгого локатора полезна: тест сообщает, что условие неоднозначно. Применять first() только ради исчезновения ошибки опасно — после изменения порядка он может нажать совсем другую кнопку. data-testid подходит для явного стабильного контракта там, где доступное имя не решает задачу.

Связанные label, настоящие button и понятные названия помогают и человеку, и тесту. Но успешные локаторы по роли не равны полному аудиту доступности: отдельно проверяют клавиатуру, фокус, сообщения об ошибках и работу вспомогательных технологий.

Документация: Локаторы по роли и метке, Проверка доступности.

Почему клик и ожидание результата — разные проверки

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

Ждём значение результата, а не произвольную паузу

await page.getByRole('button', { name: 'Сформировать отчёт', exact: true }).click();
await expect(page.getByTestId('report-count')).toHaveText('14');

Ожидающие проверки повторяют условие до успеха или таймаута. Вызов вроде expect(await locator.textContent()).toBe(...) проверяет уже полученную строку и сам по себе не ждёт дальнейшего изменения DOM. Для текста интерфейса обычно удобнее await expect(locator).toHaveText(...). Для более сложного повторяемого условия существует expect.poll.

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

Документация: Автоматические ожидания действий, Ожидающие утверждения.

Проверка 1. Форма: сообщение об успехе и новая заявка

Человек заполняет заявку, нажимает кнопку и хочет получить сохранённую запись. В исправном сценарии меняется список заявок. В режиме дефекта интерфейс сообщает об успехе, но запись не добавляется. Проверка только всплывающего уведомления пропустила бы эту ошибку: тест должен найти результат операции в предназначенном для него месте.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('form: Форма: проверить сохранение', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=form' + bug);
  await page.getByLabel('Имя клиента', {exact:true}).fill('Команда Кедр');
  await page.getByLabel('Email', {exact:true}).fill('cedar@example.test');
  await page.getByLabel('Услуга', {exact:true}).selectOption('audit');
  await page.getByRole('button', {name:'Создать заявку', exact:true}).click();
  await expect(page.getByText('Заявка создана', {exact:true})).toBeVisible();
  const created = page.getByTestId('created-request');
  await expect(created).toContainText('Команда Кедр');
  await expect(created).toContainText('cedar@example.test');
  await expect(created).toContainText('Аудит интерфейса');
});

Попробуйте сначала пустые поля, затем корректные учебные значения. Для настоящего сервиса полезно дополнить сценарий проверкой ошибок полей и сохранности ввода после отказа. Если сохранение происходит на сервере, отображение одной строки ещё не гарантирует долговечность: отдельный тест может перечитать запись после обновления страницы или проверить API.

Такой сценарий применим к регистрации обращения, созданию задачи, добавлению клиента и записи на услугу. Ожидание выбирают по смыслу операции. Если приложение должно вернуть номер заявки, проверяйте номер и данные; если оно только ставит задание в очередь — подтверждение приёма и доступность состояния задания. Не приравнивайте текст «готово» к любому требуемому результату.

Документация: Структура теста, Ожидающие утверждения.

Проверка 2. Поиск и фильтры: правильные записи вместо правдоподобного экрана

Фильтр может визуально переключиться, а таблица остаться прежней. Поэтому полезно проверять сами строки: какие записи должны остаться и какие должны исчезнуть. В учебном дефекте поисковое поле принимает текст, но поиск игнорируется. Внешне форма выглядит рабочей, однако человек получает неверную выдачу.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('filter: Поиск: проверить найденные строки', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=filter' + bug);
  const rows = page.getByTestId('request-row');
  await expect(rows).toHaveCount(6);
  await page.getByLabel('Найти заявку', {exact:true}).fill('Кедр');
  await expect(rows).toHaveCount(1);
  await expect(rows.first()).toContainText('Кедр');
});

Для каталога услуг удобно иметь небольшой известный набор: несколько подходящих записей и несколько неподходящих. Тогда проверка различает правильную фильтрацию, пустой результат и ошибку загрузки. Случайные данные из рабочей базы делают такой тест непредсказуемым: сегодня нужная запись существует, завтра её переименовали.

В таблицах Tabulator принцип тот же: проверять пользовательский результат фильтра, а не внутренний метод библиотеки. Если список виртуализирован, не считайте невидимые строки в DOM полной базой. Проверяйте видимую страницу, счётчик и переходы согласно контракту конкретного интерфейса.

Документация: Локаторы по роли и метке, Ожидающие утверждения.

Проверка 3. Подтверждение и отмена: ничего не потерять случайно

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

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('dialog: Диалог: отменить и подтвердить', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=dialog' + bug);
  const draft = page.getByTestId('draft-item');
  await page.getByRole('button', {name:'Удалить черновик', exact:true}).click();
  const dialog = page.getByRole('dialog');
  await expect(dialog).toBeVisible();
  await dialog.getByRole('button', {name:'Отмена', exact:true}).click();
  await expect(dialog).not.toBeVisible();
  await expect(draft).toContainText('Коммерческое предложение');
  await page.getByRole('button', {name:'Удалить черновик', exact:true}).click();
  await dialog.getByRole('button', {name:'Да, удалить', exact:true}).click();
  await expect(draft).not.toBeVisible();
});

Проверяйте также возврат фокуса к инициатору, возможность закрыть окно клавишей Escape, когда это предусмотрено интерфейсом, и отсутствие случайной отправки внешней формы. Внутри диалога локаторы следует ограничивать его областью, особенно если на странице уже есть кнопки с теми же названиями.

HTML-диалог приложения отличается от встроенного браузерного window.confirm. Для системных JavaScript-диалогов у Playwright есть событие dialog, а для обычного окна в DOM работают стандартные локаторы. Выбирайте механизм по фактическому интерфейсу. В учебном примере проверяется окно самого приложения.

Документация: Локаторы по роли и метке, Действия и загрузка файлов.

Проверка 4. Выбор файла: проверить то, что выбрал пользователь

Сценарий загрузки начинается с файла известного имени и содержимого. Через setInputFiles можно передать тестовый файл полю выбора без автоматизации системного окна. В мастерской файл остаётся в браузере, а интерфейс показывает его сведения. Дефект теряет имя: действие выбора произошло, но результат представлен неверно.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('upload: Файл: проверить выбранный документ', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=upload' + bug);
  await page.getByLabel('Учебный файл', {exact:true}).setInputFiles({
    name:'synthetic.txt', mimeType:'text/plain',
    buffer:Buffer.from('Only synthetic educational content.\n', 'utf8')
  });
  await expect(page.getByTestId('file-name')).toHaveText('synthetic.txt');
});

Для реальной загрузки на сервер проверок потребуется больше: допустимые типы и размеры, ошибка сети, отказ обработчика, повтор и подтверждение сохранения. Атрибут accept помогает человеку выбрать файл, но не заменяет серверную проверку. Тест браузера и тест обработчика проверяют разные участки одного процесса.

Храните рядом с тестами маленькие синтетические файлы: текст, изображение или CSV без личной информации. Не используйте случайный документ из папки Downloads. Если элемент input скрыт за кнопкой, можно дождаться события filechooser до клика, а затем передать файл. Ожидание события готовят заранее, чтобы не пропустить быстрый ответ.

Документация: Действия и загрузка файлов.

Проверка 5. Скачивание CSV: файл должен содержать все данные

Наличие скачивания ещё не доказывает правильность отчёта. Файл может иметь нужное имя, но содержать только заголовок, часть строк или HTML страницы ошибки. В нашем примере неисправный экспорт пропускает последнюю запись. Поэтому тест ожидает событие скачивания и проверяет содержимое полученного файла.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('download: Скачивание: проверить содержимое CSV', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=download' + bug);
  const downloadPromise = page.waitForEvent('download');
  await page.getByRole('button', {name:'Скачать CSV', exact:true}).click();
  const download = await downloadPromise;
  expect(download.suggestedFilename()).toBe('requests.csv');
  const {readFile} = await import('node:fs/promises');
  const csv = (await readFile(await download.path(), 'utf8')).replace(/^\uFEFF/, '');
  expect(csv.split(/\r?\n/).filter(Boolean)).toHaveLength(4);
  expect(csv).toContain('RQ-103');
  expect(csv).toContain('Бюро Маяк');
});

В зависимости от формата проверяют имя, кодировку, заголовки колонок, число записей и ключевые значения. Для CSV важно учитывать разделитель, кавычки и переносы строк; в серьёзном сценарии используйте подходящий парсер. Для PDF или таблицы потребуется чтение соответствующего формата, а визуальное оформление проверяют отдельно.

Начните ожидать download перед нажатием кнопки. Скачанные файлы привязаны к жизненному циклу браузерного контекста; если их нужно сохранить как артефакт, перенесите их в предназначенную папку до закрытия контекста. Не записывайте результат параллельных тестов в одно общее имя: используйте путь, уникальный для проверки.

Документация: Скачивание файлов.

Проверка 6. Мобильное меню: открыть, выбрать раздел и увидеть содержимое

На узком экране навигация часто меняет поведение: ссылки скрываются за кнопкой, появляется панель, после выбора раздела её нужно закрыть. В учебном дефекте меню открывается, но выбранный пункт не меняет содержимое. Один скриншот открытого меню не выявляет проблему; нужен полный путь до целевого раздела.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('navigation: Мобильное меню: перейти и закрыть', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=navigation' + bug);
  await page.setViewportSize({width:390, height:844});
  const menu = page.getByRole('button', {name:'Меню разделов', exact:true});
  await expect(menu).toHaveAttribute('aria-expanded', 'false');
  await menu.click();
  await page.getByRole('link', {name:'Отчёты', exact:true}).click();
  await expect(page.getByTestId('navigation-page')).toHaveText('Отчёты сервиса');
  await expect(menu).toHaveAttribute('aria-expanded', 'false');
});

Проверяйте не только наличие пункта, но и итог: активный раздел, заголовок, URL или видимую панель. Для мобильной версии полезны проверки отсутствия горизонтального переполнения, доступности основной кнопки и корректного закрытия меню. Клавиатурный сценарий показывает, можно ли пройти тот же путь без мыши.

Размер viewport и профиль устройства помогают воспроизвести часть мобильных условий. Это не физический смартфон: реальные клавиатуры, жесты, производительность, системные окна и особенности конкретного Safari требуют отдельного осмотра. В публикации и отчёте важно называть именно тот уровень проверки, который был выполнен.

Документация: Эмуляция устройств.

Проверка 7. Сбой API и повтор: приложение должно честно сообщить об ошибке

Сеть — удобное место для управляемого эксперимента. Тест может подменить один ответ API на 503, проверить сообщение и кнопку повтора, затем вернуть успешный JSON. Так проверяется редкая, но важная ветка без необходимости ломать настоящий сервер или ждать случайного сбоя. Остальная страница продолжает работать в обычном браузере.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('network: Сеть: ошибка и повтор', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=network' + bug);
  let attempts = 0;
  await page.route('**/api/demo-status.json', async route => {
    attempts++;
    await route.fulfill({status:attempts===1?503:200,
      contentType:'application/json',
      body:JSON.stringify(attempts===1
        ? {ok:false, message:'Учебный сбой'}
        : {ok:true, message:'Сервис доступен', requests:14})
    });
  });
  await page.getByRole('button', {name:'Проверить соединение', exact:true}).click();
  await expect(page.getByText('Не удалось загрузить данные', {exact:true})).toBeVisible();
  await page.getByRole('button', {name:'Повторить', exact:true}).click();
  await expect(page.getByText('Сервис доступен', {exact:true})).toBeVisible();
  expect(attempts).toBe(2);
});

Учебная неисправность забывает проверить response.ok и сообщает об успехе при ошибочном HTTP-статусе. Это распространённая логическая ловушка: fetch не бросает исключение только из-за ответа 404 или 503. Интерфейс должен различать успешные данные, ошибку ответа и недоступную сеть.

Подмена через route.fulfill подтверждает поведение клиента при заданном ответе. Она не доказывает исправность настоящего API. Для полного процесса нужны отдельные интеграционные проверки с тестовым сервером. Настраивайте перехват до действия, которое вызывает запрос, и делайте шаблон URL достаточно узким, чтобы случайно не заменить другую операцию.

Документация: Сеть и перехват запросов, Подмена ответов API.

Проверка 8. Асинхронный результат: закончилась ли работа на самом деле

Отчёт, расчёт или обработка документа не всегда появляются мгновенно. Пользователь должен видеть состояние ожидания, а затем результат или объяснение ошибки. В учебном дефекте индикатор исчезает и надпись сообщает о готовности, но рассчитанные данные не выводятся. Проверка должна связать завершение с самим результатом.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('waiting: Ожидание: дождаться результата', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=waiting' + bug);
  const button = page.getByRole('button', {name:'Сформировать отчёт', exact:true});
  await button.click();
  await expect(page.getByText('Отчёт готов', {exact:true})).toBeVisible();
  await expect(page.getByTestId('report-count')).toHaveText('14');
  await expect(button).toBeEnabled();
});

Для такой задачи подходят ожидающие утверждения по нужному тексту или значению. Полезно также убедиться, что повторное нажатие во время работы не создаёт несколько одинаковых операций, если интерфейс обещает защиту от дублей. Проверки времени до миллисекунды обычно хрупки: важнее допустимый предел и наблюдаемое состояние.

Не путайте таймаут проверки с производительностью сервиса. Если тест дождался результата за четыре секунды при лимите пяти, это ещё не означает, что скорость приемлема для продукта. Ожидаемое время работы и нагрузку оценивают отдельными измерениями. Здесь задача теста — обнаружить отсутствие правильного завершения.

Документация: Автоматические ожидания действий, Ожидающие утверждения.

Проверка 9. Изоляция: настройки одного пользователя не должны заражать другой тест

Тест, который проходит только после другого теста, сложно поддерживать. Предыдущий сценарий мог оставить cookie, localStorage, выбранный фильтр или авторизацию. Playwright Test создаёт отдельный браузерный контекст для теста, что помогает начинать с известного состояния. Но данные общей серверной базы от этого автоматически не сбрасываются.

Тест из исходников мастерской

import {test, expect} from '@playwright/test';

test('isolation: Сессии: проверить независимое состояние', async ({page, browser, baseURL}, testInfo) => {
  const bug = testInfo.project.metadata.bug ? '&bug=1' : '';
  await page.goto('/?case=isolation' + bug);
  const density = page.getByRole('checkbox', {name:'Плотный вид', exact:true});
  await density.check();
  await page.reload();
  await expect(density).toBeChecked();
  const fresh = await browser.newContext();
  try {
    const other = await fresh.newPage();
    await other.goto(new URL('/?case=isolation' + bug, baseURL).href);
    await expect(other.getByRole('checkbox', {name:'Плотный вид', exact:true})).not.toBeChecked();
  } finally {
    await fresh.close();
  }
});

В мастерской настройка плотности сохраняется для текущего учебного браузерного состояния. Новый контекст должен получить исходный вариант. В режиме дефекта неверное значение становится начальным даже в чистом окружении. Это позволяет показать важную разницу: изоляция убирает наследование данных, но не исправляет ошибку значения по умолчанию.

Для настоящего приложения используйте отдельные тестовые сущности, уникальные имена и понятную очистку. Два параллельных теста не должны изменять одну и ту же заявку или учётную запись без намеренного сценария конкуренции. Свежий контекст — только одна часть изоляции; API, очередь и БД тоже входят в границы окружения.

Документация: Изоляция браузерных контекстов.

Конфигурация: baseURL, проекты и локальный сервер

Повторяющиеся настройки выносят в playwright.config.js: адрес стенда, браузеры, таймауты, отчёт и сбор артефактов. Тогда тесты используют относительные адреса и остаются читаемыми. Несколько проектов позволяют выполнить один сценарий в разных браузерных конфигурациях, не копируя его содержимое.

Упрощённая конфигурация основного набора; Firefox вынесен отдельно

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  use: {
    baseURL: 'http://127.0.0.1:8794',
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure'
  },
  reporter: [['list'], ['html', { open: 'never' }]],
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } }
  ],
  webServer: {
    command: 'npm start',
    url: 'http://127.0.0.1:8794/health',
    reuseExistingServer: false
  }
});

Установка профиля Desktop Safari означает запуск WebKit в выбранной конфигурации, а не тестирование физического iPhone или установленного пользовательского Safari. Названия проектов нужны для фильтрации: например, --project=chromium. Учебный стенд слушает loopback-адрес и не предназначен для общего доступа из сети.

Документация: Конфигурация проектов, Запуск локального сервера, Эмуляция устройств.

Fixtures и тестовые данные: подготовить нужное и убрать за собой

Fixture предоставляет тесту ресурс и управляет его жизненным циклом: страницу, контекст, подготовленного пользователя или учебную запись. Это помогает убрать повторяющуюся подготовку из каждого сценария. При этом смысл проверки должен оставаться видимым: слишком сложная скрытая подготовка делает падение трудным для понимания.

В маленьком проекте достаточно встроенных page, context и небольших вспомогательных функций. Для более крупного сервиса удобно создавать данные через тестовый API, а важный пользовательский путь проходить через интерфейс. Не требуется каждый раз создавать десять вспомогательных сущностей кликами, если сами эти действия сейчас не проверяются.

Не используйте рабочие аккаунты и настоящие заказы в демонстрационных сценариях. Синтетические имена, фиксированные маленькие файлы и известные ответы ускоряют разбор. Даты и часовой пояс тоже являются данными: если тест зависит от «сегодня», заранее определите, какой день он должен считать текущим и в каком регионе.

Документация: Fixtures и подготовка окружения, Изоляция браузерных контекстов.

Как читать падение: HTML-отчёт, скриншот и trace

Когда тест падает, сначала смотрите на его смысл и ожидаемое значение. Что должно было произойти? На каком шаге ожидание не выполнилось? В HTML-отчёте удобно открыть конкретную проверку, браузер, ошибку и вложения. Trace помогает восстановить последовательность действий и состояние страницы вокруг проблемного шага.

Отдельный учебный прогон с включёнными неисправностями

npm run test:bugs
npx playwright show-report playwright-report-bugs

# Открыть конкретный trace.zip из папки результатов:
npx playwright show-trace path/to/trace.zip

npm run test:bugs специально запускает проверки против неисправного поведения и должен завершаться неуспешным кодом. Это учебная демонстрация обнаружения ошибок. Обычный npm test проверяет исправный режим. Не добавляйте намеренно падающую команду в обязательную успешную сборку без отдельной обработки её ожидаемого результата.

Результат подготовленного прогона: 18 успешных проверок исправного стенда — девять сценариев в Chromium и девять в WebKit. Отдельно обнаружены 9 из 9 намеренных дефектов в Chromium: проверки завершились на ожидаемых утверждениях о неверном результате. Firefox в число успешных проверок не включён. Это сохранённый прогон учебного проекта, который можно повторить у себя.

Скриншот показывает внешний вид в один момент, а trace связывает действия, ожидания и сетевой обмен. Артефакты могут содержать текст страницы, запросы и данные пользователя. Для открытого архива используйте только синтетический стенд; рабочие traces и сохранённое состояние авторизации не следует автоматически публиковать вместе с исходниками.

Скачать trace учебного сбоя: это сохранённая запись настоящей проверки синтетического интерфейса. Распаковывать ZIP не нужно; откройте его командой ниже или через локальный Trace Viewer.

Если trace скачан в текущую папку

npx playwright show-trace example-failure-trace.zip

Документация: Просмотр trace, HTML и JSON отчёты, Состояние авторизации.

Повторы, параллельность и нестабильные тесты

Повторный запуск полезен для диагностики, но зелёный результат со второй попытки не доказывает, что всё в порядке. Если тест падает периодически, ищите причину: неопределённые данные, гонка, общий ресурс, неправильное ожидание или реальная нестабильность приложения. Не превращайте большое число retries в способ скрыть проблему.

Параллельные проверки экономят время, когда не мешают друг другу. Если каждая меняет один и тот же объект, скорость превращается в случайные ошибки. Разделяйте данные по тестам, используйте независимые контексты и настраивайте число workers под ресурсы среды. Сначала добейтесь воспроизводимого небольшого набора, затем ускоряйте его.

Упрощённый запуск и повтор для исследования нестабильности

npx playwright test --project=chromium --workers=1
npx playwright test --project=chromium --repeat-each=3

Повтор трёх запусков не является доказательством отсутствия всех гонок. Это способ получить дополнительное наблюдение при конкретной проблеме. В отчётах различайте passed, failed и flaky; для важных сценариев стоит разбирать нестабильность даже тогда, когда повтор позволил сборке завершиться.

Документация: Повторы и нестабильные тесты, Параллельное выполнение.

Как включить проверки в CI и рабочий процесс команды

В CI тесты выполняются в подготовленном окружении после изменения кода. Минимальная последовательность знакома: установить npm-зависимости, установить браузеры и системные библиотеки, запустить проверки, сохранить отчёт при ошибке. Конкретный YAML зависит от платформы; сами команды проекта остаются одинаковыми.

Основные шаги Linux CI-окружения

npm ci
npx playwright install --with-deps
npm test

Начните с нескольких критичных сценариев, которые дают понятный сигнал перед выпуском. Например: форма сохраняет запись, поиск показывает нужную строку, отмена ничего не удаляет, выгрузка содержит данные. Время прогона и полезность результата важнее количества проверок ради красивой цифры. Невоспроизводимый тест, который все привыкли игнорировать, почти не защищает продукт.

Отчёт следует сохранять даже при неуспешной команде, иначе у разработчика останется только краткая строка в журнале. Секреты окружения передают через средства CI, а не в репозитории. Для тестового сервера выбирайте отдельные учётные данные и данные, которые можно безопасно пересоздать.

Документация: Запуск в CI, HTML и JSON отчёты, Состояние авторизации.

Генератор тестов, скриншоты и границы автоматизации

Codegen помогает быстро получить первые локаторы и последовательность действий: вы работаете с интерфейсом, инструмент записывает заготовку. После записи нужно проверить смысл, убрать случайные действия, задать учебные данные и добавить утверждения о полезном результате. Записанный клик сам по себе не знает, что важнее для вашего сервиса.

Запись заготовки на своём локальном стенде

# Сначала запустите npm start в отдельном терминале.
npx playwright codegen http://127.0.0.1:8794

Сравнение скриншотов полезно для обнаружения изменений оформления, но требует стабильного окружения: шрифтов, данных, размеров и анимаций. Функциональный тест может пройти при неудобной компоновке, а визуальная разница может быть ожидаемой. Обновлять эталон следует после осмотра изменения, а не только ради зелёного статуса.

Автотесты не заменяют все виды проверки. Они не доказывают безопасность сервера, правильность любого бизнес-правила, отсутствие проблем доступности или удобство реального мобильного устройства. Для Alpine.js, Vue и обычного JavaScript подход одинаков: проверять важный путь, а затем осматривать то, что алгоритм не умеет оценить за человека.

Документация: Генератор тестов, Сравнение скриншотов, Проверка доступности.

Частые ошибки и что проверить в первую очередь

Симптом Возможная причина Что сделать
Элемент найден несколько раз Слишком широкий локатор Уточнить имя, роль и область поиска
Кнопка нажалась, но тест прошёл зря Нет проверки результата Добавить утверждение о записи, значении или файле
Работает только с большой паузой Ожидание не связано с готовностью Ждать наблюдаемый результат через expect
После другого теста результат меняется Общее состояние или данные Проверить контексты, fixtures и серверные записи
API503 показан как успех Клиент не проверил HTTP-статус Проверять response.ok и ветку ошибки
Файл скачался, но отчёт неверный Проверяется только событие Прочитать содержимое и сравнить ключевые данные
Браузер не запускается Не установлены нужные сборки или библиотеки ОС Повторить install для текущего Playwright и проверить требования
Тест прошёл на узком экране Проверена только эмуляция Отдельно осмотреть реальные устройства и нужные браузеры

Для разбора ошибки сохраните один воспроизводимый сценарий: исходное состояние, действия, ожидаемый результат и фактический ответ. Уменьшите число переменных, запустите один проект и откройте trace. Если дефект обнаружен, добавьте проверку именно нарушенного поведения, а не случайного текста вокруг него.

Документация: Локаторы по роли и метке, Ожидающие утверждения, Просмотр trace.

Исходники и маршрут дальнейшей практики

Начните с формы и намеренно включите её неисправность. Затем проверьте поиск, отмену, файл и скачивание. Перейдите к мобильной навигации, сетевой ошибке, ожиданию и изоляции. Для каждого примера сформулируйте своими словами, какой пользовательский результат защищает тест. После этого запустите исправный набор и отдельную демонстрацию дефектов из архива.

Открыть мастерскую на весь экран ↗Скачать проект с тестами ZIP ↓

В проект входят учебный интерфейс, тесты, локальный сервер, конфигурации, фиксированные зависимости, README и лицензии. Это основа для своей задачи: замените сущности и ожидаемые результаты, сохраните независимость данных и проверяйте полезный итог. Не переносите учебные дефекты или публичные тестовые настройки в рабочий сервис.

Основные официальные источники: документация Playwright, репозиторий Microsoft Playwright и пакет Playwright Test. Ссылки на конкретные возможности размещены рядом с примерами. Перед обновлением пакета переустановите соответствующие браузеры и повторите тесты.