Ручная настройка
Большинство людей должно запустить init — он записывает все файлы ниже за вас. Эта страница — справочник того, что генерирует init, чтобы вы могли подключить всё вручную, убрать codegen или понимать каждую часть.
Full-stack (SSR + браузер)
Заголовок раздела «Full-stack (SSR + браузер)»Для Next.js и аналогичных фреймворков, где и сервер, и браузер делают вызовы API. Используйте оба механизма записи вместе — см. как это работает.
Прокси — это лёгкий процесс, который вы запускаете вместе с вашим приложением для тест-прогона (через скрипт, как ниже, или через webServer Playwright) — это не инфраструктура, которую вы деплоите или поддерживаете. Весь сетап: запустите его рядом с приложением, направьте базовый URL API вашего приложения на него, пробросьте заголовок сессии из SSR и напишите одну фикстуру.
1. Добавьте скрипты в package.json
Заголовок раздела «1. Добавьте скрипты в package.json»{ "scripts": { "proxy": "test-proxy-recorder http://localhost:8000 --port 8100 --dir ./e2e/recordings", "dev:proxy": "concurrently \"npm run proxy\" \"TEST_PROXY_RECORDER_ENABLED=1 npm run dev\"", "serve:proxy": "concurrently \"npm run proxy\" \"TEST_PROXY_RECORDER_ENABLED=1 npm run serve\"" }}В коде приложения направьте базовый URL API на прокси, когда recorder включён, и на реальный бэкенд в остальных случаях — прокси никогда не запускается в продакшене:
const API_BASE = process.env.NODE_ENV === 'production' && !process.env.TEST_PROXY_RECORDER_ENABLED ? 'https://api.example.com' : 'http://localhost:8100'; // адрес проксиTEST_PROXY_RECORDER_ENABLED устанавливается скриптами dev:proxy / serve:proxy выше, а также скриптами, сгенерированными init. Используйте ту переменную окружения, которую ваше приложение уже использует для базового URL API (например API_URL, NEXT_PUBLIC_API_URL) — то же условие применяется.
2. Тегируйте серверные fetch (Next.js)
Заголовок раздела «2. Тегируйте серверные fetch (Next.js)»Серверные вызовы fetch нуждаются в заголовке recording-session, чтобы прокси знал, какому тесту они принадлежат. Playwright уже устанавливает его на навигацию браузера, поэтому id находится в next/headers — вам нужно лишь прикрепить его к исходящим SSR-запросам. Добавьте одну строку в ваш root layout (init делает это за вас):
import { registerProxyFetch } from 'test-proxy-recorder/nextjs';
registerProxyFetch(); // no-op in production unless TEST_PROXY_RECORDER_ENABLED=true
export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body>{children}</body> </html> );}Это работает на Node и Edge-runtime. Для axios-приложений вызовите registerProxyAxios(instance) на каждом серверном инстансе; для одиночного fetch createHeadersWithRecordingId(await headers()) — альтернатива без патча. proxy.ts/middleware.ts с setNextProxyHeaders опциональны — они только расскрывают id, они не тегируют fetch. Записывайте против продакшен-сборки (next build && next start), не next dev. См. интеграцию Next.js для подробностей. Приложения только для браузера могут пропустить этот шаг.
3. Напишите тест
Заголовок раздела «3. Напишите тест»import { test, expect } from '@playwright/test';import { playwrightProxy } from 'test-proxy-recorder';
// SSR requests (server → proxy) are recorded to .mock.json.// Browser requests to the proxy URL are also covered.const CLIENT_SIDE_URL = /localhost:8100/;
// Change to 'record' to update recordings.const MODE = 'replay' as const;
test.beforeEach(async ({ page }, testInfo) => { await playwrightProxy.before(page, testInfo, MODE, { url: CLIENT_SIDE_URL });});
test('homepage loads', async ({ page }) => { await page.goto('/'); await expect(page.getByText('Welcome')).toBeVisible();});4. Запишите
Заголовок раздела «4. Запишите»# Terminal 1npm run serve:proxy
# Terminal 2 — .mock.json and .har files are written automaticallynpx playwright test5. Переключитесь на воспроизведение и закоммитьте
Заголовок раздела «5. Переключитесь на воспроизведение и закоммитьте»git add e2e/recordings/git commit -m "add e2e recordings"Только браузер / SPA / расширение
Заголовок раздела «Только браузер / SPA / расширение»Когда все вызовы API идут из браузера (без SSR), вам нужен только механизм HAR. Для самой записи бэкенд прокси не требуется — процесс прокси лишь обеспечивает управление сессиями.
1. Установите
Заголовок раздела «1. Установите»npm install --save-dev test-proxy-recorder2. Добавьте прокси в playwright.config.ts
Заголовок раздела «2. Добавьте прокси в playwright.config.ts»import { defineConfig } from '@playwright/test';
export default defineConfig({ webServer: { command: 'test-proxy-recorder https://api.example.com --port 8100 --dir ./e2e/recordings', url: 'http://localhost:8100/__control', reuseExistingServer: true, },});Цель прокси (https://api.example.com) не важна для записи только в браузере — она используется только если серверные (SSR) запросы тоже нужно проксировать. Процесс прокси должен быть запущен, чтобы его эндпоинт /__control был доступен для управления сессиями.
3. Напишите фикстуру
Заголовок раздела «3. Напишите фикстуру»import { test as base, type Page, type BrowserContext } from '@playwright/test';import { playwrightProxy } from 'test-proxy-recorder';
// Match the external API domain your browser makes requests to.// In record mode these requests go to the real API and are saved.// In replay mode they are served from disk — no network needed.const CLIENT_SIDE_URL = /api\.example\.com/;
// Change to 'record' to hit the real API and update recordings.const MODE = 'replay' as const;
export const test = base.extend<{ page: Page }>({ page: async ({ context }, use, testInfo) => { const page = await context.newPage(); await playwrightProxy.before(page, testInfo, MODE, { url: CLIENT_SIDE_URL }); await use(page); },});4. Напишите тест
Заголовок раздела «4. Напишите тест»import { test, expect } from './fixtures';
test('homepage loads', async ({ page }) => { await page.goto('https://myapp.com/'); await expect(page.getByText('Welcome')).toBeVisible();});5. Запишите — выполните один раз против реального API
Заголовок раздела «5. Запишите — выполните один раз против реального API»# In fixtures.ts: const MODE = 'record' as const;npx playwright test# .har files are written to e2e/recordings/ automatically6. Переключитесь на воспроизведение и закоммитьте
Заголовок раздела «6. Переключитесь на воспроизведение и закоммитьте»# In fixtures.ts: const MODE = 'replay' as const;git add e2e/recordings/git commit -m "add e2e recordings"Теперь CI выполняется без какого-либо доступа к сети.
Добавьте это в .gitattributes, чтобы сворачивать крупные файлы записей в diff’ах PR:
/e2e/recordings/** binary