test-proxy-recorder 0.1.4 → 0.1.6
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 +345 -152
- package/dist/{index-De4mgziH.d.cts → index-CBjvm5rb.d.cts} +5 -1
- package/dist/{index-De4mgziH.d.ts → index-CBjvm5rb.d.ts} +5 -1
- package/dist/index.cjs +210 -35
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.mjs +210 -35
- package/dist/playwright/index.cjs +39 -3
- package/dist/playwright/index.d.cts +1 -1
- package/dist/playwright/index.d.ts +1 -1
- package/dist/playwright/index.mjs +39 -3
- package/dist/proxy.js +173 -34
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -9,262 +9,455 @@ HTTP proxy server for recording and replaying network requests in testing. Works
|
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
|
12
|
-
- **Fast CI/CD Tests**: Record API responses once with real backend, replay them on CI/CD
|
|
13
|
-
- **Fast
|
|
14
|
-
- **Server Side Rendering**: Can record SSR requests from JS frameworks like
|
|
12
|
+
- **Fast CI/CD Tests**: Record API responses once with real backend, replay them on CI/CD without backend
|
|
13
|
+
- **Fast Workflow**: Record real interactions with API instead of mocking every request manually
|
|
14
|
+
- **Server Side Rendering**: Can record SSR requests from JS frameworks like Next.js
|
|
15
15
|
- **Deterministic Tests**: Same responses every time, no flaky network issues, no need to wire up the whole Backend API for testing
|
|
16
|
+
- **WebSocket Support**: Records and replays WebSocket connections
|
|
16
17
|
|
|
17
|
-
##
|
|
18
|
+
## Table of Contents
|
|
19
|
+
|
|
20
|
+
- [How It Works](#how-it-works)
|
|
21
|
+
- [Complete Setup Guide](#complete-setup-guide)
|
|
22
|
+
- [CLI Usage](#cli-usage)
|
|
23
|
+
- [Playwright Integration](#playwright-integration)
|
|
24
|
+
- [Control Endpoint](#control-endpoint)
|
|
25
|
+
- [Typical Workflow](#typical-workflow)
|
|
26
|
+
- [Recording Format](#recording-format)
|
|
27
|
+
- [Troubleshooting](#troubleshooting)
|
|
28
|
+
- [API Reference](#api-reference)
|
|
29
|
+
|
|
30
|
+
## How It Works
|
|
31
|
+
|
|
32
|
+
The proxy server runs continuously and can switch between three modes per test:
|
|
33
|
+
|
|
34
|
+
### 1. Transparent Mode (Default)
|
|
35
|
+
|
|
36
|
+
Passes requests through to the backend without recording or replaying.
|
|
37
|
+
|
|
38
|
+
### 2. Record Mode
|
|
39
|
+
|
|
40
|
+
Captures all HTTP requests/responses and WebSocket messages to disk. Each test gets its own recording file based on the test name.
|
|
41
|
+
|
|
42
|
+
### 3. Replay Mode
|
|
43
|
+
|
|
44
|
+
Replays previously recorded responses from disk instead of hitting the real API. Perfect for fast, deterministic tests.
|
|
45
|
+
|
|
46
|
+
## Complete Setup Guide
|
|
47
|
+
|
|
48
|
+
### Step 1: Install Package
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install --save-dev test-proxy-recorder
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Step 2: Add NPM Scripts
|
|
55
|
+
|
|
56
|
+
Add to `package.json`:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"scripts": {
|
|
61
|
+
"proxy": "test-proxy-recorder http://localhost:8000 --port 8100 --recordings-dir ./e2e/recordings"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**RECOMMENDED**: Use `concurrently` to run proxy and app together:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install --save-dev concurrently
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"scripts": {
|
|
75
|
+
"proxy": "test-proxy-recorder http://localhost:8000 --port 8100 --recordings-dir ./e2e/recordings",
|
|
76
|
+
"dev:proxy": "concurrently -n \"proxy,app\" -c \"blue,green\" \"npm run proxy\" \"INTERNAL_API_URL=http://localhost:8100 npm run dev\""
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Step 3: Configure Git for Recordings
|
|
82
|
+
|
|
83
|
+
**CRITICAL**: Recordings must be committed to git for CI/CD replay.
|
|
84
|
+
|
|
85
|
+
Create or update your `.gitattributes` file:
|
|
86
|
+
|
|
87
|
+
```gitattributes
|
|
88
|
+
/e2e/recordings/** binary
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This marks recording files as binary, which causes long mock files to be collapsed/folded in Pull Request diffs for better readability.
|
|
92
|
+
|
|
93
|
+
**DO NOT** add `e2e/recordings` to `.gitignore`. Recordings need to be versioned in git for CI/CD to use them.
|
|
94
|
+
|
|
95
|
+
**Note**: The recordings directory will be created automatically when you first record a test - no need to create it manually.
|
|
96
|
+
|
|
97
|
+
### Step 4: Create Playwright Global Teardown (Recommended)
|
|
98
|
+
|
|
99
|
+
Create `e2e/global-teardown.ts`:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { setProxyMode } from 'test-proxy-recorder';
|
|
103
|
+
|
|
104
|
+
async function globalTeardown() {
|
|
105
|
+
await setProxyMode('transparent');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export default globalTeardown;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Update `playwright.config.ts`:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { defineConfig } from '@playwright/test';
|
|
115
|
+
|
|
116
|
+
export default defineConfig({
|
|
117
|
+
testDir: './e2e',
|
|
118
|
+
globalTeardown: './e2e/global-teardown.ts',
|
|
119
|
+
// ... rest of config
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Step 5: Create Example Test
|
|
124
|
+
|
|
125
|
+
Create `e2e/example.spec.ts`:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { test, expect } from '@playwright/test';
|
|
129
|
+
import { playwrightProxy } from 'test-proxy-recorder';
|
|
130
|
+
|
|
131
|
+
test('example test with proxy', async ({ page }, testInfo) => {
|
|
132
|
+
// Set proxy mode: 'record' to capture, 'replay' to use recordings
|
|
133
|
+
await playwrightProxy.before(testInfo, 'replay');
|
|
134
|
+
|
|
135
|
+
await page.goto('/');
|
|
136
|
+
await expect(page.getByText('Welcome')).toBeVisible();
|
|
137
|
+
|
|
138
|
+
// Always cleanup after test
|
|
139
|
+
await playwrightProxy.after(testInfo);
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Step 6: Run Tests
|
|
144
|
+
|
|
145
|
+
**First run (record mode)**:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
await playwrightProxy.before(testInfo, 'record');
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Subsequent runs (replay mode)**:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
await playwrightProxy.before(testInfo, 'replay');
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## CLI Usage
|
|
158
|
+
|
|
159
|
+
### Basic Command
|
|
18
160
|
|
|
19
161
|
```bash
|
|
20
|
-
|
|
21
|
-
# or
|
|
22
|
-
pnpm add test-proxy-recorder
|
|
23
|
-
# or
|
|
24
|
-
yarn add test-proxy-recorder
|
|
162
|
+
test-proxy-recorder <target-url> [options]
|
|
25
163
|
```
|
|
26
164
|
|
|
27
|
-
|
|
165
|
+
### CLI Options
|
|
166
|
+
|
|
167
|
+
- `<target-url>` - Backend API URL (positional argument, required)
|
|
168
|
+
- `--port, -p <number>` - Port to listen on (default: 8080)
|
|
169
|
+
- `--recordings-dir, -r <path>` - Directory to store recordings (default: ./recordings)
|
|
170
|
+
- `--help, -h` - Show help
|
|
171
|
+
|
|
172
|
+
### Examples
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Basic usage
|
|
176
|
+
test-proxy-recorder http://localhost:8000
|
|
28
177
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
178
|
+
# Custom port and recordings directory
|
|
179
|
+
test-proxy-recorder http://localhost:8000 --port 8100 --recordings-dir ./mocks
|
|
180
|
+
|
|
181
|
+
# Multiple targets (experimental)
|
|
182
|
+
test-proxy-recorder http://localhost:8000 http://localhost:9000 --port 8100
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Playwright Integration
|
|
186
|
+
|
|
187
|
+
### Basic Test Structure
|
|
188
|
+
|
|
189
|
+
Every test using the proxy should follow this pattern:
|
|
32
190
|
|
|
33
191
|
```typescript
|
|
34
192
|
import { test } from '@playwright/test';
|
|
35
193
|
import { playwrightProxy } from 'test-proxy-recorder';
|
|
36
194
|
|
|
37
|
-
test('
|
|
38
|
-
// Set
|
|
195
|
+
test('test name', async ({ page }, testInfo) => {
|
|
196
|
+
// 1. Set mode BEFORE test actions
|
|
197
|
+
await playwrightProxy.before(testInfo, 'replay');
|
|
198
|
+
|
|
199
|
+
// 2. Test code
|
|
200
|
+
await page.goto('/page');
|
|
201
|
+
|
|
202
|
+
// 3. Reset mode AFTER test completes
|
|
203
|
+
await playwrightProxy.after(testInfo);
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Recording vs Replay
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// Recording mode - captures API responses
|
|
211
|
+
test('create user', async ({ page }, testInfo) => {
|
|
39
212
|
await playwrightProxy.before(testInfo, 'record');
|
|
40
213
|
|
|
41
|
-
|
|
42
|
-
await page.
|
|
43
|
-
|
|
214
|
+
await page.goto('/users/new');
|
|
215
|
+
await page.fill('[name="username"]', 'testuser');
|
|
216
|
+
await page.click('button[type="submit"]');
|
|
44
217
|
|
|
45
|
-
// Save mock and return to transparent mode
|
|
46
218
|
await playwrightProxy.after(testInfo);
|
|
47
219
|
});
|
|
48
220
|
|
|
49
|
-
|
|
50
|
-
|
|
221
|
+
// Replay mode - uses recorded responses
|
|
222
|
+
test('create user', async ({ page }, testInfo) => {
|
|
51
223
|
await playwrightProxy.before(testInfo, 'replay');
|
|
52
224
|
|
|
53
|
-
await page.goto('/
|
|
54
|
-
|
|
225
|
+
await page.goto('/users/new');
|
|
226
|
+
await page.fill('[name="username"]', 'testuser');
|
|
227
|
+
await page.click('button[type="submit"]');
|
|
55
228
|
|
|
56
229
|
await playwrightProxy.after(testInfo);
|
|
57
230
|
});
|
|
58
231
|
```
|
|
59
232
|
|
|
60
|
-
|
|
233
|
+
### Test Naming
|
|
61
234
|
|
|
62
|
-
|
|
235
|
+
Recording files are auto-generated from test names:
|
|
63
236
|
|
|
64
|
-
|
|
237
|
+
- Test: `"create a user"`
|
|
238
|
+
- File: `create-a-user.mock.json`
|
|
65
239
|
|
|
66
|
-
|
|
240
|
+
**Important**: Keep test names stable for replay to work correctly.
|
|
67
241
|
|
|
68
|
-
###
|
|
242
|
+
### Global Teardown (Recommended)
|
|
69
243
|
|
|
70
|
-
|
|
244
|
+
Create `e2e/global-teardown.ts`:
|
|
71
245
|
|
|
72
|
-
|
|
246
|
+
```typescript
|
|
247
|
+
import { setProxyMode } from 'test-proxy-recorder';
|
|
73
248
|
|
|
74
|
-
|
|
249
|
+
async function globalTeardown() {
|
|
250
|
+
await setProxyMode('transparent');
|
|
251
|
+
}
|
|
75
252
|
|
|
76
|
-
|
|
253
|
+
export default globalTeardown;
|
|
254
|
+
```
|
|
77
255
|
|
|
78
|
-
|
|
256
|
+
Update `playwright.config.ts`:
|
|
79
257
|
|
|
80
258
|
```typescript
|
|
81
|
-
|
|
82
|
-
await playwrightProxy.before(testInfo, 'recording');
|
|
83
|
-
// ... test code ...
|
|
84
|
-
await playwrightProxy.after(testInfo);
|
|
259
|
+
import { defineConfig } from '@playwright/test';
|
|
85
260
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
261
|
+
export default defineConfig({
|
|
262
|
+
testDir: './e2e',
|
|
263
|
+
globalTeardown: './e2e/global-teardown.ts',
|
|
264
|
+
// ... rest of config
|
|
265
|
+
});
|
|
90
266
|
```
|
|
91
267
|
|
|
92
|
-
|
|
268
|
+
## Control Endpoint
|
|
269
|
+
|
|
270
|
+
The proxy exposes a control endpoint at `/__control` for programmatic mode switching.
|
|
93
271
|
|
|
94
|
-
|
|
272
|
+
### Via HTTP
|
|
95
273
|
|
|
96
274
|
```bash
|
|
97
275
|
# Switch to record mode
|
|
98
276
|
curl -X POST http://localhost:8100/__control \
|
|
99
277
|
-H "Content-Type: application/json" \
|
|
100
|
-
-d '{"mode": "record", "id": "my-
|
|
278
|
+
-d '{"mode": "record", "id": "my-test-1", "timeout": 30000}'
|
|
101
279
|
|
|
102
280
|
# Switch to replay mode
|
|
103
281
|
curl -X POST http://localhost:8100/__control \
|
|
104
282
|
-H "Content-Type: application/json" \
|
|
105
|
-
-d '{"mode": "replay", "id": "my-
|
|
283
|
+
-d '{"mode": "replay", "id": "my-test-1"}'
|
|
106
284
|
|
|
107
285
|
# Switch to transparent mode
|
|
108
286
|
curl -X POST http://localhost:8100/__control \
|
|
109
287
|
-H "Content-Type: application/json" \
|
|
110
|
-
-d '{"mode": "transparent"
|
|
288
|
+
-d '{"mode": "transparent"}'
|
|
111
289
|
```
|
|
112
290
|
|
|
113
|
-
|
|
291
|
+
### Via JavaScript
|
|
114
292
|
|
|
115
293
|
```javascript
|
|
116
|
-
// Switch to record mode
|
|
117
294
|
await fetch('http://localhost:8100/__control', {
|
|
118
295
|
method: 'POST',
|
|
119
|
-
headers: {
|
|
120
|
-
'Content-Type': 'application/json',
|
|
121
|
-
},
|
|
296
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
297
|
body: JSON.stringify({
|
|
123
298
|
mode: 'record',
|
|
124
|
-
id: 'my-
|
|
125
|
-
timeout: 30000
|
|
299
|
+
id: 'my-test-1',
|
|
300
|
+
timeout: 30000 // Optional: auto-reset after 30s
|
|
126
301
|
})
|
|
127
302
|
});
|
|
303
|
+
```
|
|
128
304
|
|
|
129
|
-
|
|
130
|
-
await fetch('http://localhost:8100/__control', {
|
|
131
|
-
method: 'POST',
|
|
132
|
-
headers: {
|
|
133
|
-
'Content-Type': 'application/json',
|
|
134
|
-
},
|
|
135
|
-
body: JSON.stringify({
|
|
136
|
-
mode: 'replay',
|
|
137
|
-
id: 'my-testfile-1'
|
|
138
|
-
})
|
|
139
|
-
});
|
|
305
|
+
### Control Request Interface
|
|
140
306
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
body: JSON.stringify({
|
|
148
|
-
mode: 'transparent',
|
|
149
|
-
})
|
|
150
|
-
});
|
|
307
|
+
```typescript
|
|
308
|
+
interface ControlRequest {
|
|
309
|
+
mode: 'transparent' | 'record' | 'replay';
|
|
310
|
+
id?: string; // Recording ID, required for record/replay
|
|
311
|
+
timeout?: number; // Auto-reset timeout in ms (default: 120000)
|
|
312
|
+
}
|
|
151
313
|
```
|
|
152
314
|
|
|
153
|
-
##
|
|
315
|
+
## Typical Workflow
|
|
154
316
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
317
|
+
### Initial Recording
|
|
318
|
+
|
|
319
|
+
1. Start backend API: `npm run api`
|
|
320
|
+
2. Start proxy and app: `npm run dev:proxy`
|
|
321
|
+
3. Set test to `'record'` mode
|
|
322
|
+
4. Run test: Recordings saved to `./e2e/recordings/` (directory created automatically)
|
|
323
|
+
5. Commit `.mock.json` files to git
|
|
324
|
+
6. Change mode to `'replay'`
|
|
325
|
+
|
|
326
|
+
### Running with Replay
|
|
327
|
+
|
|
328
|
+
1. Start proxy and app: `npm run dev:proxy` (no backend needed!)
|
|
329
|
+
2. Set test to `'replay'` mode
|
|
330
|
+
3. Run test: Uses recorded responses
|
|
331
|
+
4. Tests run fast without backend
|
|
332
|
+
|
|
333
|
+
### Updating Recordings
|
|
334
|
+
|
|
335
|
+
1. Start backend API
|
|
336
|
+
2. Set test to `'record'` mode
|
|
337
|
+
3. Run test: Overwrites existing recording
|
|
338
|
+
4. Commit updated `.mock.json` file
|
|
339
|
+
|
|
340
|
+
## Recording Format
|
|
341
|
+
|
|
342
|
+
Recordings are stored as JSON files with `.mock.json` extension:
|
|
343
|
+
|
|
344
|
+
```text
|
|
345
|
+
e2e/recordings/
|
|
346
|
+
├── create-a-user.mock.json
|
|
347
|
+
├── fetch-users-list.mock.json
|
|
348
|
+
└── delete-user.mock.json
|
|
158
349
|
```
|
|
159
350
|
|
|
160
|
-
|
|
351
|
+
## Troubleshooting
|
|
161
352
|
|
|
162
|
-
|
|
163
|
-
- `--target, -t`: Backend target URL (can add multiple targets)
|
|
164
|
-
- `--recordings, -r`: Directory to store recordings (default: ./recordings)
|
|
353
|
+
### Proxy not responding
|
|
165
354
|
|
|
166
|
-
|
|
355
|
+
**Check if proxy is running**:
|
|
167
356
|
|
|
168
|
-
|
|
357
|
+
```bash
|
|
358
|
+
curl http://localhost:8100/__control
|
|
359
|
+
```
|
|
169
360
|
|
|
170
|
-
|
|
171
|
-
class ProxyServer {
|
|
172
|
-
constructor(targets: string[], recordingsDir: string);
|
|
361
|
+
**Check port availability**:
|
|
173
362
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
363
|
+
```bash
|
|
364
|
+
lsof -i :8100
|
|
177
365
|
```
|
|
178
366
|
|
|
179
|
-
###
|
|
367
|
+
### No recordings saved
|
|
368
|
+
|
|
369
|
+
- Verify proxy mode is `'record'`
|
|
370
|
+
- Check app is using proxy URL (`http://localhost:8100`)
|
|
371
|
+
- Verify write permissions on recordings directory
|
|
372
|
+
- Check proxy server logs for errors
|
|
373
|
+
|
|
374
|
+
### Test fails in replay mode
|
|
375
|
+
|
|
376
|
+
- Ensure recording exists for this test
|
|
377
|
+
- Check test name hasn't changed
|
|
378
|
+
- Verify recording file matches expected format
|
|
379
|
+
- Re-record if API responses changed
|
|
380
|
+
|
|
381
|
+
### Recordings not matching requests
|
|
382
|
+
|
|
383
|
+
- Request URLs must match exactly
|
|
384
|
+
- Headers may affect matching (configurable)
|
|
385
|
+
- Query parameters must be in same order
|
|
386
|
+
- Re-record to capture current API behavior
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
## API Reference
|
|
180
390
|
|
|
181
|
-
|
|
391
|
+
### ProxyServer Class
|
|
182
392
|
|
|
183
393
|
```typescript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
394
|
+
class ProxyServer {
|
|
395
|
+
constructor(targets: string[], recordingsDir: string);
|
|
396
|
+
async init(): Promise<void>;
|
|
397
|
+
listen(port: number): http.Server;
|
|
188
398
|
}
|
|
189
399
|
```
|
|
190
400
|
|
|
191
|
-
### Playwright Integration
|
|
401
|
+
### Playwright Integration
|
|
192
402
|
|
|
193
403
|
```typescript
|
|
194
|
-
import { playwrightProxy } from 'test-proxy-recorder';
|
|
404
|
+
import { playwrightProxy, setProxyMode } from 'test-proxy-recorder';
|
|
195
405
|
|
|
196
|
-
// Main helper
|
|
406
|
+
// Main helper for Playwright tests
|
|
197
407
|
const playwrightProxy = {
|
|
198
408
|
// Set proxy mode before test
|
|
199
|
-
async before(
|
|
409
|
+
async before(
|
|
410
|
+
testInfo: TestInfo,
|
|
411
|
+
mode: 'record' | 'replay' | 'transparent',
|
|
412
|
+
timeout?: number
|
|
413
|
+
): Promise<void>;
|
|
200
414
|
|
|
201
415
|
// Reset to transparent mode after test
|
|
202
|
-
async after(testInfo:
|
|
416
|
+
async after(testInfo: TestInfo): Promise<void>;
|
|
203
417
|
};
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Recording Format
|
|
207
|
-
|
|
208
|
-
Recordings are stored as JSON files in the recordings directory:
|
|
209
418
|
|
|
419
|
+
// Direct mode control
|
|
420
|
+
async function setProxyMode(
|
|
421
|
+
mode: 'record' | 'replay' | 'transparent',
|
|
422
|
+
id?: string,
|
|
423
|
+
timeout?: number
|
|
424
|
+
): Promise<void>;
|
|
210
425
|
```
|
|
211
|
-
recordings/
|
|
212
|
-
├── test-session-1.json
|
|
213
|
-
├── test-session-2.json
|
|
214
|
-
└── ...
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
Each recording contains:
|
|
218
|
-
|
|
219
|
-
- Request/response pairs with headers and bodies
|
|
220
|
-
- WebSocket messages with timestamps
|
|
221
|
-
- Unique keys for request matching during replay
|
|
222
|
-
|
|
223
|
-
## Typical Workflow
|
|
224
|
-
|
|
225
|
-
1. **Start the proxy server** (runs continuously):
|
|
226
426
|
|
|
227
|
-
|
|
228
|
-
test-proxy-recorder http://localhost:8000 --port 8100
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
2. **Configure your app** to use the proxy:
|
|
427
|
+
### Control Endpoint
|
|
232
428
|
|
|
233
|
-
|
|
234
|
-
export EXTERNAL_API_URL=http://localhost:8100 yarn dev
|
|
235
|
-
```
|
|
429
|
+
**Endpoint**: `POST http://localhost:8100/__control`
|
|
236
430
|
|
|
237
|
-
|
|
431
|
+
**Request Body**:
|
|
238
432
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
```
|
|
433
|
+
```typescript
|
|
434
|
+
{
|
|
435
|
+
mode: 'transparent' | 'record' | 'replay';
|
|
436
|
+
id?: string; // Recording ID (required for record/replay)
|
|
437
|
+
timeout?: number; // Auto-reset timeout in ms (default: 120000)
|
|
438
|
+
}
|
|
439
|
+
```
|
|
247
440
|
|
|
248
|
-
|
|
441
|
+
**Response**:
|
|
249
442
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
443
|
+
```typescript
|
|
444
|
+
{
|
|
445
|
+
success: boolean;
|
|
446
|
+
mode: string;
|
|
447
|
+
id: string | null;
|
|
448
|
+
timeout: number;
|
|
449
|
+
}
|
|
450
|
+
```
|
|
258
451
|
|
|
259
452
|
## Requirements
|
|
260
453
|
|
|
261
454
|
- Node.js >= 22.0.0
|
|
262
|
-
- @playwright/test >= 1.0.0
|
|
263
|
-
|
|
264
|
-
## License
|
|
265
|
-
|
|
266
|
-
MIT
|
|
455
|
+
- @playwright/test >= 1.0.0 (for Playwright integration)
|
|
267
456
|
|
|
268
457
|
## Contributing
|
|
269
458
|
|
|
270
459
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
460
|
+
|
|
461
|
+
## License
|
|
462
|
+
|
|
463
|
+
MIT
|
|
@@ -47,7 +47,7 @@ interface RecordingSession {
|
|
|
47
47
|
websocketRecordings: WebSocketRecording[];
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
|
|
50
|
+
type PlaywrightTestInfo = Pick<TestInfo, 'title' | 'titlePath'>;
|
|
51
51
|
/**
|
|
52
52
|
* Set the proxy mode for a given session
|
|
53
53
|
* @param mode - The proxy mode to set (recording, replay, transparent)
|
|
@@ -57,6 +57,10 @@ type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
|
|
|
57
57
|
declare function setProxyMode(mode: Mode, sessionId?: string, timeout?: number): Promise<void>;
|
|
58
58
|
/**
|
|
59
59
|
* Generate a session ID from test info
|
|
60
|
+
* Uses titlePath to create folder structure with test file name
|
|
61
|
+
* Supports both .spec.ts and .test.ts extensions
|
|
62
|
+
* Example: ['jobs/Create.spec.ts', 'create a job'] becomes 'jobs/Create__create-a-job'
|
|
63
|
+
* Example: ['users/Auth.test.ts', 'login test'] becomes 'users/Auth__login-test'
|
|
60
64
|
* @param testInfo - Playwright test info object
|
|
61
65
|
*/
|
|
62
66
|
declare function generateSessionId(testInfo: PlaywrightTestInfo): string;
|
|
@@ -47,7 +47,7 @@ interface RecordingSession {
|
|
|
47
47
|
websocketRecordings: WebSocketRecording[];
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
|
|
50
|
+
type PlaywrightTestInfo = Pick<TestInfo, 'title' | 'titlePath'>;
|
|
51
51
|
/**
|
|
52
52
|
* Set the proxy mode for a given session
|
|
53
53
|
* @param mode - The proxy mode to set (recording, replay, transparent)
|
|
@@ -57,6 +57,10 @@ type PlaywrightTestInfo = Pick<TestInfo, 'title'>;
|
|
|
57
57
|
declare function setProxyMode(mode: Mode, sessionId?: string, timeout?: number): Promise<void>;
|
|
58
58
|
/**
|
|
59
59
|
* Generate a session ID from test info
|
|
60
|
+
* Uses titlePath to create folder structure with test file name
|
|
61
|
+
* Supports both .spec.ts and .test.ts extensions
|
|
62
|
+
* Example: ['jobs/Create.spec.ts', 'create a job'] becomes 'jobs/Create__create-a-job'
|
|
63
|
+
* Example: ['users/Auth.test.ts', 'login test'] becomes 'users/Auth__login-test'
|
|
60
64
|
* @param testInfo - Playwright test info object
|
|
61
65
|
*/
|
|
62
66
|
declare function generateSessionId(testInfo: PlaywrightTestInfo): string;
|