playwright-api-logger 1.0.0 → 2.0.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/README.md +85 -76
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/withApiLogging.d.ts +38 -0
- package/dist/withApiLogging.d.ts.map +1 -0
- package/dist/withApiLogging.js +145 -0
- package/dist/withApiLogging.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,38 +24,38 @@
|
|
|
24
24
|
flowchart TD
|
|
25
25
|
subgraph T[Playwright Test]
|
|
26
26
|
F[Fixture setup]
|
|
27
|
-
C[API Call
|
|
27
|
+
C[API Call GET/POST/PUT/DELETE]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
subgraph
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
subgraph P[playwright-api-logger]
|
|
31
|
+
W["withApiLogging(request, testInfo)"]
|
|
32
|
+
PR[Proxy over APIRequestContext]
|
|
33
|
+
AL[ApiLogger]
|
|
34
|
+
CG[CurlGenerator]
|
|
34
35
|
end
|
|
35
36
|
|
|
36
|
-
subgraph
|
|
37
|
-
LOG[logs/TEST_*.log
|
|
37
|
+
subgraph O[Output]
|
|
38
|
+
LOG["logs/TEST_*.log<br/>request, response, curl, duration"]
|
|
38
39
|
RC[Ready-to-use curl for Postman / terminal]
|
|
39
40
|
end
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
C -->
|
|
44
|
-
|
|
45
|
-
%% logging interaction
|
|
46
|
-
B --> AL
|
|
42
|
+
F -->|"1 line change"| W
|
|
43
|
+
W --> PR
|
|
44
|
+
C --> PR
|
|
45
|
+
PR --> AL
|
|
47
46
|
AL --> LOG
|
|
48
|
-
|
|
49
|
-
%% curl generation
|
|
50
|
-
B --> CG
|
|
47
|
+
AL --> CG
|
|
51
48
|
CG --> RC
|
|
52
49
|
```
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
```
|
|
52
|
+
API_LOGS=true → Logging ON (files created in logs/)
|
|
53
|
+
API_LOGS=false → Logging OFF (zero overhead, default)
|
|
54
|
+
```
|
|
56
55
|
|
|
57
56
|
## Features
|
|
58
57
|
|
|
58
|
+
- **One-line integration** — just wrap `request` with `withApiLogging()`, zero changes to controllers/clients
|
|
59
59
|
- **Full Logging** — method, URL, headers, request/response body, status, timing
|
|
60
60
|
- **Curl Export** — copy from log, paste into terminal or import into Postman
|
|
61
61
|
- **Env Control** — `API_LOGS=true/false` (default: `false`, zero overhead when off)
|
|
@@ -72,58 +72,23 @@ npm install playwright-api-logger
|
|
|
72
72
|
|
|
73
73
|
## Quick Start
|
|
74
74
|
|
|
75
|
-
###
|
|
75
|
+
### One line in your fixture — that's it!
|
|
76
76
|
|
|
77
77
|
```typescript
|
|
78
|
-
import {
|
|
78
|
+
import { withApiLogging } from 'playwright-api-logger';
|
|
79
79
|
|
|
80
80
|
export const test = base.extend({
|
|
81
81
|
apiClient: async ({ request }, use, testInfo) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const logger = createApiLogger(testInfo.title, 'test');
|
|
86
|
-
apiClient.setApiLogger(logger);
|
|
87
|
-
await use(apiClient);
|
|
88
|
-
logger.finalize(testInfo.status === 'passed' ? 'PASSED' : 'FAILED');
|
|
89
|
-
} else {
|
|
90
|
-
await use(apiClient);
|
|
91
|
-
}
|
|
82
|
+
// Just wrap request — all API calls are logged automatically
|
|
83
|
+
const apiClient = new ApiClient(withApiLogging(request, testInfo));
|
|
84
|
+
await use(apiClient);
|
|
92
85
|
},
|
|
93
86
|
});
|
|
94
87
|
```
|
|
95
88
|
|
|
96
|
-
|
|
89
|
+
No changes to your controllers, clients, or test files.
|
|
97
90
|
|
|
98
|
-
|
|
99
|
-
import { ApiLogger } from 'playwright-api-logger';
|
|
100
|
-
|
|
101
|
-
class BaseApiController {
|
|
102
|
-
protected apiLogger: ApiLogger | null = null;
|
|
103
|
-
|
|
104
|
-
setApiLogger(logger: ApiLogger): void {
|
|
105
|
-
this.apiLogger = logger;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async get(url: string, headers?: Record<string, string>) {
|
|
109
|
-
const startTime = Date.now();
|
|
110
|
-
const response = await this.request.get(url, { headers });
|
|
111
|
-
const duration = Date.now() - startTime;
|
|
112
|
-
|
|
113
|
-
if (this.apiLogger?.isEnabled()) {
|
|
114
|
-
const body = await response.json().catch(() => response.text());
|
|
115
|
-
this.apiLogger.logApiCall(
|
|
116
|
-
'GET', url, headers, undefined,
|
|
117
|
-
response.status(), undefined, body, duration
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return response;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Step 3. Enable via environment variable
|
|
91
|
+
### Enable via environment variable
|
|
127
92
|
|
|
128
93
|
```bash
|
|
129
94
|
# .env
|
|
@@ -135,6 +100,31 @@ API_LOGS=false
|
|
|
135
100
|
API_LOGS=true npx playwright test
|
|
136
101
|
```
|
|
137
102
|
|
|
103
|
+
### Finalize with test result (optional)
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
apiClient: async ({ request }, use, testInfo) => {
|
|
107
|
+
const loggedRequest = withApiLogging(request, testInfo);
|
|
108
|
+
const apiClient = new ApiClient(loggedRequest);
|
|
109
|
+
await use(apiClient);
|
|
110
|
+
|
|
111
|
+
// Write PASSED/FAILED to log
|
|
112
|
+
loggedRequest.__logger.finalize(
|
|
113
|
+
testInfo.status === 'passed' ? 'PASSED' : 'FAILED'
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Setup / Teardown logging
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// In beforeAll
|
|
122
|
+
const loggedRequest = withApiLogging(request, { testName: 'auth-setup', context: 'setup' });
|
|
123
|
+
|
|
124
|
+
// In afterAll
|
|
125
|
+
const loggedRequest = withApiLogging(request, { testName: 'cleanup', context: 'teardown' });
|
|
126
|
+
```
|
|
127
|
+
|
|
138
128
|
## Log Output
|
|
139
129
|
|
|
140
130
|
Logs are saved to `logs/` directory:
|
|
@@ -170,24 +160,33 @@ Each log entry is a JSON object:
|
|
|
170
160
|
|
|
171
161
|
## API Reference
|
|
172
162
|
|
|
173
|
-
###
|
|
163
|
+
### `withApiLogging(request, testInfoOrOptions?)` ⭐
|
|
174
164
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
165
|
+
Main integration point. Wraps `APIRequestContext` with a Proxy that logs all HTTP calls.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// With TestInfo (recommended)
|
|
169
|
+
const loggedRequest = withApiLogging(request, testInfo);
|
|
170
|
+
|
|
171
|
+
// With options
|
|
172
|
+
const loggedRequest = withApiLogging(request, {
|
|
173
|
+
testName: 'my-test',
|
|
174
|
+
context: 'setup',
|
|
175
|
+
logDirectory: 'custom-logs/',
|
|
176
|
+
maskAuthTokens: true,
|
|
177
|
+
});
|
|
180
178
|
|
|
181
|
-
|
|
179
|
+
// Access logger for finalization
|
|
180
|
+
loggedRequest.__logger.finalize('PASSED');
|
|
181
|
+
```
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
|
186
|
-
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `getLogFilePath()` | Get current log file path |
|
|
183
|
+
### Factory Functions (advanced)
|
|
184
|
+
|
|
185
|
+
| Function | Description |
|
|
186
|
+
|----------|-------------|
|
|
187
|
+
| `createApiLogger(testName, context?)` | Create standalone logger |
|
|
188
|
+
| `createSetupLogger(testName)` | Logger with `'setup'` context |
|
|
189
|
+
| `createTeardownLogger(testName)` | Logger with `'teardown'` context |
|
|
191
190
|
|
|
192
191
|
### `CurlGenerator`
|
|
193
192
|
|
|
@@ -201,8 +200,9 @@ Each log entry is a JSON object:
|
|
|
201
200
|
|-------------|---------|-------------|
|
|
202
201
|
| `API_LOGS` | `false` | Set to `'true'` to enable logging |
|
|
203
202
|
|
|
203
|
+
### `ApiLoggingOptions`
|
|
204
|
+
|
|
204
205
|
```typescript
|
|
205
|
-
// LoggerConfig
|
|
206
206
|
{
|
|
207
207
|
testName?: string; // Test name (default: 'unknown-test')
|
|
208
208
|
context?: LogContext; // 'setup' | 'test' | 'teardown'
|
|
@@ -211,6 +211,15 @@ Each log entry is a JSON object:
|
|
|
211
211
|
}
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
+
## Migration from v1
|
|
215
|
+
|
|
216
|
+
v1 required changes to controllers, clients, and fixtures. v2 needs only **one line**:
|
|
217
|
+
|
|
218
|
+
```diff
|
|
219
|
+
- const apiClient = new ApiClient(request);
|
|
220
|
+
+ const apiClient = new ApiClient(withApiLogging(request, testInfo));
|
|
221
|
+
```
|
|
222
|
+
|
|
214
223
|
## License
|
|
215
224
|
|
|
216
225
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { ApiLogger, createApiLogger, createSetupLogger, createTeardownLogger } from './ApiLogger';
|
|
2
2
|
export { CurlGenerator } from './CurlGenerator';
|
|
3
|
+
export { withApiLogging } from './withApiLogging';
|
|
4
|
+
export type { ApiLoggingOptions } from './withApiLogging';
|
|
3
5
|
export type { LoggerConfig, RequestLogData, ResponseLogData, LogEntry, LogContext } from './types';
|
|
4
6
|
export type { RequestData } from './CurlGenerator';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAClG,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnG,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAClG,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnG,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CurlGenerator = exports.createTeardownLogger = exports.createSetupLogger = exports.createApiLogger = exports.ApiLogger = void 0;
|
|
3
|
+
exports.withApiLogging = exports.CurlGenerator = exports.createTeardownLogger = exports.createSetupLogger = exports.createApiLogger = exports.ApiLogger = void 0;
|
|
4
4
|
var ApiLogger_1 = require("./ApiLogger");
|
|
5
5
|
Object.defineProperty(exports, "ApiLogger", { enumerable: true, get: function () { return ApiLogger_1.ApiLogger; } });
|
|
6
6
|
Object.defineProperty(exports, "createApiLogger", { enumerable: true, get: function () { return ApiLogger_1.createApiLogger; } });
|
|
@@ -8,4 +8,6 @@ Object.defineProperty(exports, "createSetupLogger", { enumerable: true, get: fun
|
|
|
8
8
|
Object.defineProperty(exports, "createTeardownLogger", { enumerable: true, get: function () { return ApiLogger_1.createTeardownLogger; } });
|
|
9
9
|
var CurlGenerator_1 = require("./CurlGenerator");
|
|
10
10
|
Object.defineProperty(exports, "CurlGenerator", { enumerable: true, get: function () { return CurlGenerator_1.CurlGenerator; } });
|
|
11
|
+
var withApiLogging_1 = require("./withApiLogging");
|
|
12
|
+
Object.defineProperty(exports, "withApiLogging", { enumerable: true, get: function () { return withApiLogging_1.withApiLogging; } });
|
|
11
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAkG;AAAzF,sGAAA,SAAS,OAAA;AAAE,4GAAA,eAAe,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAC5E,iDAAgD;AAAvC,8GAAA,aAAa,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAkG;AAAzF,sGAAA,SAAS,OAAA;AAAE,4GAAA,eAAe,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAC5E,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proxy-based API logging wrapper for Playwright's APIRequestContext
|
|
3
|
+
* Zero changes to your controllers/clients — just wrap `request` in the fixture
|
|
4
|
+
*/
|
|
5
|
+
import type { APIRequestContext, TestInfo } from '@playwright/test';
|
|
6
|
+
import { ApiLogger } from './ApiLogger';
|
|
7
|
+
import { LogContext } from './types';
|
|
8
|
+
export interface ApiLoggingOptions {
|
|
9
|
+
/** Test name for log filename (auto-detected from testInfo if provided) */
|
|
10
|
+
testName?: string;
|
|
11
|
+
/** Log context: 'setup' | 'test' | 'teardown' (default: 'test') */
|
|
12
|
+
context?: LogContext;
|
|
13
|
+
/** Custom log directory (default: 'logs/') */
|
|
14
|
+
logDirectory?: string;
|
|
15
|
+
/** Mask Authorization headers (default: true) */
|
|
16
|
+
maskAuthTokens?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Wrap Playwright's APIRequestContext with automatic logging.
|
|
20
|
+
* All HTTP calls (get, post, put, patch, delete, head, fetch) are intercepted and logged.
|
|
21
|
+
*
|
|
22
|
+
* @param request - Playwright APIRequestContext
|
|
23
|
+
* @param testInfoOrOptions - TestInfo object or ApiLoggingOptions
|
|
24
|
+
* @returns Proxied APIRequestContext with logging + logger reference via `__logger`
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Minimal — just pass testInfo:
|
|
28
|
+
* const loggedRequest = withApiLogging(request, testInfo);
|
|
29
|
+
* const apiClient = new ApiClient(loggedRequest);
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // With options:
|
|
33
|
+
* const loggedRequest = withApiLogging(request, { testName: 'my-test', context: 'setup' });
|
|
34
|
+
*/
|
|
35
|
+
export declare function withApiLogging(request: APIRequestContext, testInfoOrOptions?: TestInfo | ApiLoggingOptions): APIRequestContext & {
|
|
36
|
+
__logger: ApiLogger;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=withApiLogging.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withApiLogging.d.ts","sourceRoot":"","sources":["../src/withApiLogging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAe,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAEnD,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,iBAAiB,CAAC,EAAE,QAAQ,GAAG,iBAAiB,GAC/C,iBAAiB,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,CA6F7C"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Proxy-based API logging wrapper for Playwright's APIRequestContext
|
|
4
|
+
* Zero changes to your controllers/clients — just wrap `request` in the fixture
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.withApiLogging = withApiLogging;
|
|
8
|
+
const ApiLogger_1 = require("./ApiLogger");
|
|
9
|
+
/**
|
|
10
|
+
* Wrap Playwright's APIRequestContext with automatic logging.
|
|
11
|
+
* All HTTP calls (get, post, put, patch, delete, head, fetch) are intercepted and logged.
|
|
12
|
+
*
|
|
13
|
+
* @param request - Playwright APIRequestContext
|
|
14
|
+
* @param testInfoOrOptions - TestInfo object or ApiLoggingOptions
|
|
15
|
+
* @returns Proxied APIRequestContext with logging + logger reference via `__logger`
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Minimal — just pass testInfo:
|
|
19
|
+
* const loggedRequest = withApiLogging(request, testInfo);
|
|
20
|
+
* const apiClient = new ApiClient(loggedRequest);
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // With options:
|
|
24
|
+
* const loggedRequest = withApiLogging(request, { testName: 'my-test', context: 'setup' });
|
|
25
|
+
*/
|
|
26
|
+
function withApiLogging(request, testInfoOrOptions) {
|
|
27
|
+
// Resolve options
|
|
28
|
+
let options;
|
|
29
|
+
if (testInfoOrOptions && 'title' in testInfoOrOptions) {
|
|
30
|
+
// TestInfo object
|
|
31
|
+
options = {
|
|
32
|
+
testName: testInfoOrOptions.title,
|
|
33
|
+
context: 'test',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
options = testInfoOrOptions || {};
|
|
38
|
+
}
|
|
39
|
+
const config = {
|
|
40
|
+
testName: options.testName,
|
|
41
|
+
context: options.context || 'test',
|
|
42
|
+
logDirectory: options.logDirectory,
|
|
43
|
+
maskAuthTokens: options.maskAuthTokens,
|
|
44
|
+
};
|
|
45
|
+
const logger = new ApiLogger_1.ApiLogger(config);
|
|
46
|
+
// If logging is disabled, return original request with dummy logger
|
|
47
|
+
if (!logger.isEnabled()) {
|
|
48
|
+
return Object.assign(request, { __logger: logger });
|
|
49
|
+
}
|
|
50
|
+
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'fetch'];
|
|
51
|
+
const proxy = new Proxy(request, {
|
|
52
|
+
get(target, prop, receiver) {
|
|
53
|
+
const propName = typeof prop === 'string' ? prop : '';
|
|
54
|
+
// Expose logger reference
|
|
55
|
+
if (propName === '__logger') {
|
|
56
|
+
return logger;
|
|
57
|
+
}
|
|
58
|
+
if (HTTP_METHODS.includes(propName)) {
|
|
59
|
+
return async (url, options) => {
|
|
60
|
+
const method = propName === 'fetch' ? (options?.method || 'GET') : propName;
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
// Extract request details from Playwright options
|
|
63
|
+
const requestHeaders = options?.headers;
|
|
64
|
+
const requestBody = extractBody(options);
|
|
65
|
+
const contentType = extractContentType(options, requestHeaders);
|
|
66
|
+
try {
|
|
67
|
+
const response = await target[propName](url, options);
|
|
68
|
+
const duration = Date.now() - startTime;
|
|
69
|
+
// Parse response body safely
|
|
70
|
+
const responseBody = await safeParseResponseBody(response);
|
|
71
|
+
const responseHeaders = extractResponseHeaders(response);
|
|
72
|
+
logger.logApiCall(method.toUpperCase(), response.url(), requestHeaders, requestBody, response.status(), responseHeaders, responseBody, duration);
|
|
73
|
+
return response;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
const duration = Date.now() - startTime;
|
|
77
|
+
logger.logApiCall(method.toUpperCase(), url, requestHeaders, requestBody, 0, undefined, { error: String(error) }, duration);
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return Reflect.get(target, prop, receiver);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return Object.assign(proxy, { __logger: logger });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Extract body from Playwright request options
|
|
89
|
+
*/
|
|
90
|
+
function extractBody(options) {
|
|
91
|
+
if (!options)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (options.data)
|
|
94
|
+
return options.data;
|
|
95
|
+
if (options.form)
|
|
96
|
+
return options.form;
|
|
97
|
+
if (options.multipart)
|
|
98
|
+
return options.multipart;
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Detect content type from options
|
|
103
|
+
*/
|
|
104
|
+
function extractContentType(options, headers) {
|
|
105
|
+
if (options?.form)
|
|
106
|
+
return 'application/x-www-form-urlencoded';
|
|
107
|
+
if (options?.multipart)
|
|
108
|
+
return 'multipart/form-data';
|
|
109
|
+
if (headers) {
|
|
110
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
111
|
+
if (key.toLowerCase() === 'content-type') {
|
|
112
|
+
return Array.isArray(value) ? value[0] : String(value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Safely parse response body (try JSON, fallback to text)
|
|
120
|
+
*/
|
|
121
|
+
async function safeParseResponseBody(response) {
|
|
122
|
+
try {
|
|
123
|
+
return await response.json();
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
try {
|
|
127
|
+
return await response.text();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Extract headers from APIResponse
|
|
136
|
+
*/
|
|
137
|
+
function extractResponseHeaders(response) {
|
|
138
|
+
try {
|
|
139
|
+
return response.headers();
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=withApiLogging.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withApiLogging.js","sourceRoot":"","sources":["../src/withApiLogging.ts"],"names":[],"mappings":"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"}
|