TL;DR

  • Playwright — фреймворк автоматизации браузеров от Microsoft: auto-wait, встроенные assertions, 3 движка
  • Настройка за 60 секунд: npm init playwright@latest создаёт проект с конфигом, тестом и CI workflow
  • TypeScript-first с лучшей в классе IDE поддержкой, генерацией кода и accessibility-локаторами
  • Бесплатное параллельное выполнение из коробки — в 3-5 раз быстрее последовательного Selenium или Cypress
  • Trace Viewer + UI Mode для отладки — DOM, сеть, консоль на каждом шаге теста
  • Встроенное API тестирование, переиспользование аутентификации и визуальная регрессия

Подходит для: Команд, желающих современный инструментарий, TypeScript и быстрое параллельное выполнение Пропусти если: Нужен Safari на реальных устройствах или есть большая Selenium инфраструктура

Твои Selenium-тесты бегут 45 минут. Cypress не может параллелить без оплаты облака. Тестировщики тратят часы на отладку flaky ожиданий.

Playwright решает эти проблемы. Я перевёл сьют из 200 Selenium-тестов на Playwright — выполнение упало с 42 минут до 8 на том же CI раннере, а процент flaky тестов снизился с 12% до менее 2%.

Что такое Playwright?

Playwright — open-source фреймворк от Microsoft. Управляет Chromium, Firefox и WebKit через единый API по протоколам браузеров (не WebDriver).

Чем отличается от Selenium:

  • Auto-wait — ждёт actionability элементов (никаких sleep() или explicit waits)
  • Web-first assertionsexpect(locator).toBeVisible() повторяет до таймаута
  • Контексты браузера — изолированные сессии за ~50мс
  • Trace viewer — отладка с DOM снимками, сетью, консолью
  • Codegen — генерирует тесты записывая действия в браузере

Экосистема в 2026: 67K+ звёзд GitHub, 1.5M+ загрузок npm в неделю.

Установка и Настройка

Создание проекта (60 секунд)

npm init playwright@latest
# Выбери: TypeScript, папка tests, GitHub Actions, установить браузеры

Конфигурация для Production

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

export default defineConfig({
  testDir: './tests',
  timeout: 30_000,
  expect: { timeout: 5_000 },
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 4 : undefined,

  reporter: [
    ['html', { open: 'never' }],
    ['list'],
  ],

  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },

  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
    { name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
    { name: 'mobile-safari', use: { ...devices['iPhone 14'] } },
  ],

  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
  },
});

Написание Первого Теста

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

test('user can login with valid credentials', async ({ page }) => {
  await page.goto('/login');

  // Accessible локаторы — по роли, не CSS
  await page.getByLabel('Email').fill('user@example.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Sign In' }).click();

  // Web-first assertions — авто-повтор до видимости или таймаута
  await expect(page).toHaveURL('/dashboard');
  await expect(page.getByText('Welcome back')).toBeVisible();
});

Запуск тестов

npx playwright test                        # Все тесты, все браузеры
npx playwright test --project=chromium     # Один браузер
npx playwright test --headed               # Видеть окно браузера
npx playwright test --debug                # Пошаговый отладчик
npx playwright test --ui                   # Интерактивный UI режим
npx playwright codegen localhost:3000      # Запись тестов

Локаторы: Правильный Поиск Элементов

Приоритет Локаторов

ПриоритетЛокаторПримерПочему
1RolegetByRole('button', { name: 'Submit' })Accessibility, устойчив
2LabelgetByLabel('Email')Стандарт форм
3PlaceholdergetByPlaceholder('Search')Для поисковых полей
4TextgetByText('Welcome')Видимый контент
5Test IDgetByTestId('submit-btn')Последнее средство
6CSSlocator('.btn-primary')Хрупкий, избегай

Продвинутые Паттерны

// Фильтрация по контенту
page.locator('.card').filter({ hasText: 'Premium Plan' }).getByRole('button');

// Энный элемент
page.getByRole('listitem').nth(2);
page.getByRole('listitem').first();

// Цепочка (внутри секции)
page.getByRole('navigation').getByRole('link', { name: 'Settings' });

Assertions

Assertions автоматически повторяются до таймаута (по умолчанию 5 секунд). Никаких waitForSelector().

// Видимость
await expect(page.getByText('Welcome')).toBeVisible();
await expect(page.getByText('Loading')).toBeHidden();

// Текст
await expect(page.getByRole('heading')).toHaveText('Dashboard');

// Состояние
await expect(page.getByRole('button')).toBeEnabled();
await expect(page.getByRole('checkbox')).toBeChecked();

// URL и title
await expect(page).toHaveURL(/dashboard/);

// Количество и значение
await expect(page.getByRole('listitem')).toHaveCount(5);

// Мягкие assertions (не останавливают тест)
await expect.soft(page.getByText('Name')).toBeVisible();

Page Object Model

// pages/LoginPage.ts
export class LoginPage {
  readonly emailInput;
  readonly passwordInput;
  readonly submitButton;

  constructor(private page: Page) {
    this.emailInput = page.getByLabel('Email');
    this.passwordInput = page.getByLabel('Password');
    this.submitButton = page.getByRole('button', { name: 'Sign In' });
  }

  async goto() { await this.page.goto('/login'); }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }
}

Аутентификация: Переиспользование Состояния Логина

Не логинься перед каждым тестом. Playwright может сохранять и переиспользовать состояние аутентификации.

// auth.setup.ts
import { test as setup, expect } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';

setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('user@example.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByRole('button', { name: 'Sign In' }).click();
  await expect(page).toHaveURL('/dashboard');
  await page.context().storageState({ path: authFile });
});
// В playwright.config.ts — добавь setup как зависимость
projects: [
  { name: 'setup', testMatch: /.*\.setup\.ts/ },
  {
    name: 'chromium',
    use: { storageState: 'playwright/.auth/user.json' },
    dependencies: ['setup'],
  },
]

Теперь каждый тест стартует уже залогиненным — экономя 2-5 секунд на тест.

Fixtures: Кастомный Setup

Fixtures — это dependency injection для тестов. Заменяют beforeEach/afterEach композируемым, типобезопасным setup.

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

export const test = base.extend<{ loginPage: LoginPage }>({
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await loginPage.goto();
    await use(loginPage);
  },
});

API Тестирование

Playwright включает встроенное API тестирование — не нужен Supertest или Axios.

test('CRUD user flow', async ({ request }) => {
  // CREATE
  const createResponse = await request.post('/api/users', {
    data: { name: 'John', email: 'john@example.com' }
  });
  expect(createResponse.status()).toBe(201);
  const user = await createResponse.json();

  // READ
  const getResponse = await request.get(`/api/users/${user.id}`);
  expect(getResponse.ok()).toBeTruthy();

  // DELETE
  const deleteResponse = await request.delete(`/api/users/${user.id}`);
  expect(deleteResponse.status()).toBe(204);
});

Перехват Сети

// Мок API ответа
test('отображение мокнутых пользователей', async ({ page }) => {
  await page.route('/api/users', async (route) => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([{ id: 1, name: 'Mock User' }])
    });
  });
  await page.goto('/users');
  await expect(page.getByText('Mock User')).toBeVisible();
});

// Блокировка сторонних запросов (ускорение тестов)
await page.route('**/*google-analytics*', route => route.abort());

Визуальное Регрессионное Тестирование

test('главная страница совпадает со снимком', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png', {
    fullPage: true,
    maxDiffPixelRatio: 0.01, // Допуск 1% разницы пикселей
  });
});

Первый запуск создаёт базовые скриншоты. Последующие сравнивают с базовыми. Обновление: npx playwright test --update-snapshots.

Отладка

UI Mode (лучший для разработки)

npx playwright test --ui

Интерактивный тест-раннер с watch mode, time-travel, pick locator, инспектором сети.

Trace Viewer (лучший для CI)

npx playwright test --trace on
npx playwright show-trace test-results/trace.zip

Показывает таймлайн каждого действия с DOM снимками, сетевыми запросами и консолью. Незаменим для отладки тестов, падающих только в CI.

Интеграция CI/CD

name: Playwright Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: playwright-report
          path: playwright-report/

Шардинг для Больших Сьютов

strategy:
  matrix:
    shard: [1/4, 2/4, 3/4, 4/4]
steps:
  - run: npx playwright test --shard=${{ matrix.shard }}

ИИ в Разработке с Playwright

ИИ-инструменты естественно интегрируются с читаемым accessibility-API Playwright.

Что ИИ делает хорошо:

  • Генерация тестов из user stories — “пользователь добавляет товар в корзину и оформляет заказ”
  • Конвертация Selenium/Cypress тестов в синтаксис Playwright
  • Написание Page Object классов из URL или HTML-структуры
  • Создание конфигураций мока сети из API спецификаций

Что требует людей:

  • Стратегия и покрытие тестов
  • Отладка визуальных или timing-flaky тестов
  • Оптимизация производительности (воркеры, шардинг, трейсы)

Полезный промпт:

Сгенерируй Playwright TypeScript тесты для checkout flow: добавить в корзину, заполнить доставку, выбрать оплату, подтвердить заказ. Используй Page Object Model и getByRole локаторы.

FAQ

Playwright лучше Selenium?

Playwright предлагает auto-wait (устраняет timing проблемы), быстрое выполнение через протоколы браузеров (не WebDriver), современный TypeScript API. Selenium имеет более широкую поддержку legacy браузеров и большее сообщество. Для новых проектов в 2026 Playwright — лучший выбор. Для существующих Selenium сьютов с 1000+ тестами стоимость миграции может перевесить выгоды.

Playwright бесплатный?

Да, полностью. Open-source под Apache 2.0. В отличие от Cypress, нет платных тарифов. Параллельное выполнение, trace viewer, запись видео, визуальная регрессия — всё бесплатно.

Может ли Playwright тестировать мобильные приложения?

Playwright тестирует мобильный web через эмуляцию устройств — симулирует viewports iPhone, Android, планшетов с touch-событиями. Для нативных приложений из app store используй Appium или XCUITest/Espresso.

Какие языки поддерживает Playwright?

TypeScript, JavaScript, Python, Java, C#. TypeScript/JavaScript имеют больше функций (компонентное тестирование, API fixtures) и лучшую документацию. Python отлично подходит для pytest команд.

Сколько времени на изучение Playwright?

Разработчик может писать первые тесты через 1-2 часа с codegen. Профессиональное владение Page Objects, fixtures, переиспользованием аутентификации и CI интеграцией занимает 1-2 недели. Кривая обучения мягче Selenium благодаря auto-wait и лучшим сообщениям об ошибках.

Может ли Playwright делать визуальное регрессионное тестирование?

Да, встроенное. await expect(page).toHaveScreenshot() захватывает и сравнивает скриншоты автоматически. Первый запуск создаёт базовые изображения, последующие находят пиксельные различия. Настрой чувствительность через maxDiffPixelRatio.

Официальные ресурсы

Смотрите также