Playwright: обзор

Playwright — E2E testing framework от Microsoft с поддержкой Chromium, Firefox и WebKit из одного API, встроенным авто-ожиданием и мощными инструментами для отладки.

Зачем нужно

Playwright решает главные проблемы E2E тестирования: авто-ожидание встроено в каждое действие, кроссбраузерность из коробки, встроенные инструменты трассировки и скриншотов. TypeScript поддерживается нативно без дополнительных плагинов.

Где используется

  • E2E тесты для веб-приложений с поддержкой нескольких браузеров
  • Component testing для React/Vue/Angular
  • API тестирование через request fixture
  • Visual regression testing в связке со snapshot assertions

Основной контент

Установка

npm init playwright@latest
# Создаёт playwright.config.ts, example tests, .github/workflows

# Или в существующий проект
npm install --save-dev @playwright/test
npx playwright install  # скачивает браузеры

Базовый тест

// tests/example.spec.ts
import { test, expect } from '@playwright/test';

test('главная страница имеет заголовок', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle(/Example/);
});

test('форма поиска работает', async ({ page }) => {
  await page.goto('/');
  await page.getByPlaceholder('Поиск...').fill('playwright');
  await page.getByRole('button', { name: 'Найти' }).click();
  await expect(page.getByTestId('search-results')).toBeVisible;
});

Ключевые локаторы

// Приоритет: accessibility-first подход
page.getByRole('button', { name: 'Submit' })    // по роли
page.getByLabel('Email')                          // по label
page.getByPlaceholder('Enter email')              // по placeholder
page.getByText('Sign in')                         // по тексту
page.getByTestId('submit-btn')                    // по data-testid
page.locator('[data-testid="submit"]')            // CSS/XPath

playwright.config.ts

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',

  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',  // трассировка при retry
    screenshot: 'only-on-failure',
  },

  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
    { name: 'mobile',   use: { ...devices['iPhone 14'] } },
  ],
});

Fixture для авторизации

// tests/fixtures.ts
import { test as base } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';

export const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await loginPage.goto;
    await loginPage.login(process.env.TEST_EMAIL!, process.env.TEST_PASSWORD!);
    await use(page);
  },
});

// В тесте
test('защищённая страница', async ({ authenticatedPage: page }) => {
  await page.goto('/dashboard');
  await expect(page.getByTestId('user-menu')).toBeVisible;
});

Отладка

npx playwright test --debug           # пошаговый дебаггер
npx playwright codegen http://localhost:3000  # запись тестов
npx playwright show-report            # HTML-отчёт
npx playwright show-trace trace.zip   # просмотр трассировки

Частые ошибки

  • page.waitForTimeout вместо assertions — заменяй все sleep на expect(locator).toBeVisible — Playwright ждёт сам
  • Нет baseURL в конфиге — в каждом тесте приходится писать полный URL; задай один раз в конфиге
  • Запуск без --project — по умолчанию запускает все браузеры; в разработке используй --project chromium
  • Тест создаёт данные без cleanup — используй test.afterEach или API-сброс для изоляции

Связанные темы

Ресурсы