test-proxy-recorder 0.3.5 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +295 -100
- package/dist/{index-BlBWqSE4.d.cts → index-BloXCw69.d.cts} +4 -0
- package/dist/{index-BlBWqSE4.d.ts → index-BloXCw69.d.ts} +4 -0
- package/dist/index.cjs +488 -400
- package/dist/index.d.cts +10 -42
- package/dist/index.d.ts +10 -42
- package/dist/index.mjs +486 -398
- package/dist/playwright/index.cjs +8 -0
- package/dist/playwright/index.d.cts +1 -1
- package/dist/playwright/index.d.ts +1 -1
- package/dist/playwright/index.mjs +8 -0
- package/dist/proxy.js +478 -398
- package/package.json +13 -17
- package/skills/nextjs-ssr/SKILL.md +377 -0
- package/skills/proxy-setup/SKILL.md +458 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: proxy-setup
|
|
3
|
+
description: >
|
|
4
|
+
Set up test-proxy-recorder for any Playwright project. Covers the proxy CLI
|
|
5
|
+
(test-proxy-recorder <target> --port --dir), package.json scripts for the
|
|
6
|
+
three-service architecture (UI app → proxy → backend API), playwright.config.ts
|
|
7
|
+
webServer block pointing to /__control, per-test fixtures using
|
|
8
|
+
playwrightProxy.before(page, testInfo, mode, { url }), HAR browser-side
|
|
9
|
+
recording via url pattern, .mock.json server-side recording, record/replay/
|
|
10
|
+
transparent modes, the record-once→commit→CI-replay lifecycle, and parallel
|
|
11
|
+
test execution with fullyParallel. Load this skill when installing
|
|
12
|
+
test-proxy-recorder, writing Playwright fixtures, or configuring record/replay.
|
|
13
|
+
type: core
|
|
14
|
+
library: test-proxy-recorder
|
|
15
|
+
library_version: "0.3.5"
|
|
16
|
+
sources:
|
|
17
|
+
- "asmyshlyaev177/test-proxy-recorder:README.md"
|
|
18
|
+
- "asmyshlyaev177/test-proxy-recorder:packages/test-proxy-recorder/src/playwright/index.ts"
|
|
19
|
+
- "asmyshlyaev177/test-proxy-recorder:packages/test-proxy-recorder/src/types.ts"
|
|
20
|
+
- "asmyshlyaev177/test-proxy-recorder:apps/example-nextjs16/package.json"
|
|
21
|
+
- "asmyshlyaev177/test-proxy-recorder:apps/example-extension/e2e/fixtures.ts"
|
|
22
|
+
- "asmyshlyaev177/test-proxy-recorder:apps/example-extension/playwright.config.ts"
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# test-proxy-recorder — Proxy Setup
|
|
26
|
+
|
|
27
|
+
`test-proxy-recorder` runs an HTTP proxy that records real API responses to
|
|
28
|
+
disk (`.mock.json` for server-side, `.har` for browser-side) and replays them
|
|
29
|
+
in Playwright tests without a live backend.
|
|
30
|
+
|
|
31
|
+
Two recording mechanisms work independently or together:
|
|
32
|
+
|
|
33
|
+
| Mechanism | File | Records |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| Proxy | `.mock.json` | Server-side (SSR) fetches from Node.js |
|
|
36
|
+
| HAR | `.har` | Browser-side `fetch` calls, Chrome extension traffic |
|
|
37
|
+
|
|
38
|
+
## Setup
|
|
39
|
+
|
|
40
|
+
### Browser-only / SPA / Chrome extension
|
|
41
|
+
|
|
42
|
+
No backend proxy needed for recording — only the proxy process for session
|
|
43
|
+
management via `/__control`.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// playwright.config.ts
|
|
47
|
+
import { defineConfig } from '@playwright/test';
|
|
48
|
+
|
|
49
|
+
export default defineConfig({
|
|
50
|
+
webServer: {
|
|
51
|
+
command: 'test-proxy-recorder https://api.example.com --port 8100 --dir ./e2e/recordings',
|
|
52
|
+
url: 'http://localhost:8100/__control',
|
|
53
|
+
reuseExistingServer: true,
|
|
54
|
+
timeout: 15_000,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
// e2e/fixtures.ts
|
|
61
|
+
import { test as base, type Page } from '@playwright/test';
|
|
62
|
+
import { playwrightProxy } from 'test-proxy-recorder';
|
|
63
|
+
|
|
64
|
+
const CLIENT_SIDE_URL = /api\.example\.com/;
|
|
65
|
+
|
|
66
|
+
// Change to 'record' to hit the real API and update recordings.
|
|
67
|
+
const MODE = 'replay' as const;
|
|
68
|
+
|
|
69
|
+
export const test = base.extend<{ page: Page }>({
|
|
70
|
+
page: async ({ context }, use, testInfo) => {
|
|
71
|
+
const page = await context.newPage();
|
|
72
|
+
await playwrightProxy.before(page, testInfo, MODE, { url: CLIENT_SIDE_URL });
|
|
73
|
+
await use(page);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
export { expect } from '@playwright/test';
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Full-stack (SSR + browser)
|
|
80
|
+
|
|
81
|
+
The app's API base URL must be pointed at the proxy, not the real backend.
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
// package.json
|
|
85
|
+
{
|
|
86
|
+
"scripts": {
|
|
87
|
+
"proxy": "test-proxy-recorder http://localhost:3002 --port 8100 --dir ./e2e/recordings",
|
|
88
|
+
"start:all": "concurrently \"pnpm proxy\" \"INTERNAL_API_URL=http://localhost:8100 pnpm start\"",
|
|
89
|
+
"test:e2e": "pnpm build && concurrently --kill-others --success first --names services,tests \"pnpm start:all\" \"wait-on http://127.0.0.1:3000 http://127.0.0.1:8100/__control && playwright test --retries 1\"",
|
|
90
|
+
"test:e2e:record": "pnpm build && playwright test --workers 1 --ui"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`INTERNAL_API_URL` stands in for whatever env var your app reads its API base
|
|
96
|
+
URL from — it must point at the proxy (see Common Mistakes). For Next.js apps
|
|
97
|
+
running a production build, also set `TEST_PROXY_RECORDER_ENABLED=true`
|
|
98
|
+
(see test-proxy-recorder/nextjs-ssr).
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// e2e/my.test.ts
|
|
102
|
+
import { test, expect } from '@playwright/test';
|
|
103
|
+
import { playwrightProxy } from 'test-proxy-recorder';
|
|
104
|
+
|
|
105
|
+
// Change to 'record' to hit the real API and update recordings.
|
|
106
|
+
const MODE = 'replay' as const;
|
|
107
|
+
|
|
108
|
+
// External services the browser calls directly (auth, CDN, analytics, etc.).
|
|
109
|
+
// Server-side fetches through the proxy are recorded automatically via .mock.json.
|
|
110
|
+
const CLIENT_SIDE_URL = /cognito-.*\.amazonaws\.com|\.s3\..*\.amazonaws\.com/;
|
|
111
|
+
|
|
112
|
+
test.beforeEach(async ({ page }, testInfo) => {
|
|
113
|
+
await playwrightProxy.before(page, testInfo, MODE, {
|
|
114
|
+
url: CLIENT_SIDE_URL,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('creates a todo', async ({ page }) => {
|
|
119
|
+
await page.goto('/');
|
|
120
|
+
await page.getByTestId('new-todo-input').fill('Buy groceries');
|
|
121
|
+
await page.getByTestId('add-btn').click();
|
|
122
|
+
await expect(page.getByTestId('todo-text').first()).toHaveText('Buy groceries');
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Core Patterns
|
|
127
|
+
|
|
128
|
+
### Record/replay lifecycle
|
|
129
|
+
|
|
130
|
+
Recording is manual, done once per test with a single worker. Replay runs on
|
|
131
|
+
CI with multiple workers, headlessly.
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# 1. In fixtures.ts (or test file): set MODE = 'record'
|
|
135
|
+
# 2. Run once against the real backend, headed, one worker
|
|
136
|
+
npx playwright test --workers 1 --ui
|
|
137
|
+
|
|
138
|
+
# 3. Set MODE back to 'replay', commit recordings
|
|
139
|
+
git add e2e/recordings/
|
|
140
|
+
git commit -m "add e2e recordings"
|
|
141
|
+
|
|
142
|
+
# 4. Replay on CI — headless, parallel workers, no backend needed
|
|
143
|
+
npx playwright test
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Recording files must be committed — do not add `e2e/recordings/` to
|
|
147
|
+
`.gitignore`. Optionally collapse diffs with:
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
# .gitattributes
|
|
151
|
+
/e2e/recordings/** binary
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Auth setup
|
|
155
|
+
|
|
156
|
+
Auth always runs against the real auth provider — never recorded or replayed.
|
|
157
|
+
Use `setProxyMode('transparent')` so auth requests bypass the proxy entirely.
|
|
158
|
+
Skip the auth step in replay mode (the recorded session is already embedded in
|
|
159
|
+
the HAR / storage state file from the previous record run).
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// e2e/auth.setup.ts
|
|
163
|
+
import { test as setup } from '@playwright/test';
|
|
164
|
+
import { setProxyMode } from 'test-proxy-recorder';
|
|
165
|
+
|
|
166
|
+
const AUTH_FILE = 'e2e/.auth/state.json';
|
|
167
|
+
|
|
168
|
+
const TEST_USER = {
|
|
169
|
+
email: 'testuser@example.com',
|
|
170
|
+
password: 'TestPassword123',
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
setup('authenticate', async ({ page }) => {
|
|
174
|
+
// Bypass the proxy — auth must always hit the real provider.
|
|
175
|
+
await setProxyMode('transparent');
|
|
176
|
+
|
|
177
|
+
await page.goto('/users/sign-in');
|
|
178
|
+
await page.getByTestId('email').fill(TEST_USER.email);
|
|
179
|
+
await page.getByTestId('password').fill(TEST_USER.password);
|
|
180
|
+
await page.getByTestId('signinButton').click();
|
|
181
|
+
await page.waitForURL('/', { timeout: 15_000 });
|
|
182
|
+
|
|
183
|
+
await page.context().storageState({ path: AUTH_FILE });
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Add the auth state file to `.gitignore` — it contains session tokens and must not be committed:
|
|
188
|
+
|
|
189
|
+
```gitignore
|
|
190
|
+
# .gitignore
|
|
191
|
+
e2e/.auth/
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// playwright.config.ts
|
|
196
|
+
export default defineConfig({
|
|
197
|
+
projects: [
|
|
198
|
+
{ name: 'setup', testMatch: /auth\.setup\.ts/ },
|
|
199
|
+
{
|
|
200
|
+
name: 'e2e',
|
|
201
|
+
testIgnore: /auth\.setup\.ts/,
|
|
202
|
+
dependencies: ['setup'],
|
|
203
|
+
use: { storageState: 'e2e/.auth/state.json' },
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Include the auth provider domain in `CLIENT_SIDE_URL` when the browser makes
|
|
210
|
+
direct calls to it (e.g. Cognito token refresh, OAuth redirects):
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// These browser-to-Cognito calls are recorded in the HAR, not via the proxy.
|
|
214
|
+
const CLIENT_SIDE_URL = /cognito-.*\.amazonaws\.com/;
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Global teardown
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// e2e/global-teardown.ts
|
|
221
|
+
import { playwrightProxy } from 'test-proxy-recorder';
|
|
222
|
+
|
|
223
|
+
export default async function globalTeardown() {
|
|
224
|
+
await playwrightProxy.teardown().catch((err) => console.warn('teardown', err));
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// playwright.config.ts
|
|
230
|
+
export default defineConfig({
|
|
231
|
+
globalTeardown: './e2e/global-teardown.ts',
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### playwrightProxy.before() signature
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
await playwrightProxy.before(
|
|
239
|
+
page, // Playwright Page
|
|
240
|
+
testInfo, // TestInfo from test function argument
|
|
241
|
+
mode, // 'record' | 'replay' | 'transparent'
|
|
242
|
+
{
|
|
243
|
+
url, // RegExp | string — browser-side requests to intercept via HAR
|
|
244
|
+
timeout, // ms — auto-reset timeout (default: 120000)
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`url` is optional. Omit it for proxy-only (SSR) recording with no browser-side
|
|
250
|
+
HAR interception.
|
|
251
|
+
|
|
252
|
+
### Session file naming
|
|
253
|
+
|
|
254
|
+
Session IDs are derived from the test file path and title:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
jobs/Create.spec.ts + 'create a job' → jobs/Create__create-a-job
|
|
258
|
+
location.test.ts + 'homepage loads' → location__homepage-loads
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Files on disk:
|
|
262
|
+
```
|
|
263
|
+
e2e/recordings/
|
|
264
|
+
jobs/Create__create-a-job.mock.json # server-side
|
|
265
|
+
jobs__Create__create-a-job.har # browser-side (/ replaced with __)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Control endpoint
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Check current proxy state
|
|
272
|
+
curl http://localhost:8100/__control
|
|
273
|
+
|
|
274
|
+
# Programmatically switch mode
|
|
275
|
+
curl -X POST http://localhost:8100/__control \
|
|
276
|
+
-H 'Content-Type: application/json' \
|
|
277
|
+
-d '{"mode": "record", "id": "my-test"}'
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Override the default port (8100) with `TEST_PROXY_RECORDER_PORT` env var.
|
|
281
|
+
|
|
282
|
+
## Common Mistakes
|
|
283
|
+
|
|
284
|
+
### CRITICAL App env var not redirected through proxy
|
|
285
|
+
|
|
286
|
+
Wrong:
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"scripts": {
|
|
290
|
+
"dev:proxy": "concurrently \"pnpm proxy\" \"pnpm dev\""
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Correct:
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"scripts": {
|
|
299
|
+
"dev:proxy": "concurrently \"pnpm proxy\" \"INTERNAL_API_URL=http://localhost:8100 pnpm dev\""
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The app's API base URL must point at the proxy, not the real backend. When
|
|
305
|
+
omitted, requests bypass the proxy entirely and nothing is recorded.
|
|
306
|
+
|
|
307
|
+
Source: README.md — Full-stack Quick Start
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
### CRITICAL Wrong CLIENT_SIDE_URL pattern for HAR recording
|
|
312
|
+
|
|
313
|
+
Wrong:
|
|
314
|
+
```typescript
|
|
315
|
+
// Matches the proxy URL — but the proxy handles server-side recording
|
|
316
|
+
// automatically. This intercepts nothing useful for HAR.
|
|
317
|
+
await playwrightProxy.before(page, testInfo, MODE, {
|
|
318
|
+
url: /localhost:8100/,
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Correct:
|
|
323
|
+
```typescript
|
|
324
|
+
// Match the actual external domains the browser calls directly:
|
|
325
|
+
// third-party auth, CDN, analytics, chat SDKs, etc.
|
|
326
|
+
const CLIENT_SIDE_URL = /cognito-.*\.amazonaws\.com|\.stream-io-api\.com/;
|
|
327
|
+
await playwrightProxy.before(page, testInfo, MODE, { url: CLIENT_SIDE_URL });
|
|
328
|
+
|
|
329
|
+
// Browser-only / SPA with no SSR — match the real API domain
|
|
330
|
+
await playwrightProxy.before(page, testInfo, MODE, { url: /api\.example\.com/ });
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
`url` must match the external domains the browser calls directly — not the
|
|
334
|
+
proxy. Server-side fetches through the proxy are already recorded to
|
|
335
|
+
`.mock.json` automatically. `url` is only for browser-side HAR recording of
|
|
336
|
+
requests that never touch the proxy (third-party services, CDNs, auth providers).
|
|
337
|
+
|
|
338
|
+
Source: README.md — Playwright Integration; apps/example-extension/e2e/fixtures.ts
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
### HIGH teardown() called per-test breaks parallel replay
|
|
343
|
+
|
|
344
|
+
Wrong:
|
|
345
|
+
```typescript
|
|
346
|
+
test.afterAll(async () => {
|
|
347
|
+
await playwrightProxy.teardown(); // resets global proxy mode for all workers
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Correct:
|
|
352
|
+
```typescript
|
|
353
|
+
// Omit afterAll entirely.
|
|
354
|
+
// Session cleanup is automatic via context.on('close').
|
|
355
|
+
// Only call teardown() in globalTeardown (see Global Teardown pattern above).
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
`teardown()` sets the **global** proxy mode to `transparent`. With
|
|
359
|
+
`fullyParallel: true`, a fast test's `afterAll` fires while other tests are
|
|
360
|
+
still replaying, switching the proxy mid-session and routing requests to the
|
|
361
|
+
real network.
|
|
362
|
+
|
|
363
|
+
Source: README.md — Parallel Replay section
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
### HIGH webServer url points to proxy root not /__control
|
|
368
|
+
|
|
369
|
+
Wrong:
|
|
370
|
+
```typescript
|
|
371
|
+
webServer: {
|
|
372
|
+
command: 'test-proxy-recorder http://localhost:8000 --port 8100',
|
|
373
|
+
url: 'http://localhost:8100', // root proxies to backend — may 502
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Correct:
|
|
378
|
+
```typescript
|
|
379
|
+
webServer: {
|
|
380
|
+
command: 'test-proxy-recorder http://localhost:8000 --port 8100 --dir ./e2e/recordings',
|
|
381
|
+
url: 'http://localhost:8100/__control',
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Playwright uses `url` to health-check that the server is ready. The proxy root
|
|
386
|
+
`/` forwards to the backend, which may be unavailable, causing Playwright to
|
|
387
|
+
report the server as not ready. `/__control` is always available.
|
|
388
|
+
|
|
389
|
+
Source: README.md; apps/example-extension/playwright.config.ts
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
### HIGH Recording files added to .gitignore
|
|
394
|
+
|
|
395
|
+
Wrong:
|
|
396
|
+
```gitignore
|
|
397
|
+
# .gitignore
|
|
398
|
+
e2e/recordings/
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Correct:
|
|
402
|
+
```gitignore
|
|
403
|
+
# .gitignore — do NOT list e2e/recordings/
|
|
404
|
+
|
|
405
|
+
# .gitattributes — collapse diffs without excluding files
|
|
406
|
+
/e2e/recordings/** binary
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
CI has no recordings to replay from if the directory is gitignored. Tests will
|
|
410
|
+
fail or hit the real network.
|
|
411
|
+
|
|
412
|
+
Source: README.md — Switch to replay and commit
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
### MEDIUM Recording with Next.js dev server produces flaky recordings
|
|
417
|
+
|
|
418
|
+
Wrong:
|
|
419
|
+
```bash
|
|
420
|
+
# Recording against the dev server (MODE = 'record' in fixtures)
|
|
421
|
+
next dev & npx playwright test --workers 1
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Correct:
|
|
425
|
+
```bash
|
|
426
|
+
# Build first, then record against the production build
|
|
427
|
+
pnpm build && npx playwright test --workers 1 --ui
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The Next.js dev server is slow and can cause SSR fetches to timeout or execute
|
|
431
|
+
out of order, producing incomplete recordings that fail in replay.
|
|
432
|
+
|
|
433
|
+
Source: README.md — Full-stack Quick Start note; apps/example-nextjs16/package.json
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
### MEDIUM Recording with multiple workers corrupts session files
|
|
438
|
+
|
|
439
|
+
Wrong:
|
|
440
|
+
```typescript
|
|
441
|
+
// fixtures.ts — MODE set to 'record', running with default workers
|
|
442
|
+
const MODE = 'record' as const;
|
|
443
|
+
// playwright test ← parallel workers write to same session files
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Correct:
|
|
447
|
+
```typescript
|
|
448
|
+
// fixtures.ts
|
|
449
|
+
const MODE = 'record' as const;
|
|
450
|
+
// npx playwright test --workers 1 --ui ← single worker when recording
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Recording is a manual, single-worker operation. Replay is what uses multiple
|
|
454
|
+
workers (`fullyParallel: true`). Set `MODE = 'record'` in the fixture file, then set it back to `'replay'` before committing.
|
|
455
|
+
|
|
456
|
+
Source: apps/example-nextjs16/package.json; maintainer guidance
|
|
457
|
+
|
|
458
|
+
See also: test-proxy-recorder/nextjs-ssr — for Next.js SSR header propagation
|