TL;DR
- Playwright — фреймворк автоматизации браузеров от Microsoft с auto-wait и встроенными assertions
- Поддерживает Chromium, Firefox и WebKit единым API
- TypeScript-first с отличной поддержкой IDE и генерацией кода
- Параллельное выполнение из коробки — запускает тесты быстрее Selenium или Cypress
- Trace viewer и запись видео для отладки упавших тестов
Подходит для: Команд, желающих современный инструментарий, поддержку TypeScript и быстрое параллельное выполнение Пропусти, если: Нужен Safari на реальных устройствах или есть большая существующая инфраструктура Selenium Время чтения: 15 минут
Твои Selenium-тесты выполняются 45 минут. Cypress-тесты не могут запускаться параллельно без оплаты облака. Тестировщики тратят часы на отладку flaky ожиданий.
Playwright решает эти проблемы. Auto-wait устраняет проблемы тайминга. Параллельное выполнение бесплатное и встроенное. Trace viewer показывает, что именно произошло, когда тест упал.
Что такое Playwright?
Playwright — open-source фреймворк автоматизации браузеров от Microsoft. Он управляет Chromium, Firefox и WebKit через единый API.
Ключевые возможности:
- Auto-wait — ждет, пока элементы станут доступными перед взаимодействием
- Web-first assertions — встроенная логика повторов для assertions
- Параллельное выполнение — запускает тесты на нескольких воркерах по умолчанию
- Trace viewer — отладка с путешествием во времени для упавших тестов
- Codegen — генерирует тесты записывая действия в браузере
Установка и настройка
Создание нового проекта
# Создать новый Playwright проект
npm init playwright@latest
# Ответь на вопросы:
# - TypeScript или JavaScript? → TypeScript
# - Куда положить тесты? → tests
# - Добавить GitHub Actions? → Yes
# - Установить браузеры? → Yes
Структура проекта
my-project/
├── tests/
│ └── example.spec.ts
├── playwright.config.ts
├── package.json
└── .github/
└── workflows/
└── playwright.yml
Конфигурация
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
timeout: 30000,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html', { open: 'never' }],
['list']
],
use: {
baseURL: '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', use: { ...devices['iPhone 14'] } },
],
});
Написание первого теста
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
test('user can login with valid credentials', 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();
// Assertions
await expect(page).toHaveURL('/dashboard');
await expect(page.getByText('Welcome back')).toBeVisible();
});
test('shows error for invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('wrong@example.com');
await page.getByLabel('Password').fill('wrongpass');
await page.getByRole('button', { name: 'Sign In' }).click();
await expect(page.getByText('Invalid credentials')).toBeVisible();
await expect(page).toHaveURL('/login');
});
Запуск тестов
# Запустить все тесты
npx playwright test
# Запустить конкретный файл
npx playwright test tests/login.spec.ts
# Запустить в headed режиме (видеть браузер)
npx playwright test --headed
# Запустить конкретный браузер
npx playwright test --project=chromium
# Режим отладки
npx playwright test --debug
# UI режим (интерактивный)
npx playwright test --ui
Локаторы: Поиск элементов
Playwright рекомендует user-facing локаторы вместо CSS/XPath селекторов.
Рекомендуемые локаторы
// По роли (основанные на accessibility)
page.getByRole('button', { name: 'Submit' })
page.getByRole('link', { name: 'Home' })
page.getByRole('textbox', { name: 'Email' })
page.getByRole('checkbox', { name: 'Remember me' })
// По label (поля форм)
page.getByLabel('Email')
page.getByLabel('Password')
// По placeholder
page.getByPlaceholder('Search...')
// По тексту
page.getByText('Welcome')
page.getByText('Welcome', { exact: true })
// По test ID (когда другие варианты не работают)
page.getByTestId('submit-button')
CSS и XPath (когда нужны)
// CSS селектор
page.locator('button.primary')
page.locator('[data-testid="submit"]')
page.locator('#login-form input[type="email"]')
// XPath
page.locator('xpath=//button[contains(text(), "Submit")]')
// Цепочка локаторов
page.locator('.card').filter({ hasText: 'Premium' }).getByRole('button')
Assertions
Assertions в Playwright автоматически повторяются до таймаута.
Распространенные assertions
import { test, expect } from '@playwright/test';
test('примеры assertions', async ({ page }) => {
await page.goto('/dashboard');
// Видимость
await expect(page.getByText('Welcome')).toBeVisible();
await expect(page.getByText('Loading')).toBeHidden();
// Текстовое содержимое
await expect(page.getByRole('heading')).toHaveText('Dashboard');
await expect(page.getByRole('heading')).toContainText('Dash');
// Атрибуты
await expect(page.getByRole('button')).toBeEnabled();
await expect(page.getByRole('button')).toBeDisabled();
await expect(page.getByRole('checkbox')).toBeChecked();
// URL и title
await expect(page).toHaveURL('/dashboard');
await expect(page).toHaveURL(/dashboard/);
await expect(page).toHaveTitle('My App - Dashboard');
// Количество
await expect(page.getByRole('listitem')).toHaveCount(5);
// Значение
await expect(page.getByLabel('Email')).toHaveValue('user@example.com');
});
Page Object Model
// pages/LoginPage.ts
import { Page, Locator, expect } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.getByLabel('Email');
this.passwordInput = page.getByLabel('Password');
this.submitButton = page.getByRole('button', { name: 'Sign In' });
this.errorMessage = page.getByRole('alert');
}
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();
}
async expectError(message: string) {
await expect(this.errorMessage).toContainText(message);
}
}
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test.describe('Login', () => {
test('successful login', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password123');
await expect(page).toHaveURL('/dashboard');
});
test('invalid credentials', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('wrong@example.com', 'wrong');
await loginPage.expectError('Invalid credentials');
});
});
Перехват сети
test('мокаем API ответ', async ({ page }) => {
// Мокаем API
await page.route('/api/users', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, name: 'Mock User', email: 'mock@example.com' }
])
});
});
await page.goto('/users');
await expect(page.getByText('Mock User')).toBeVisible();
});
test('блокируем запросы', async ({ page }) => {
// Блокируем аналитику
await page.route('**/*google-analytics*', route => route.abort());
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
await page.goto('/');
});
Отладка
Trace Viewer
# Включить traces
npx playwright test --trace on
# Просмотреть traces
npx playwright show-trace trace.zip
Режим отладки
# Пошаговое выполнение теста
npx playwright test --debug
# Пауза в конкретной точке
await page.pause();
Интеграция CI/CD
# .github/workflows/playwright.yml
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: 20
- name: Install dependencies
run: npm ci
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
AI-Assisted разработка Playwright
AI-инструменты хорошо интегрируются с читаемым API Playwright.
Что AI делает хорошо:
- Генерация тестов из user stories или требований
- Конвертация Selenium/Cypress тестов в Playwright
- Написание Page Object классов из HTML-структуры
- Создание assertions для сложной валидации данных
- Объяснение методов и паттернов API Playwright
Что всё ещё требует людей:
- Решения о стратегии и покрытии тестов
- Отладка визуальных или timing-связанных сбоев
- Выбор между стратегиями локаторов
- Оптимизация производительности для больших test suite
FAQ
Playwright лучше Selenium?
Playwright предлагает несколько преимуществ: auto-wait устраняет большинство проблем тайминга, выполнение быстрее благодаря коммуникации через протокол браузера, API более современный. Selenium имеет более широкую поддержку браузеров и большее сообщество. Для новых проектов Playwright обычно лучший выбор.
Playwright бесплатный?
Да, полностью. Playwright open-source под лицензией Apache 2.0. В отличие от Cypress, нет платных тарифов. Параллельное выполнение, trace viewer, запись видео — всё бесплатно. Единственная стоимость — твоя собственная CI-инфраструктура.
Может ли Playwright тестировать мобильные приложения?
Playwright тестирует мобильные веб-браузеры через эмуляцию устройств — симулирует viewport iPhone, Android и планшетов. Для нативных мобильных приложений (iOS/Android из app store) нужен Appium или платформенно-специфичные инструменты.
Какие языки поддерживает Playwright?
Playwright официально поддерживает TypeScript, JavaScript, Python, Java и C#. TypeScript/JavaScript имеют больше функций и лучшую документацию. Python отлично подходит для команд, уже использующих pytest.
Когда выбирать Playwright
Выбирай Playwright когда:
- Начинаешь новый проект автоматизации
- Команда использует TypeScript/JavaScript
- Нужно быстрое параллельное выполнение
- Хочешь современные инструменты отладки (trace viewer)
- Тестирование на Chromium, Firefox, WebKit
Рассмотри альтернативы когда:
- Нужен реальный Safari на macOS (Selenium + Safari)
- Большая существующая Selenium-инфраструктура
- Команда предпочитает Python/Java-first инструменты
Официальные ресурсы
Смотрите также
- Playwright: Полное Руководство - Продвинутые паттерны и лучшие практики
- Cypress vs Playwright - Детальное сравнение для выбора правильного инструмента
- Selenium Tutorial - Когда Selenium всё ещё правильный выбор
- Cypress Deep Dive - Глубокое погружение в Cypress
