test-proxy-recorder 0.1.1 → 0.1.2
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 +79 -172
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# test-proxy-recorder
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/test-proxy-recorder)
|
|
4
|
+
[](https://github.com/asmyshlyaev177/test-proxy-recorder/blob/master/LICENSE)
|
|
5
|
+
|
|
3
6
|
HTTP proxy server for recording and replaying network requests in testing. Works seamlessly with Playwright and other testing frameworks.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
## BETA VERSION
|
|
6
9
|
|
|
7
10
|
## Features
|
|
8
11
|
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **WebSocket Support**: Full support for recording and replaying WebSocket connections
|
|
14
|
-
- **Multiple Targets**: Load balance between multiple backend targets
|
|
15
|
-
- **Timeout Control**: Automatic mode switching after configurable timeouts
|
|
12
|
+
- **Fast CI/CD Tests**: Record API responses once with real backend, replay them on CI/CD
|
|
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 NextJS.
|
|
15
|
+
- **Deterministic Tests**: Same responses every time, no flaky network issues, no need to wire up the whole Backend API for testing
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
@@ -26,40 +26,23 @@ yarn add test-proxy-recorder
|
|
|
26
26
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
import { ProxyServer } from 'test-proxy-recorder';
|
|
33
|
-
|
|
34
|
-
const proxy = new ProxyServer(
|
|
35
|
-
['http://localhost:3000'], // backend targets
|
|
36
|
-
'./recordings' // directory to store recordings
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
await proxy.init();
|
|
40
|
-
const server = proxy.listen(8080);
|
|
41
|
-
console.log('Proxy running on http://localhost:8080');
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### With Playwright
|
|
45
|
-
|
|
46
|
-
The proxy runs continuously in the background. Tests control the recording/replay mode using `playwrightProxy.before()` and `playwrightProxy.after()`:
|
|
29
|
+
1. Run proxy with your backend API as a target `test-proxy-recorder --port 8100 --target http://localhost:8000 --recordings ./recordings`, here your backend on port 8000 as target, proxy on port 8100.
|
|
30
|
+
2. Point your Frontend app to proxy port, 8100 as example
|
|
31
|
+
3. The proxy runs continuously in the background. Tests control the recording/replay mode using `playwrightProxy.before()` and `playwrightProxy.after()`:
|
|
47
32
|
|
|
48
33
|
```typescript
|
|
49
34
|
import { test } from '@playwright/test';
|
|
50
35
|
import { playwrightProxy } from 'test-proxy-recorder';
|
|
51
36
|
|
|
52
|
-
test('
|
|
53
|
-
// Set proxy to recording mode
|
|
54
|
-
await playwrightProxy.before(testInfo, '
|
|
37
|
+
test('Test UI with API responses', async ({ page }, testInfo) => {
|
|
38
|
+
// Set proxy to recording mode to record mocks, sanitized test title will be used as file name
|
|
39
|
+
await playwrightProxy.before(testInfo, 'record');
|
|
55
40
|
|
|
56
41
|
// Make requests - they will be recorded
|
|
57
|
-
await page.goto('
|
|
58
|
-
|
|
59
|
-
// Your test assertions here
|
|
60
|
-
await expect(page.getByText('Data loaded')).toBeVisible();
|
|
42
|
+
await page.goto('/myPage');
|
|
43
|
+
/// ... test content ...
|
|
61
44
|
|
|
62
|
-
//
|
|
45
|
+
// Save mock and return to transparent mode
|
|
63
46
|
await playwrightProxy.after(testInfo);
|
|
64
47
|
});
|
|
65
48
|
|
|
@@ -67,34 +50,13 @@ test('replay recorded responses', async ({ page }, testInfo) => {
|
|
|
67
50
|
// Set proxy to replay mode - uses recording from test above
|
|
68
51
|
await playwrightProxy.before(testInfo, 'replay');
|
|
69
52
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
await expect(page.getByText('Data loaded')).toBeVisible();
|
|
53
|
+
await page.goto('/myPage');
|
|
54
|
+
/// ... test content ...
|
|
74
55
|
|
|
75
56
|
await playwrightProxy.after(testInfo);
|
|
76
57
|
});
|
|
77
58
|
```
|
|
78
59
|
|
|
79
|
-
You can also use standalone functions for more control:
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
import { test } from '@playwright/test';
|
|
83
|
-
import { setProxyMode, generateSessionId } from 'test-proxy-recorder';
|
|
84
|
-
|
|
85
|
-
test('custom proxy control', async ({ page }, testInfo) => {
|
|
86
|
-
const sessionId = generateSessionId(testInfo);
|
|
87
|
-
|
|
88
|
-
// Start recording with a 30s timeout
|
|
89
|
-
await setProxyMode('recording', sessionId, 30000);
|
|
90
|
-
|
|
91
|
-
await page.goto('http://localhost:8080/api/data');
|
|
92
|
-
|
|
93
|
-
// Switch to transparent mode
|
|
94
|
-
await setProxyMode('transparent', sessionId);
|
|
95
|
-
});
|
|
96
|
-
```
|
|
97
|
-
|
|
98
60
|
## How It Works
|
|
99
61
|
|
|
100
62
|
The proxy server runs continuously and can switch between three modes:
|
|
@@ -113,7 +75,7 @@ Replays previously recorded responses from disk instead of hitting the real API.
|
|
|
113
75
|
|
|
114
76
|
## Modes Control
|
|
115
77
|
|
|
116
|
-
### Via Playwright
|
|
78
|
+
### Via Playwright
|
|
117
79
|
|
|
118
80
|
```typescript
|
|
119
81
|
// Recording mode
|
|
@@ -129,40 +91,76 @@ await playwrightProxy.after(testInfo);
|
|
|
129
91
|
|
|
130
92
|
### Via HTTP Control Endpoint
|
|
131
93
|
|
|
94
|
+
Using curl:
|
|
95
|
+
|
|
132
96
|
```bash
|
|
133
97
|
# Switch to record mode
|
|
134
|
-
curl -X POST http://localhost:
|
|
98
|
+
curl -X POST http://localhost:8100/__control \
|
|
135
99
|
-H "Content-Type: application/json" \
|
|
136
|
-
-d '{"mode": "record", "id": "
|
|
100
|
+
-d '{"mode": "record", "id": "my-testfile-1", "timeout": 30000}'
|
|
137
101
|
|
|
138
102
|
# Switch to replay mode
|
|
139
|
-
curl -X POST http://localhost:
|
|
103
|
+
curl -X POST http://localhost:8100/__control \
|
|
140
104
|
-H "Content-Type: application/json" \
|
|
141
|
-
-d '{"mode": "replay", "id": "
|
|
105
|
+
-d '{"mode": "replay", "id": "my-testfile-1"}'
|
|
142
106
|
|
|
143
107
|
# Switch to transparent mode
|
|
144
|
-
curl -X POST http://localhost:
|
|
108
|
+
curl -X POST http://localhost:8100/__control \
|
|
145
109
|
-H "Content-Type: application/json" \
|
|
146
|
-
-d '{"mode": "transparent", "id": "
|
|
110
|
+
-d '{"mode": "transparent", "id": "my-testfile-1"}'
|
|
147
111
|
```
|
|
148
112
|
|
|
149
|
-
|
|
113
|
+
Using JavaScript fetch:
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
// Switch to record mode
|
|
117
|
+
await fetch('http://localhost:8100/__control', {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: {
|
|
120
|
+
'Content-Type': 'application/json',
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify({
|
|
123
|
+
mode: 'record',
|
|
124
|
+
id: 'my-testfile-1',
|
|
125
|
+
timeout: 30000
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Switch to replay mode
|
|
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
|
+
});
|
|
140
|
+
|
|
141
|
+
// Switch to transparent mode
|
|
142
|
+
await fetch('http://localhost:8100/__control', {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers: {
|
|
145
|
+
'Content-Type': 'application/json',
|
|
146
|
+
},
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
mode: 'transparent',
|
|
149
|
+
})
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Usage
|
|
150
154
|
|
|
151
155
|
```bash
|
|
152
156
|
# Start proxy server
|
|
153
|
-
test-proxy-recorder --port
|
|
154
|
-
|
|
155
|
-
# With multiple targets for load balancing
|
|
156
|
-
test-proxy-recorder --port 8080 \
|
|
157
|
-
--target http://localhost:3000 \
|
|
158
|
-
--target http://localhost:3001 \
|
|
159
|
-
--recordings ./recordings
|
|
157
|
+
test-proxy-recorder --port 8100 --target http://localhost:8000 --recordings ./recordings
|
|
160
158
|
```
|
|
161
159
|
|
|
162
160
|
### CLI Options
|
|
163
161
|
|
|
164
162
|
- `--port, -p`: Port to listen on (default: 8080)
|
|
165
|
-
- `--target, -t`: Backend target URL (can
|
|
163
|
+
- `--target, -t`: Backend target URL (can add multiple targets)
|
|
166
164
|
- `--recordings, -r`: Directory to store recordings (default: ./recordings)
|
|
167
165
|
|
|
168
166
|
## API
|
|
@@ -180,13 +178,13 @@ class ProxyServer {
|
|
|
180
178
|
|
|
181
179
|
### Control Endpoint
|
|
182
180
|
|
|
183
|
-
Send POST requests to `/
|
|
181
|
+
Send POST requests to `/__control` with JSON body:
|
|
184
182
|
|
|
185
183
|
```typescript
|
|
186
184
|
interface ControlRequest {
|
|
187
185
|
mode: 'transparent' | 'record' | 'replay';
|
|
188
|
-
id?: string; //
|
|
189
|
-
timeout?: number; // Auto-switch to transparent mode after timeout (ms)
|
|
186
|
+
id?: string; // Will be used as file name for recordings, required for record/replay modes
|
|
187
|
+
timeout?: number; // Auto-switch to transparent mode after timeout (ms), default 120 seconds
|
|
190
188
|
}
|
|
191
189
|
```
|
|
192
190
|
|
|
@@ -198,36 +196,11 @@ import { playwrightProxy } from 'test-proxy-recorder';
|
|
|
198
196
|
// Main helper object for use with Playwright tests
|
|
199
197
|
const playwrightProxy = {
|
|
200
198
|
// Set proxy mode before test
|
|
201
|
-
async before(testInfo: PlaywrightTestInfo, mode: '
|
|
199
|
+
async before(testInfo: PlaywrightTestInfo, mode: 'record' | 'replay' | 'transparent'): Promise<void>;
|
|
202
200
|
|
|
203
201
|
// Reset to transparent mode after test
|
|
204
202
|
async after(testInfo: PlaywrightTestInfo): Promise<void>;
|
|
205
203
|
};
|
|
206
|
-
|
|
207
|
-
// Standalone functions for custom control:
|
|
208
|
-
import {
|
|
209
|
-
setProxyMode, // Set mode with custom session ID
|
|
210
|
-
generateSessionId, // Generate session ID from test info
|
|
211
|
-
startRecording, // Helper to start recording
|
|
212
|
-
startReplay, // Helper to start replay
|
|
213
|
-
stopProxy // Helper to stop recording/replay
|
|
214
|
-
} from 'test-proxy-recorder';
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Usage Pattern
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
test('your test', async ({ page }, testInfo) => {
|
|
221
|
-
// Setup: Set proxy mode at start of test
|
|
222
|
-
await playwrightProxy.before(testInfo, 'replay');
|
|
223
|
-
|
|
224
|
-
// Your test code here
|
|
225
|
-
await page.goto('/your-page');
|
|
226
|
-
await expect(page.getByText('Something')).toBeVisible();
|
|
227
|
-
|
|
228
|
-
// Cleanup: Return to transparent mode
|
|
229
|
-
await playwrightProxy.after(testInfo);
|
|
230
|
-
});
|
|
231
204
|
```
|
|
232
205
|
|
|
233
206
|
## Recording Format
|
|
@@ -252,20 +225,20 @@ Each recording contains:
|
|
|
252
225
|
1. **Start the proxy server** (runs continuously):
|
|
253
226
|
|
|
254
227
|
```bash
|
|
255
|
-
test-proxy-recorder http://localhost:
|
|
228
|
+
test-proxy-recorder http://localhost:8000 --port 8100
|
|
256
229
|
```
|
|
257
230
|
|
|
258
231
|
2. **Configure your app** to use the proxy:
|
|
259
232
|
|
|
260
233
|
```bash
|
|
261
|
-
export EXTERNAL_API_URL=http://localhost:
|
|
234
|
+
export EXTERNAL_API_URL=http://localhost:8100 yarn dev
|
|
262
235
|
```
|
|
263
236
|
|
|
264
237
|
3. **Record responses** (first run):
|
|
265
238
|
|
|
266
239
|
```typescript
|
|
267
240
|
test('my test', async ({ page }, testInfo) => {
|
|
268
|
-
await playwrightProxy.before(testInfo, '
|
|
241
|
+
await playwrightProxy.before(testInfo, 'record');
|
|
269
242
|
// Test interacts with real API through proxy
|
|
270
243
|
await page.goto('/my-page');
|
|
271
244
|
await playwrightProxy.after(testInfo);
|
|
@@ -283,68 +256,10 @@ Each recording contains:
|
|
|
283
256
|
});
|
|
284
257
|
```
|
|
285
258
|
|
|
286
|
-
## Use Cases
|
|
287
|
-
|
|
288
|
-
- **Fast CI/CD Tests**: Record API responses once, replay them for instant test execution
|
|
289
|
-
- **Deterministic Tests**: Same responses every time, no flaky network issues
|
|
290
|
-
- **API Contract Testing**: Verify frontend handles all API scenarios
|
|
291
|
-
- **WebSocket Testing**: Record and replay complex WebSocket message sequences
|
|
292
|
-
|
|
293
|
-
## Configuration Examples
|
|
294
|
-
|
|
295
|
-
### With Environment Variables
|
|
296
|
-
|
|
297
|
-
```typescript
|
|
298
|
-
const proxy = new ProxyServer(
|
|
299
|
-
[process.env.BACKEND_URL || 'http://localhost:3000'],
|
|
300
|
-
process.env.RECORDINGS_DIR || './recordings'
|
|
301
|
-
);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Multiple Backend Targets
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
// Round-robin load balancing between targets
|
|
308
|
-
const proxy = new ProxyServer(
|
|
309
|
-
[
|
|
310
|
-
'http://backend-1:3000',
|
|
311
|
-
'http://backend-2:3000',
|
|
312
|
-
'http://backend-3:3000'
|
|
313
|
-
],
|
|
314
|
-
'./recordings'
|
|
315
|
-
);
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
## WebSocket Support
|
|
319
|
-
|
|
320
|
-
WebSocket connections are automatically detected and recorded/replayed:
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
test('websocket test', async ({ page }, testInfo) => {
|
|
324
|
-
// Recording WebSocket messages
|
|
325
|
-
await playwrightProxy.before(testInfo, 'recording');
|
|
326
|
-
|
|
327
|
-
await page.goto('/websocket-page');
|
|
328
|
-
// WebSocket connections and messages are recorded
|
|
329
|
-
|
|
330
|
-
await playwrightProxy.after(testInfo);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
test('websocket replay', async ({ page }, testInfo) => {
|
|
334
|
-
// Replay WebSocket messages
|
|
335
|
-
await playwrightProxy.before(testInfo, 'replay');
|
|
336
|
-
|
|
337
|
-
await page.goto('/websocket-page');
|
|
338
|
-
// Previously recorded WebSocket messages are replayed
|
|
339
|
-
|
|
340
|
-
await playwrightProxy.after(testInfo);
|
|
341
|
-
});
|
|
342
|
-
```
|
|
343
|
-
|
|
344
259
|
## Requirements
|
|
345
260
|
|
|
346
261
|
- Node.js >= 22.0.0
|
|
347
|
-
-
|
|
262
|
+
- @playwright/test >= 1.0.0
|
|
348
263
|
|
|
349
264
|
## License
|
|
350
265
|
|
|
@@ -353,11 +268,3 @@ MIT
|
|
|
353
268
|
## Contributing
|
|
354
269
|
|
|
355
270
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
356
|
-
|
|
357
|
-
## Author
|
|
358
|
-
|
|
359
|
-
asmyshlyaev177
|
|
360
|
-
|
|
361
|
-
## Repository
|
|
362
|
-
|
|
363
|
-
[https://github.com/asmyshlyaev177/test-proxy-recorder](https://github.com/asmyshlyaev177/test-proxy-recorder)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "test-proxy-recorder",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "HTTP proxy server for recording and replaying network requests in testing. Works seamlessly with Playwright testing framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
81
|
"commander": "^12.0.0",
|
|
82
|
+
"filenamify": "^7.0.1",
|
|
82
83
|
"http-proxy": "^1.18.1"
|
|
83
84
|
},
|
|
84
85
|
"peerDependencies": {
|