Puppeteer и Playwright (как обсуждается в Playwright Comprehensive Guide: Multi-Browser Testing, Auto-Wait, and Trace Viewer Mastery) представляют современное поколение инструментов автоматизации браузеров, оба произошли от команд в Google и Microsoft соответственно. В то время как Puppeteer был пионером высокоуровневой автоматизации Chrome DevTools Protocol (CDP), Playwright (как обсуждается в Cypress Deep Dive: Architecture, Debugging, and Network Stubbing Mastery) появился как мультибраузерное решение, устраняющее ограничения Puppeteer. Этот всесторонний анализ сравнивает оба инструмента по архитектуре, функциям, производительности и практическим случаям использования для информирования вашей стратегии автоматизации.

Введение

Автоматизация браузеров значительно эволюционировала с эры WebDriver. Puppeteer (как обсуждается в Percy, Applitools & BackstopJS: Visual Regression Testing Solutions Compared), выпущенный Google в 2017 году, продемонстрировал, что прямой доступ к CDP может обеспечить более быструю и надежную автоматизацию, чем инструменты на основе WebDriver. Опираясь на эту основу, команда Playwright от Microsoft (многие бывшие контрибьюторы Puppeteer) запустила Playwright в 2020 году с амбициозными целями: истинная поддержка кросс-браузерности, улучшенные возможности отладки и более опинионированный фреймворк тестирования.

Ключевые Вопросы, на Которые Отвечает Эта Статья:

  • Каковы фундаментальные архитектурные различия?
  • Когда следует выбирать Puppeteer или Playwright?
  • Как они сравниваются в реальных сценариях?
  • Каковы соображения по миграции?

Исторический Контекст и Происхождение

Генезис Puppeteer

Выпуск: Январь 2017 командой Google Chrome

Первоначальные Цели:

  • Предоставить высокоуровневый API поверх Chrome DevTools Protocol
  • Включить автоматизацию headless Chrome
  • Поддержать веб-скрейпинг и генерацию PDF
  • Предложить более простую альтернативу Selenium

Эволюция:

  • Изначально только Chrome
  • Добавлена поддержка Firefox (экспериментальная) через протокол Juggler
  • Сосредоточен на Chrome/Chromium как основной цели
  • Поддерживается командой Google Chrome

Появление Playwright

Выпуск: Январь 2020 Microsoft

Предыстория Команды:

  • Возглавляется бывшими основными контрибьюторами Puppeteer
  • Инженеры, которые создали Puppeteer в Google

Философия Дизайна:

  • Кросс-браузерность с первого дня (Chromium, Firefox, WebKit)
  • Создан для тестирования, а не просто автоматизации
  • Комплексная трассировка и отладка
  • Автоматическое ожидание и устойчивость

Архитектурное Сравнение

Слой Протокола

Puppeteer:

API Puppeteer → Chrome DevTools Protocol → Chromium/Chrome
                                         → Firefox (через Juggler)

Playwright:

API Playwright → Протокол Playwright → Браузерные патчи
                                      → Chromium (CDP)
                                      → Firefox (Juggler)
                                      → WebKit (Пользовательский протокол)

Ключевое Различие: Playwright патчит браузеры во время сборки, обеспечивая согласованные API для всех браузеров. Puppeteer полагается на нативные протоколы браузеров.

Матрица Поддержки Браузеров

ФункцияPuppeteerPlaywright
Chromium✅ Полная поддержка✅ Полная поддержка
Chrome✅ Полная поддержка✅ Полная поддержка
Firefox⚠️ Экспериментальная✅ Полная поддержка
WebKit/Safari❌ Не поддерживается✅ Полная поддержка
Edge✅ Через Chromium✅ Полная поддержка
ПротоколCDP (нативный)Пользовательские патчи
Версионирование БраузераУстановлен в системеВключен в Playwright

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

Puppeteer:

npm install puppeteer
# Автоматически загружает Chromium

npm install puppeteer-core
# Без загрузки браузера, использует системный Chrome

Playwright:

npm install playwright
npx playwright install  # Загружает все браузеры

npm install @playwright/test  # С test runner
npx playwright install chromium  # Один браузер

Сравнение Размера Установки:

  • Puppeteer + Chromium: ~170-300 МБ
  • Playwright + Все браузеры: ~1 ГБ
  • Playwright + Только Chromium: ~300 МБ

API и Опыт Разработчика

Базовая Автоматизация

Puppeteer:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({ headless: false });
    const page = await browser.newPage();

    await page.goto('https://example.com');
    await page.type('#username', 'testuser');
    await page.type('#password', 'password123');
    await page.click('button[type="submit"]');

    await page.waitForSelector('.dashboard');
    const title = await page.title();
    console.log('Заголовок страницы:', title);

    await browser.close();
})();

Playwright:

const { chromium } = require('playwright');

(async () => {
    const browser = await chromium.launch({ headless: false });
    const page = await browser.newPage();

    await page.goto('https://example.com');
    await page.fill('#username', 'testuser');
    await page.fill('#password', 'password123');
    await page.click('button[type="submit"]');

    await page.waitForSelector('.dashboard');
    const title = await page.title();
    console.log('Заголовок страницы:', title);

    await browser.close();
})();

Схожесть API: ~90% совместимость API для базовых операций, но Playwright предлагает дополнительные функции.

Продвинутые Селекторы

Puppeteer:

// CSS селекторы
await page.click('#submit-button');
await page.click('.form-control:nth-child(2)');

// XPath
await page.waitForXPath('//button[contains(text(), "Отправить")]');
const [button] = await page.$x('//button[@type="submit"]');
await button.click();

// Пользовательский текстовый селектор (через page.evaluate)
await page.evaluate(() => {
    const button = Array.from(document.querySelectorAll('button'))
        .find(b => b.textContent.includes('Отправить'));
    button.click();
});

Playwright:

// CSS селекторы
await page.click('#submit-button');

// Текстовый селектор
await page.click('text=Отправить');
await page.click('button:has-text("Отправить")');

// XPath
await page.click('xpath=//button[@type="submit"]');

// Продвинутые селекторы
await page.click('button >> text=Отправить');  // Цепочка
await page.click('button:right-of(:text("Пользователь"))');  // На основе layout
await page.click('button:below(#header)');
await page.click('button:near(.form-group)');

// На основе ролей (доступность)
await page.click('role=button[name="Отправить"]');

Победитель: Playwright - более богатый движок селекторов с селекторами на основе layout и ролей.

Поведение Автоматического Ожидания

Puppeteer:

// Часто необходимо ручное ожидание
await page.waitForSelector('#result');
await page.waitForFunction(() => document.querySelector('#result').textContent !== '');

// Ожидание бездействия сети
await page.goto('https://example.com', { waitUntil: 'networkidle2' });

// Пользовательские ожидания
await page.waitForTimeout(1000);
await page.waitForFunction(
    () => window.appReady === true,
    { timeout: 5000 }
);

Playwright:

// Встроенное автоматическое ожидание
await page.click('#result');  // Автоматически ждет элемент

// Авто-ожидание для:
// - Элемент виден
// - Элемент стабилен (не анимируется)
// - Элемент включен
// - Элемент получает события

// Все еще поддерживает явные ожидания
await page.waitForSelector('#result', { state: 'visible' });
await page.waitForLoadState('networkidle');
await page.waitForFunction(() => window.appReady === true);

Победитель: Playwright - более агрессивное и интеллектуальное автоматическое ожидание уменьшает потребность в явных ожиданиях.

Множественные Контексты и Параллельное Выполнение

Puppeteer:

const browser = await puppeteer.launch();

// Инкогнито контексты
const context1 = await browser.createIncognitoBrowserContext();
const context2 = await browser.createIncognitoBrowserContext();

const page1 = await context1.newPage();
const page2 = await context2.newPage();

await Promise.all([
    page1.goto('https://example.com'),
    page2.goto('https://example.com')
]);

// Изолированное хранилище, cookies, сессии

Playwright:

const browser = await chromium.launch();

// Контексты браузера (как инкогнито)
const context1 = await browser.newContext({
    viewport: { width: 1920, height: 1080 },
    userAgent: 'Пользовательский User Agent',
    locale: 'ru-RU',
    timezoneId: 'Europe/Moscow',
    permissions: ['geolocation']
});

const context2 = await browser.newContext({
    viewport: { width: 375, height: 667 },  // Мобильный
    isMobile: true,
    hasTouch: true
});

// Параллельное выполнение с разными контекстами
const [page1, page2] = await Promise.all([
    context1.newPage(),
    context2.newPage()
]);

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

Кросс-Браузерное Тестирование

Согласованность Браузера

Puppeteer:

  • Chrome/Chromium: Отлично, нативная поддержка
  • Firefox: Ограниченная, экспериментальная, отсутствующие функции
  • Safari: Не поддерживается

Playwright:

const { chromium, firefox, webkit } = require('playwright');

async function testAllBrowsers() {
    for (const browserType of [chromium, firefox, webkit]) {
        const browser = await browserType.launch();
        const page = await browser.newPage();

        await page.goto('https://example.com');
        // Одинаковый API для всех браузеров
        await page.screenshot({ path: `screenshot-${browserType.name()}.png` });

        await browser.close();
    }
}

Реальный Кросс-Браузерный Тест:

const { test, expect } = require('@playwright/test');

test.describe('Кросс-браузерный логин', () => {
    test('должен успешно войти', async ({ page, browserName }) => {
        await page.goto('https://example.com/login');
        await page.fill('#username', 'testuser');
        await page.fill('#password', 'password123');
        await page.click('button[type="submit"]');

        await expect(page.locator('.dashboard')).toBeVisible();

        // Специфичные для браузера проверки
        if (browserName === 'webkit') {
            // Проверки специфичные для Safari
            await expect(page.locator('.webkit-notice')).toBeVisible();
        }
    });
});

Победитель: Playwright - комплексная, согласованная поддержка кросс-браузерности является основной функцией.

Перехват Сети и Мокирование

Перехват Запросов

Puppeteer:

await page.setRequestInterception(true);

page.on('request', (request) => {
    if (request.resourceType() === 'image') {
        request.abort();
    } else if (request.url().includes('/api/users')) {
        request.respond({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify([
                { id: 1, name: 'Тестовый Пользователь' }
            ])
        });
    } else {
        request.continue();
    }
});

await page.goto('https://example.com');

Playwright:

// Перехват на основе маршрутов
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());

await page.route('**/api/users', route => {
    route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify([
            { id: 1, name: 'Тестовый Пользователь' }
        ])
    });
});

// Или изменить запросы
await page.route('**/api/**', route => {
    const headers = route.request().headers();
    headers['Authorization'] = 'Bearer fake-token';
    route.continue({ headers });
});

await page.goto('https://example.com');

Победитель: Playwright - более гибкая маршрутизация, встроенная запись HAR.

Интеграция с Фреймворком Тестирования

Puppeteer с Jest

// jest-puppeteer.config.js
module.exports = {
    launch: {
        headless: true,
        args: ['--no-sandbox']
    },
    browserContext: 'default'
};

// test.spec.js
describe('Тест Логина', () => {
    beforeAll(async () => {
        await page.goto('https://example.com');
    });

    it('должен отобразить форму логина', async () => {
        await expect(page).toMatch('Логин');
        const title = await page.title();
        expect(title).toBe('Логин - Example');
    });

    it('должен успешно войти', async () => {
        await page.type('#username', 'testuser');
        await page.type('#password', 'password123');
        await page.click('button[type="submit"]');
        await page.waitForSelector('.dashboard');

        const url = page.url();
        expect(url).toContain('/dashboard');
    });
});

Test Runner Playwright

// playwright.config.js
module.exports = {
    testDir: './tests',
    timeout: 30000,
    retries: 2,
    use: {
        headless: true,
        viewport: { width: 1920, height: 1080 },
        screenshot: 'only-on-failure',
        video: 'retain-on-failure',
        trace: 'on-first-retry'
    },
    projects: [
        { name: 'chromium', use: { browserName: 'chromium' } },
        { name: 'firefox', use: { browserName: 'firefox' } },
        { name: 'webkit', use: { browserName: 'webkit' } }
    ]
};

// test.spec.js
const { test, expect } = require('@playwright/test');

test.describe('Тест Логина', () => {
    test.beforeEach(async ({ page }) => {
        await page.goto('https://example.com');
    });

    test('должен отобразить форму логина', async ({ page }) => {
        await expect(page.locator('h1')).toHaveText('Логин');
        await expect(page).toHaveTitle('Логин - Example');
    });

    test('должен успешно войти', async ({ page }) => {
        await page.fill('#username', 'testuser');
        await page.fill('#password', 'password123');
        await page.click('button[type="submit"]');

        await expect(page).toHaveURL(/.*dashboard/);
        await expect(page.locator('.dashboard')).toBeVisible();
    });
});

Победитель: Playwright - опинионированный test runner со встроенными утверждениями, параллелизацией, повторами, скриншотами, видео и трассировками.

Возможности Отладки

Отладка Puppeteer

// Slowmo
const browser = await puppeteer.launch({
    headless: false,
    slowMo: 100  // Замедлить на 100мс
});

// Devtools
const browser = await puppeteer.launch({
    headless: false,
    devtools: true
});

// Скриншоты
await page.screenshot({ path: 'screenshot.png', fullPage: true });

// Логи консоли
page.on('console', msg => console.log('ЛОГ СТРАНИЦЫ:', msg.text()));

// Ошибки страницы
page.on('pageerror', error => console.log('ОШИБКА СТРАНИЦЫ:', error.message));

Отладка Playwright

// Inspector (интерактивная отладка)
// Установить переменную окружения
// PWDEBUG=1 npm test

// Или в коде
await page.pause();  // Открывает inspector

// Trace viewer (отладка с перемоткой времени)
const context = await browser.newContext({
    recordVideo: { dir: 'videos/' },
    recordTrace: { dir: 'traces/' }
});

// Просмотр трассировки: npx playwright show-trace trace.zip

// Codegen (запись действий)
// npx playwright codegen https://example.com

// Режим headed со slomo
const browser = await chromium.launch({
    headless: false,
    slowMo: 1000
});

Победитель: Playwright - trace viewer обеспечивает отладку с перемоткой времени, inspector более отполирован.

Матрица Решений

Выбирайте Puppeteer Если:

  • ✅ Вам нужна только поддержка Chrome/Chromium
  • ✅ Вы создаете веб-скрейперы
  • ✅ Вам нужна легковесная автоматизация
  • ✅ Вам нужно генерировать PDF или скриншоты
  • ✅ У вас существующая кодовая база Puppeteer
  • ✅ Вы предпочитаете минимальные абстракции

Выбирайте Playwright Если:

  • ✅ Вам нужно кросс-браузерное тестирование (Firefox, Safari)
  • ✅ Вы создаете E2E тестовые наборы
  • ✅ Вам нужен интегрированный test runner
  • ✅ Вам нужна продвинутая отладка (trace viewer)
  • ✅ Вам нужно автоматическое ожидание и повторы
  • ✅ Вам нужна эмуляция мобильных/планшетов
  • ✅ Вы начинаете новый проект

Заключение

И Puppeteer, и Playwright являются отличными инструментами автоматизации браузеров, каждый превосходит в разных сценариях. Puppeteer остается предпочтительным выбором для веб-скрейпинга, генерации PDF и автоматизации специфичной для Chrome, где его зрелость и простота сияют. Playwright, однако, появился как превосходное решение для комплексного E2E тестирования, предлагая подлинную поддержку кросс-браузерности, мощные возможности отладки и опинионированный фреймворк тестирования, который решает общие болевые точки.

Для новых проектов автоматизации тестирования преимущества Playwright—истинная поддержка кросс-браузерности, trace viewer, автоматическое ожидание и интегрированный test runner—делают его рекомендуемым выбором. Для скрейпинга, генерации PDF или автоматизации только Chrome легковесный подход и зрелая экосистема Puppeteer остаются привлекательными.

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