test-proxy-recorder 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 asmyshlyaev177
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,363 @@
1
+ # test-proxy-recorder
2
+
3
+ HTTP proxy server for recording and replaying network requests in testing. Works seamlessly with Playwright and other testing frameworks.
4
+
5
+ ### BETA VERSION, NOT STABLE FOR PRODUCTION USE
6
+
7
+ ## Features
8
+
9
+ - **Record Mode**: Capture HTTP/HTTPS requests and responses, including WebSocket connections
10
+ - **Replay Mode**: Replay captured requests from disk without hitting real endpoints
11
+ - **Transparent Mode**: Act as a simple proxy without recording or replaying
12
+ - **Playwright Integration**: Built-in fixture for easy integration with Playwright tests
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
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install test-proxy-recorder
21
+ # or
22
+ pnpm add test-proxy-recorder
23
+ # or
24
+ yarn add test-proxy-recorder
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### Standalone Usage
30
+
31
+ ```typescript
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()`:
47
+
48
+ ```typescript
49
+ import { test } from '@playwright/test';
50
+ import { playwrightProxy } from 'test-proxy-recorder';
51
+
52
+ test('record API responses', async ({ page }, testInfo) => {
53
+ // Set proxy to recording mode for this test
54
+ await playwrightProxy.before(testInfo, 'recording');
55
+
56
+ // Make requests - they will be recorded
57
+ await page.goto('http://localhost:8080/api/data');
58
+
59
+ // Your test assertions here
60
+ await expect(page.getByText('Data loaded')).toBeVisible();
61
+
62
+ // Clean up - return to transparent mode
63
+ await playwrightProxy.after(testInfo);
64
+ });
65
+
66
+ test('replay recorded responses', async ({ page }, testInfo) => {
67
+ // Set proxy to replay mode - uses recording from test above
68
+ await playwrightProxy.before(testInfo, 'replay');
69
+
70
+ // This will use recorded responses instead of hitting the real API
71
+ await page.goto('http://localhost:8080/api/data');
72
+
73
+ await expect(page.getByText('Data loaded')).toBeVisible();
74
+
75
+ await playwrightProxy.after(testInfo);
76
+ });
77
+ ```
78
+
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
+ ## How It Works
99
+
100
+ The proxy server runs continuously and can switch between three modes:
101
+
102
+ ### 1. Transparent Mode (Default)
103
+
104
+ Simply proxies requests to the backend without recording or replaying.
105
+
106
+ ### 2. Record Mode
107
+
108
+ Captures all HTTP requests/responses and WebSocket messages to disk. Each test gets its own recording file based on the test name.
109
+
110
+ ### 3. Replay Mode
111
+
112
+ Replays previously recorded responses from disk instead of hitting the real API. Perfect for fast, deterministic tests.
113
+
114
+ ## Modes Control
115
+
116
+ ### Via Playwright (Recommended)
117
+
118
+ ```typescript
119
+ // Recording mode
120
+ await playwrightProxy.before(testInfo, 'recording');
121
+ // ... test code ...
122
+ await playwrightProxy.after(testInfo);
123
+
124
+ // Replay mode
125
+ await playwrightProxy.before(testInfo, 'replay');
126
+ // ... test code ...
127
+ await playwrightProxy.after(testInfo);
128
+ ```
129
+
130
+ ### Via HTTP Control Endpoint
131
+
132
+ ```bash
133
+ # Switch to record mode
134
+ curl -X POST http://localhost:8080/__proxy_control__ \
135
+ -H "Content-Type: application/json" \
136
+ -d '{"mode": "record", "id": "test-session-1", "timeout": 30000}'
137
+
138
+ # Switch to replay mode
139
+ curl -X POST http://localhost:8080/__proxy_control__ \
140
+ -H "Content-Type: application/json" \
141
+ -d '{"mode": "replay", "id": "test-session-1"}'
142
+
143
+ # Switch to transparent mode
144
+ curl -X POST http://localhost:8080/__proxy_control__ \
145
+ -H "Content-Type: application/json" \
146
+ -d '{"mode": "transparent", "id": "test-session-1"}'
147
+ ```
148
+
149
+ ## CLI Usage
150
+
151
+ ```bash
152
+ # Start proxy server
153
+ test-proxy-recorder --port 8080 --target http://localhost:3000 --recordings ./recordings
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
160
+ ```
161
+
162
+ ### CLI Options
163
+
164
+ - `--port, -p`: Port to listen on (default: 8080)
165
+ - `--target, -t`: Backend target URL (can be specified multiple times)
166
+ - `--recordings, -r`: Directory to store recordings (default: ./recordings)
167
+
168
+ ## API
169
+
170
+ ### ProxyServer
171
+
172
+ ```typescript
173
+ class ProxyServer {
174
+ constructor(targets: string[], recordingsDir: string);
175
+
176
+ async init(): Promise<void>;
177
+ listen(port: number): http.Server;
178
+ }
179
+ ```
180
+
181
+ ### Control Endpoint
182
+
183
+ Send POST requests to `/__proxy_control__` with JSON body:
184
+
185
+ ```typescript
186
+ interface ControlRequest {
187
+ mode: 'transparent' | 'record' | 'replay';
188
+ id?: string; // Required for record/replay modes
189
+ timeout?: number; // Auto-switch to transparent mode after timeout (ms)
190
+ }
191
+ ```
192
+
193
+ ### Playwright Integration API
194
+
195
+ ```typescript
196
+ import { playwrightProxy } from 'test-proxy-recorder';
197
+
198
+ // Main helper object for use with Playwright tests
199
+ const playwrightProxy = {
200
+ // Set proxy mode before test
201
+ async before(testInfo: PlaywrightTestInfo, mode: 'recording' | 'replay' | 'transparent'): Promise<void>;
202
+
203
+ // Reset to transparent mode after test
204
+ async after(testInfo: PlaywrightTestInfo): Promise<void>;
205
+ };
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
+ ```
232
+
233
+ ## Recording Format
234
+
235
+ Recordings are stored as JSON files in the recordings directory:
236
+
237
+ ```
238
+ recordings/
239
+ ├── test-session-1.json
240
+ ├── test-session-2.json
241
+ └── ...
242
+ ```
243
+
244
+ Each recording contains:
245
+
246
+ - Request/response pairs with headers and bodies
247
+ - WebSocket messages with timestamps
248
+ - Unique keys for request matching during replay
249
+
250
+ ## Typical Workflow
251
+
252
+ 1. **Start the proxy server** (runs continuously):
253
+
254
+ ```bash
255
+ test-proxy-recorder http://localhost:3000 --port 8080
256
+ ```
257
+
258
+ 2. **Configure your app** to use the proxy:
259
+
260
+ ```bash
261
+ export EXTERNAL_API_URL=http://localhost:8080
262
+ ```
263
+
264
+ 3. **Record responses** (first run):
265
+
266
+ ```typescript
267
+ test('my test', async ({ page }, testInfo) => {
268
+ await playwrightProxy.before(testInfo, 'recording');
269
+ // Test interacts with real API through proxy
270
+ await page.goto('/my-page');
271
+ await playwrightProxy.after(testInfo);
272
+ });
273
+ ```
274
+
275
+ 4. **Replay responses** (subsequent runs):
276
+
277
+ ```typescript
278
+ test('my test', async ({ page }, testInfo) => {
279
+ await playwrightProxy.before(testInfo, 'replay');
280
+ // Test uses recorded responses - no real API calls
281
+ await page.goto('/my-page');
282
+ await playwrightProxy.after(testInfo);
283
+ });
284
+ ```
285
+
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
+ ## Requirements
345
+
346
+ - Node.js >= 22.0.0
347
+ - For Playwright integration: @playwright/test >= 1.0.0
348
+
349
+ ## License
350
+
351
+ MIT
352
+
353
+ ## Contributing
354
+
355
+ 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)
@@ -0,0 +1,39 @@
1
+ import http from 'node:http';
2
+ export declare class ProxyServer {
3
+ private targets;
4
+ private currentTargetIndex;
5
+ private mode;
6
+ private recordingId;
7
+ private replayId;
8
+ private modeTimeout;
9
+ private proxy;
10
+ private currentSession;
11
+ private recordingsDir;
12
+ constructor(targets: string[], recordingsDir: string);
13
+ init(): Promise<void>;
14
+ listen(port: number): http.Server;
15
+ private setupProxyEventHandlers;
16
+ private handleProxyError;
17
+ private handleProxyResponse;
18
+ private getTarget;
19
+ private handleControlRequest;
20
+ private clearModeTimeout;
21
+ private switchMode;
22
+ private switchToTransparentMode;
23
+ private switchToRecordMode;
24
+ private switchToReplayMode;
25
+ private setupModeTimeout;
26
+ private saveCurrentSession;
27
+ private saveRequestRecord;
28
+ private recordResponse;
29
+ private handleReplayRequest;
30
+ private handleReplayError;
31
+ private handleRequest;
32
+ private handleProxyRequest;
33
+ private bufferRequestForRecord;
34
+ private handleUpgrade;
35
+ private handleRecordWebSocket;
36
+ private handleReplayWebSocket;
37
+ private logServerStartup;
38
+ }
39
+ //# sourceMappingURL=ProxyServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProxyServer.d.ts","sourceRoot":"","sources":["../src/ProxyServer.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AA8B7B,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAS;gBAElB,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,MAAM;IAiB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM;IAiBjC,OAAO,CAAC,uBAAuB;IAK/B,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,SAAS;YAOH,oBAAoB;IA8BlC,OAAO,CAAC,gBAAgB;YAOV,UAAU;IA8BxB,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,gBAAgB;YAWV,kBAAkB;YAoBlB,iBAAiB;YAuBjB,cAAc;YAmCd,mBAAmB;IA6BjC,OAAO,CAAC,iBAAiB;YAoBX,aAAa;YAeb,kBAAkB;YAclB,sBAAsB;IAepC,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,qBAAqB;IA2G7B,OAAO,CAAC,qBAAqB;IAqG7B,OAAO,CAAC,gBAAgB;CAQzB"}