Playwright управляет Chromium, Firefox и WebKit через единый API и запускает тесты параллельно. В отличие от простой записи кликов, он ждёт готовности элемента, сохраняет трассировку и умеет проверять не только интерфейс, но и API. Ниже — рабочий каркас проекта, который не разваливается на первом же медленном ответе сервера.

Установка и структура

Нужен Node.js LTS. Инициализация создаёт конфиг, пример теста и папку tests; браузеры скачиваются отдельно, потому что в пакет npm они не входят.

node --version
npm init playwright@latest
npx playwright install --with-deps chromium firefox webkit
npx playwright --version

На Linux --with-deps доустанавливает системные библиотеки; без него Chromium может не запуститься в минимальном контейнере. Для CI обычно ставят только нужный браузер, чтобы не тратить минуты на остальные.

Конфигурация

Конфиг — обычный TypeScript. В нём задаются базовый URL, повторы, число воркеров и артефакты. Трассировка на первом повторе даёт максимум пользы при минимуме диска.

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

export default defineConfig({
  testDir: './tests',
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : undefined,
  reporter: [['html', { open: 'never' }], ['list']],
  use: {
    baseURL: 'http://127.0.0.1:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
});

baseURL позволяет писать page.goto('/') вместо полного адреса. Повторы не лечат нестабильные тесты, но отделяют случайные сетевые сбои от реальных ошибок; если тест падает на втором повторе, смотрите трассировку, а не увеличивайте retries.

Первый тест и локаторы

Локаторы по роли и тексту ближе к тому, что видит пользователь, и меньше ломаются при смене вёрстки. CSS-классы вроде .btn-primary меняются чаще, чем подпись кнопки.

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

test('поиск открывает результат', async ({ page }) => {
  await page.goto('/');
  await page.getByRole('searchbox', { name: 'Поиск' }).fill('curl');
  await page.getByRole('button', { name: 'Найти' }).click();
  await expect(page.getByRole('heading', { name: /curl/i })).toBeVisible();
});

Playwright сам ждёт появления элемента и кликабельности, поэтому waitForTimeout не нужен и вреден: он замедляет прогон и маскирует гонку. Если действие не срабатывает, проверьте, не перекрыт ли элемент, и используйте expect(...).toBeVisible() вместо ручного ожидания.

Запуск, отладка и кодогенерация

npx playwright test
npx playwright test tests/search.spec.ts --project=chromium --headed
npx playwright test --ui
npx playwright codegen https://example.com
npx playwright show-report

--ui открывает режим с таймлайном и повторным запуском; --debug останавливает выполнение и открывает инспектор. codegen удобен для черновика, но сгенерированные селекторы почти всегда надо заменять на роли и getByLabel. HTML-отчёт хранит шаги, скриншоты и трассировку — прикладывайте его к задаче, а не пересказывайте падение словами.

API, моки и авторизация

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

test('API отдаёт список', async ({ request }) => {
  const response = await request.get('/api/items');
  expect(response.status()).toBe(200);
  const body = await response.json();
  expect(Array.isArray(body.items)).toBe(true);
});

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

npx playwright codegen --save-storage=auth.json https://example.com
# затем в конфиге: use: { storageState: 'auth.json' }

Файл состояния содержит cookies и токены, поэтому не коммитьте его и не передавайте третьим лицам.

Медленные ответы и фикстуры

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

test('ошибка API показывает сообщение', async ({ page }) => {
  await page.route('**/api/items', route =>
    route.fulfill({ status: 500, contentType: 'application/json', body: '{}' })
  );
  await page.goto('/items');
  await expect(page.getByText('Не удалось загрузить')).toBeVisible();
});

Для проверки обрыва используйте route.abort('failed'), для медленного ответа — задержку внутри обработчика. Общие действия выносите в фикстуры через test.extend: так авторизация, создание записи и уборка данных описываются один раз и не дублируются в каждом тесте. Фикстура должна чистить за собой данные, иначе повторный запуск на том же стенде начнёт падать.

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

CI и стабильность

- uses: actions/setup-node@v4
  with:
    node-version: 22
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test

В CI запускайте тесты против поднятого стенда или через webServer в конфиге: Playwright сам стартует команду и ждёт указанный URL. Сохраняйте playwright-report и test-results как артефакты. Один браузер на джобу экономит время; кроссбраузерный прогон выносите в отдельное расписание. Нестабильность чаще всего связана с общими данными между тестами, зависимостью от порядка и ожиданием фиксированных пауз.

В блоке webServer для локального запуска удобно держать reuseExistingServer: !process.env.CI: на машине разработчика используется уже открытый сервер, а в CI поднимается новый. Порог по времени задавайте на уровне теста, а не глобально, иначе медленный, но исправный сценарий начнёт падать на слабой машине.

  • Каждый тест создаёт свои данные и не полагается на соседей.
  • Проверяйте пользовательский сценарий, а не реализацию.
  • Для динамических списков используйте toHaveCount и toContainText с понятными ожиданиями.
  • Секреты — только через переменные окружения CI.

Итог

Playwright даёт предсказуемый запуск в трёх движках, автоожидания, трассировку и единый подход к UI и API. Начните с одного браузера и пяти ключевых сценариев, затем добавьте CI и отчёты. Главное — писать тесты от действий пользователя, изолировать данные и разбирать трассировку первого падения, а не прятать проблему повторами.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте