playwright-api-logger 1.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/LICENSE +21 -0
- package/README.md +216 -0
- package/dist/ApiLogger.d.ts +78 -0
- package/dist/ApiLogger.d.ts.map +1 -0
- package/dist/ApiLogger.js +228 -0
- package/dist/ApiLogger.js.map +1 -0
- package/dist/CurlGenerator.d.ts +66 -0
- package/dist/CurlGenerator.d.ts.map +1 -0
- package/dist/CurlGenerator.js +178 -0
- package/dist/CurlGenerator.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maіevskyi Leonid
|
|
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,216 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://playwright.dev/img/playwright-logo.svg" width="80" alt="Playwright Logo" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">playwright-api-logger</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Comprehensive API request/response logger with curl export for Playwright tests
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/playwright-api-logger"><img src="https://img.shields.io/npm/v/playwright-api-logger.svg?style=flat-square&color=cb3837" alt="npm version" /></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/playwright-api-logger"><img src="https://img.shields.io/npm/dm/playwright-api-logger.svg?style=flat-square&color=blue" alt="npm downloads" /></a>
|
|
14
|
+
<a href="https://github.com/AZANIR/playwright-api-logger/blob/master/LICENSE"><img src="https://img.shields.io/github/license/AZANIR/playwright-api-logger?style=flat-square" alt="license" /></a>
|
|
15
|
+
<a href="https://playwright.dev/"><img src="https://img.shields.io/badge/Playwright-%3E%3D1.40-45ba4b?style=flat-square&logo=playwright" alt="Playwright" /></a>
|
|
16
|
+
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.0+-3178c6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## How It Works
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
flowchart TD
|
|
25
|
+
subgraph T[Playwright Test]
|
|
26
|
+
F[Fixture setup]
|
|
27
|
+
C[API Call (GET / POST)]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
subgraph L[Logging & API layer]
|
|
31
|
+
AL[ApiLogger (per test)]
|
|
32
|
+
B[BaseApi Controller]
|
|
33
|
+
CG[Curl Generator]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
subgraph FS[File system]
|
|
37
|
+
LOG[logs/TEST_*.log { request, response, curl, duration }]
|
|
38
|
+
RC[Ready-to-use curl for Postman / terminal]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
%% test flow
|
|
42
|
+
F --> AL
|
|
43
|
+
C --> B
|
|
44
|
+
|
|
45
|
+
%% logging interaction
|
|
46
|
+
B --> AL
|
|
47
|
+
AL --> LOG
|
|
48
|
+
|
|
49
|
+
%% curl generation
|
|
50
|
+
B --> CG
|
|
51
|
+
CG --> RC
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`API_LOGS=true` → **Logging ON** (files created in `logs/`)
|
|
55
|
+
`API_LOGS=false` → **Logging OFF** (zero overhead, default)
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
- **Full Logging** — method, URL, headers, request/response body, status, timing
|
|
60
|
+
- **Curl Export** — copy from log, paste into terminal or import into Postman
|
|
61
|
+
- **Env Control** — `API_LOGS=true/false` (default: `false`, zero overhead when off)
|
|
62
|
+
- **Context Tracking** — setup / test / teardown phases
|
|
63
|
+
- **Token Masking** — Authorization headers are automatically masked
|
|
64
|
+
- **Form Data** — JSON, URL-encoded, and multipart/form-data support
|
|
65
|
+
- **Error Resilient** — logging never breaks your tests
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install playwright-api-logger
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick Start
|
|
74
|
+
|
|
75
|
+
### Step 1. Add logger to your fixture
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { createApiLogger } from 'playwright-api-logger';
|
|
79
|
+
|
|
80
|
+
export const test = base.extend({
|
|
81
|
+
apiClient: async ({ request }, use, testInfo) => {
|
|
82
|
+
const apiClient = new ApiClient(request);
|
|
83
|
+
|
|
84
|
+
if (process.env.API_LOGS === 'true') {
|
|
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
|
+
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Step 2. Add logging to your base API controller
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
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
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# .env
|
|
130
|
+
API_LOGS=false
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Run with logging enabled
|
|
135
|
+
API_LOGS=true npx playwright test
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Log Output
|
|
139
|
+
|
|
140
|
+
Logs are saved to `logs/` directory:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
logs/
|
|
144
|
+
TEST_my-test-name_2026-03-16T12-00-00.log
|
|
145
|
+
SETUP_auth-setup_2026-03-16T12-00-00.log
|
|
146
|
+
TEARDOWN_cleanup_2026-03-16T12-00-00.log
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Each log entry is a JSON object:
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"timestamp": "2026-03-16T12:00:00.000Z",
|
|
154
|
+
"testName": "my-test-name",
|
|
155
|
+
"context": "test",
|
|
156
|
+
"request": {
|
|
157
|
+
"method": "POST",
|
|
158
|
+
"url": "https://api.example.com/users",
|
|
159
|
+
"headers": { "Content-Type": "application/json" },
|
|
160
|
+
"body": { "name": "John" }
|
|
161
|
+
},
|
|
162
|
+
"response": {
|
|
163
|
+
"status": 201,
|
|
164
|
+
"body": { "id": 1, "name": "John" }
|
|
165
|
+
},
|
|
166
|
+
"duration": 150,
|
|
167
|
+
"curl": "curl -X POST 'https://api.example.com/users' -H 'Content-Type: application/json' --data '{\"name\":\"John\"}'"
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## API Reference
|
|
172
|
+
|
|
173
|
+
### Factory Functions
|
|
174
|
+
|
|
175
|
+
| Function | Description |
|
|
176
|
+
|----------|-------------|
|
|
177
|
+
| `createApiLogger(testName, context?)` | Create logger (context default: `'test'`) |
|
|
178
|
+
| `createSetupLogger(testName)` | Create logger with `'setup'` context |
|
|
179
|
+
| `createTeardownLogger(testName)` | Create logger with `'teardown'` context |
|
|
180
|
+
|
|
181
|
+
### `ApiLogger`
|
|
182
|
+
|
|
183
|
+
| Method | Description |
|
|
184
|
+
|--------|-------------|
|
|
185
|
+
| `logApiCall(method, url, reqHeaders, reqBody, status, resHeaders, resBody, duration)` | Log complete request + response |
|
|
186
|
+
| `logRequest(method, url, headers?, body?)` | Log request (pair with `logResponse`) |
|
|
187
|
+
| `logResponse(status, headers?, body?)` | Log response (pair with `logRequest`) |
|
|
188
|
+
| `isEnabled()` | Check if logging is active |
|
|
189
|
+
| `finalize(result, additionalInfo?)` | Write test result (`PASSED` / `FAILED` / `SKIPPED`) |
|
|
190
|
+
| `getLogFilePath()` | Get current log file path |
|
|
191
|
+
|
|
192
|
+
### `CurlGenerator`
|
|
193
|
+
|
|
194
|
+
| Method | Description |
|
|
195
|
+
|--------|-------------|
|
|
196
|
+
| `CurlGenerator.generate(requestData, maskAuth?)` | Generate curl command string |
|
|
197
|
+
|
|
198
|
+
## Configuration
|
|
199
|
+
|
|
200
|
+
| Env Variable | Default | Description |
|
|
201
|
+
|-------------|---------|-------------|
|
|
202
|
+
| `API_LOGS` | `false` | Set to `'true'` to enable logging |
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// LoggerConfig
|
|
206
|
+
{
|
|
207
|
+
testName?: string; // Test name (default: 'unknown-test')
|
|
208
|
+
context?: LogContext; // 'setup' | 'test' | 'teardown'
|
|
209
|
+
logDirectory?: string; // Custom log dir (default: 'logs/')
|
|
210
|
+
maskAuthTokens?: boolean; // Mask auth headers (default: true)
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Logger for capturing and logging HTTP requests/responses
|
|
3
|
+
* Features:
|
|
4
|
+
* - Comprehensive request/response logging
|
|
5
|
+
* - Curl command generation for manual testing
|
|
6
|
+
* - Environment-based enable/disable via API_LOGS
|
|
7
|
+
* - Automatic file logging with JSON format
|
|
8
|
+
* - Test context tracking (setup/test/teardown)
|
|
9
|
+
*/
|
|
10
|
+
import { LogContext, LoggerConfig } from './types';
|
|
11
|
+
export declare class ApiLogger {
|
|
12
|
+
private enabled;
|
|
13
|
+
private testName;
|
|
14
|
+
private context;
|
|
15
|
+
private logDirectory;
|
|
16
|
+
private logFilePath;
|
|
17
|
+
private maskAuthTokens;
|
|
18
|
+
private currentRequest;
|
|
19
|
+
private requestStartTime;
|
|
20
|
+
constructor(config?: LoggerConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Get default log directory path
|
|
23
|
+
*/
|
|
24
|
+
private getDefaultLogDirectory;
|
|
25
|
+
/**
|
|
26
|
+
* Initialize log file and directory
|
|
27
|
+
*/
|
|
28
|
+
private initializeLogFile;
|
|
29
|
+
/**
|
|
30
|
+
* Log an API call with request and response
|
|
31
|
+
*/
|
|
32
|
+
logApiCall(method: string, url: string, requestHeaders: Record<string, string | string[]> | undefined, requestBody: any, status: number, responseHeaders: Record<string, string> | undefined, responseBody: any, duration: number): void;
|
|
33
|
+
/**
|
|
34
|
+
* Log just the request part (when response isn't available yet)
|
|
35
|
+
*/
|
|
36
|
+
logRequest(method: string, url: string, headers?: Record<string, string | string[]>, body?: any): void;
|
|
37
|
+
/**
|
|
38
|
+
* Log just the response part (pairs with logRequest)
|
|
39
|
+
*/
|
|
40
|
+
logResponse(status: number, headers?: Record<string, string>, body?: any): void;
|
|
41
|
+
/**
|
|
42
|
+
* Write log entry to file
|
|
43
|
+
*/
|
|
44
|
+
private writeLogEntry;
|
|
45
|
+
/**
|
|
46
|
+
* Generate ISO timestamp for filenames
|
|
47
|
+
*/
|
|
48
|
+
private generateTimestamp;
|
|
49
|
+
/**
|
|
50
|
+
* Sanitize test name for use in filename
|
|
51
|
+
*/
|
|
52
|
+
private sanitizeTestName;
|
|
53
|
+
/**
|
|
54
|
+
* Get the current log file path
|
|
55
|
+
*/
|
|
56
|
+
getLogFilePath(): string | null;
|
|
57
|
+
/**
|
|
58
|
+
* Check if logger is enabled
|
|
59
|
+
*/
|
|
60
|
+
isEnabled(): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Finalize logging for test completion
|
|
63
|
+
*/
|
|
64
|
+
finalize(result: 'PASSED' | 'FAILED' | 'SKIPPED', additionalInfo?: Record<string, any>): void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Factory function for creating test-context loggers
|
|
68
|
+
*/
|
|
69
|
+
export declare function createApiLogger(testName: string, context?: LogContext): ApiLogger;
|
|
70
|
+
/**
|
|
71
|
+
* Factory for setup context
|
|
72
|
+
*/
|
|
73
|
+
export declare function createSetupLogger(testName: string): ApiLogger;
|
|
74
|
+
/**
|
|
75
|
+
* Factory for teardown context
|
|
76
|
+
*/
|
|
77
|
+
export declare function createTeardownLogger(testName: string): ApiLogger;
|
|
78
|
+
//# sourceMappingURL=ApiLogger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiLogger.d.ts","sourceRoot":"","sources":["../src/ApiLogger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EACL,UAAU,EACV,YAAY,EAIb,MAAM,SAAS,CAAC;AAEjB,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,gBAAgB,CAAuB;gBAEnC,MAAM,GAAE,YAAiB;IAcrC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAI9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoBzB;;OAEG;IACH,UAAU,CACR,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,SAAS,EAC7D,WAAW,EAAE,GAAG,EAChB,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACnD,YAAY,EAAE,GAAG,EACjB,QAAQ,EAAE,MAAM,GACf,IAAI;IAmDP;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAStG;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAsB/E;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IASzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IASxB;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;CAuB9F;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,UAAmB,GAAG,SAAS,CAEzF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAE7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAEhE"}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* API Logger for capturing and logging HTTP requests/responses
|
|
4
|
+
* Features:
|
|
5
|
+
* - Comprehensive request/response logging
|
|
6
|
+
* - Curl command generation for manual testing
|
|
7
|
+
* - Environment-based enable/disable via API_LOGS
|
|
8
|
+
* - Automatic file logging with JSON format
|
|
9
|
+
* - Test context tracking (setup/test/teardown)
|
|
10
|
+
*/
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ApiLogger = void 0;
|
|
16
|
+
exports.createApiLogger = createApiLogger;
|
|
17
|
+
exports.createSetupLogger = createSetupLogger;
|
|
18
|
+
exports.createTeardownLogger = createTeardownLogger;
|
|
19
|
+
const fs_1 = __importDefault(require("fs"));
|
|
20
|
+
const path_1 = __importDefault(require("path"));
|
|
21
|
+
const CurlGenerator_1 = require("./CurlGenerator");
|
|
22
|
+
class ApiLogger {
|
|
23
|
+
constructor(config = {}) {
|
|
24
|
+
this.logFilePath = null;
|
|
25
|
+
this.currentRequest = null;
|
|
26
|
+
this.requestStartTime = null;
|
|
27
|
+
// Check if logging is enabled via environment variable
|
|
28
|
+
this.enabled = process.env.API_LOGS === 'true';
|
|
29
|
+
this.testName = config.testName || 'unknown-test';
|
|
30
|
+
this.context = config.context || 'test';
|
|
31
|
+
this.logDirectory = config.logDirectory || this.getDefaultLogDirectory();
|
|
32
|
+
this.maskAuthTokens = config.maskAuthTokens ?? true;
|
|
33
|
+
if (this.enabled) {
|
|
34
|
+
this.initializeLogFile();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get default log directory path
|
|
39
|
+
*/
|
|
40
|
+
getDefaultLogDirectory() {
|
|
41
|
+
return path_1.default.join(process.cwd(), 'logs');
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Initialize log file and directory
|
|
45
|
+
*/
|
|
46
|
+
initializeLogFile() {
|
|
47
|
+
try {
|
|
48
|
+
if (!fs_1.default.existsSync(this.logDirectory)) {
|
|
49
|
+
fs_1.default.mkdirSync(this.logDirectory, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
const timestamp = this.generateTimestamp();
|
|
52
|
+
const sanitizedTestName = this.sanitizeTestName(this.testName);
|
|
53
|
+
const filename = `${this.context.toUpperCase()}_${sanitizedTestName}_${timestamp}.log`;
|
|
54
|
+
this.logFilePath = path_1.default.join(this.logDirectory, filename);
|
|
55
|
+
if (!fs_1.default.existsSync(this.logFilePath)) {
|
|
56
|
+
fs_1.default.writeFileSync(this.logFilePath, '');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.warn('[ApiLogger] Failed to initialize log file:', error);
|
|
61
|
+
this.logFilePath = null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Log an API call with request and response
|
|
66
|
+
*/
|
|
67
|
+
logApiCall(method, url, requestHeaders, requestBody, status, responseHeaders, responseBody, duration) {
|
|
68
|
+
if (!this.enabled) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
// Extract content type from headers
|
|
73
|
+
let contentType;
|
|
74
|
+
if (requestHeaders) {
|
|
75
|
+
for (const [key, value] of Object.entries(requestHeaders)) {
|
|
76
|
+
if (key.toLowerCase() === 'content-type') {
|
|
77
|
+
contentType = Array.isArray(value) ? value[0] : value;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const requestData = {
|
|
83
|
+
method,
|
|
84
|
+
url,
|
|
85
|
+
headers: requestHeaders,
|
|
86
|
+
body: requestBody,
|
|
87
|
+
contentType,
|
|
88
|
+
};
|
|
89
|
+
const responseData = {
|
|
90
|
+
status,
|
|
91
|
+
headers: responseHeaders,
|
|
92
|
+
body: responseBody,
|
|
93
|
+
};
|
|
94
|
+
// Generate curl command
|
|
95
|
+
const curl = CurlGenerator_1.CurlGenerator.generate(requestData, this.maskAuthTokens);
|
|
96
|
+
// Create log entry
|
|
97
|
+
const logEntry = {
|
|
98
|
+
timestamp: new Date().toISOString(),
|
|
99
|
+
testName: this.testName,
|
|
100
|
+
context: this.context,
|
|
101
|
+
request: requestData,
|
|
102
|
+
response: responseData,
|
|
103
|
+
duration,
|
|
104
|
+
curl,
|
|
105
|
+
};
|
|
106
|
+
this.writeLogEntry(logEntry);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.warn('[ApiLogger] Error logging API call:', error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Log just the request part (when response isn't available yet)
|
|
114
|
+
*/
|
|
115
|
+
logRequest(method, url, headers, body) {
|
|
116
|
+
if (!this.enabled) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
this.currentRequest = { method, url, headers, body };
|
|
120
|
+
this.requestStartTime = Date.now();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Log just the response part (pairs with logRequest)
|
|
124
|
+
*/
|
|
125
|
+
logResponse(status, headers, body) {
|
|
126
|
+
if (!this.enabled || !this.currentRequest) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const duration = this.requestStartTime ? Date.now() - this.requestStartTime : 0;
|
|
130
|
+
this.logApiCall(this.currentRequest.method, this.currentRequest.url, this.currentRequest.headers, this.currentRequest.body, status, headers, body, duration);
|
|
131
|
+
this.currentRequest = null;
|
|
132
|
+
this.requestStartTime = null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Write log entry to file
|
|
136
|
+
*/
|
|
137
|
+
writeLogEntry(entry) {
|
|
138
|
+
try {
|
|
139
|
+
if (!this.logFilePath) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const logLine = JSON.stringify(entry, null, 2);
|
|
143
|
+
fs_1.default.appendFileSync(this.logFilePath, logLine + '\n\n');
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.warn('[ApiLogger] Failed to write log file:', error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Generate ISO timestamp for filenames
|
|
151
|
+
*/
|
|
152
|
+
generateTimestamp() {
|
|
153
|
+
const now = new Date();
|
|
154
|
+
return now
|
|
155
|
+
.toISOString()
|
|
156
|
+
.replace(/[:.]/g, '-')
|
|
157
|
+
.replace('T', 'T')
|
|
158
|
+
.split('.')[0];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Sanitize test name for use in filename
|
|
162
|
+
*/
|
|
163
|
+
sanitizeTestName(name) {
|
|
164
|
+
return name
|
|
165
|
+
.toLowerCase()
|
|
166
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
167
|
+
.replace(/-+/g, '-')
|
|
168
|
+
.replace(/^-|-$/g, '')
|
|
169
|
+
.substring(0, 80);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the current log file path
|
|
173
|
+
*/
|
|
174
|
+
getLogFilePath() {
|
|
175
|
+
return this.logFilePath;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Check if logger is enabled
|
|
179
|
+
*/
|
|
180
|
+
isEnabled() {
|
|
181
|
+
return this.enabled;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Finalize logging for test completion
|
|
185
|
+
*/
|
|
186
|
+
finalize(result, additionalInfo) {
|
|
187
|
+
if (!this.enabled) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const finalizationEntry = {
|
|
192
|
+
timestamp: new Date().toISOString(),
|
|
193
|
+
step: 'TEST_FINALIZATION',
|
|
194
|
+
testName: this.testName,
|
|
195
|
+
context: this.context,
|
|
196
|
+
result,
|
|
197
|
+
logFilePath: this.logFilePath,
|
|
198
|
+
additionalInfo,
|
|
199
|
+
};
|
|
200
|
+
if (this.logFilePath) {
|
|
201
|
+
fs_1.default.appendFileSync(this.logFilePath, JSON.stringify(finalizationEntry, null, 2) + '\n\n');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.warn('[ApiLogger] Error finalizing log:', error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
exports.ApiLogger = ApiLogger;
|
|
210
|
+
/**
|
|
211
|
+
* Factory function for creating test-context loggers
|
|
212
|
+
*/
|
|
213
|
+
function createApiLogger(testName, context = 'test') {
|
|
214
|
+
return new ApiLogger({ testName, context });
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Factory for setup context
|
|
218
|
+
*/
|
|
219
|
+
function createSetupLogger(testName) {
|
|
220
|
+
return new ApiLogger({ testName, context: 'setup' });
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Factory for teardown context
|
|
224
|
+
*/
|
|
225
|
+
function createTeardownLogger(testName) {
|
|
226
|
+
return new ApiLogger({ testName, context: 'teardown' });
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=ApiLogger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiLogger.js","sourceRoot":"","sources":["../src/ApiLogger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curl command generator from HTTP request data
|
|
3
|
+
* Converts captured request details into ready-to-use curl commands
|
|
4
|
+
*/
|
|
5
|
+
export interface RequestData {
|
|
6
|
+
method: string;
|
|
7
|
+
url: string;
|
|
8
|
+
headers?: Record<string, string | string[]>;
|
|
9
|
+
body?: any;
|
|
10
|
+
params?: Record<string, string | number | boolean>;
|
|
11
|
+
contentType?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class CurlGenerator {
|
|
14
|
+
/**
|
|
15
|
+
* Generate a curl command from request data
|
|
16
|
+
* @param requestData - The request information to convert
|
|
17
|
+
* @param maskAuth - Whether to mask Authorization headers (default: true)
|
|
18
|
+
* @returns A properly formatted curl command string
|
|
19
|
+
*/
|
|
20
|
+
static generate(requestData: RequestData, maskAuth?: boolean): string;
|
|
21
|
+
/**
|
|
22
|
+
* Get content type from headers or contentType parameter
|
|
23
|
+
*/
|
|
24
|
+
private static getContentType;
|
|
25
|
+
/**
|
|
26
|
+
* Convert body object to URL-encoded form string
|
|
27
|
+
*/
|
|
28
|
+
private static bodyToFormString;
|
|
29
|
+
/**
|
|
30
|
+
* Generate multipart form fields for curl -F flag
|
|
31
|
+
*/
|
|
32
|
+
private static generateMultipartFields;
|
|
33
|
+
/**
|
|
34
|
+
* Escape URL special characters (except for safe URL characters)
|
|
35
|
+
*/
|
|
36
|
+
private static escapeUrl;
|
|
37
|
+
/**
|
|
38
|
+
* Escape string content in curl --data parameter
|
|
39
|
+
*/
|
|
40
|
+
private static escapeBodyString;
|
|
41
|
+
/**
|
|
42
|
+
* Mask sensitive tokens/credentials (keep first 20 and last 10 chars)
|
|
43
|
+
*/
|
|
44
|
+
private static maskToken;
|
|
45
|
+
/**
|
|
46
|
+
* Normalize headers from various formats to array of tuples
|
|
47
|
+
*/
|
|
48
|
+
private static normalizeHeaders;
|
|
49
|
+
/**
|
|
50
|
+
* Determine if a header should be included in curl
|
|
51
|
+
*/
|
|
52
|
+
private static shouldSkipHeader;
|
|
53
|
+
/**
|
|
54
|
+
* Determine if HTTP method typically includes a request body
|
|
55
|
+
*/
|
|
56
|
+
private static shouldIncludeBody;
|
|
57
|
+
/**
|
|
58
|
+
* Format curl command for display (no-op, kept for API compatibility)
|
|
59
|
+
*/
|
|
60
|
+
static formatForDisplay(curl: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* Format curl command as single line (for easy copying)
|
|
63
|
+
*/
|
|
64
|
+
static formatAsOneLine(curl: string): string;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=CurlGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CurlGenerator.d.ts","sourceRoot":"","sources":["../src/CurlGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,aAAa;IACxB;;;;;OAKG;IACH,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,GAAE,OAAc,GAAG,MAAM;IAyC3E;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAc7B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAe/B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,uBAAuB;IAuBtC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,SAAS;IAIxB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAI/B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,SAAS;IASxB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAiB/B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAY/B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAKhC;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI7C;;OAEG;IACH,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAG7C"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Curl command generator from HTTP request data
|
|
4
|
+
* Converts captured request details into ready-to-use curl commands
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CurlGenerator = void 0;
|
|
8
|
+
class CurlGenerator {
|
|
9
|
+
/**
|
|
10
|
+
* Generate a curl command from request data
|
|
11
|
+
* @param requestData - The request information to convert
|
|
12
|
+
* @param maskAuth - Whether to mask Authorization headers (default: true)
|
|
13
|
+
* @returns A properly formatted curl command string
|
|
14
|
+
*/
|
|
15
|
+
static generate(requestData, maskAuth = true) {
|
|
16
|
+
const { method, url, headers = {}, body, contentType } = requestData;
|
|
17
|
+
let curl = `curl -X ${method.toUpperCase()} '${this.escapeUrl(url)}'`;
|
|
18
|
+
// Detect content type
|
|
19
|
+
const contentTypeHeader = this.getContentType(headers, contentType);
|
|
20
|
+
// Add headers (except Content-Type for multipart, curl handles it)
|
|
21
|
+
const headerEntries = this.normalizeHeaders(headers);
|
|
22
|
+
for (const [key, value] of headerEntries) {
|
|
23
|
+
// Skip Content-Type for multipart, curl generates it automatically with -F
|
|
24
|
+
if (contentTypeHeader.includes('multipart') && key.toLowerCase() === 'content-type') {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const headerValue = maskAuth && key.toLowerCase() === 'authorization'
|
|
28
|
+
? this.maskToken(value)
|
|
29
|
+
: value;
|
|
30
|
+
curl += ` -H '${key}: ${headerValue}'`;
|
|
31
|
+
}
|
|
32
|
+
// Add body for methods that typically have bodies
|
|
33
|
+
if (body && this.shouldIncludeBody(method)) {
|
|
34
|
+
if (contentTypeHeader.includes('multipart')) {
|
|
35
|
+
// Multipart form data - use -F flag
|
|
36
|
+
curl += this.generateMultipartFields(body);
|
|
37
|
+
}
|
|
38
|
+
else if (contentTypeHeader.includes('form-urlencoded')) {
|
|
39
|
+
// URL-encoded form data - use --data
|
|
40
|
+
const formString = this.bodyToFormString(body);
|
|
41
|
+
curl += ` --data '${this.escapeBodyString(formString)}'`;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// JSON or other data - use --data with JSON
|
|
45
|
+
const bodyString = typeof body === 'string' ? body : JSON.stringify(body);
|
|
46
|
+
curl += ` --data '${this.escapeBodyString(bodyString)}'`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return curl;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get content type from headers or contentType parameter
|
|
53
|
+
*/
|
|
54
|
+
static getContentType(headers, contentType) {
|
|
55
|
+
if (contentType) {
|
|
56
|
+
return contentType;
|
|
57
|
+
}
|
|
58
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
59
|
+
if (key.toLowerCase() === 'content-type') {
|
|
60
|
+
return Array.isArray(value) ? value[0] : value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return 'application/json';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convert body object to URL-encoded form string
|
|
67
|
+
*/
|
|
68
|
+
static bodyToFormString(body) {
|
|
69
|
+
if (typeof body === 'string') {
|
|
70
|
+
return body;
|
|
71
|
+
}
|
|
72
|
+
const params = new URLSearchParams();
|
|
73
|
+
if (typeof body === 'object' && body !== null) {
|
|
74
|
+
for (const [key, value] of Object.entries(body)) {
|
|
75
|
+
params.append(key, String(value));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return params.toString();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Generate multipart form fields for curl -F flag
|
|
82
|
+
*/
|
|
83
|
+
static generateMultipartFields(body) {
|
|
84
|
+
if (typeof body === 'string') {
|
|
85
|
+
return ` --data '${this.escapeBodyString(body)}'`;
|
|
86
|
+
}
|
|
87
|
+
let result = '';
|
|
88
|
+
if (typeof body === 'object' && body !== null) {
|
|
89
|
+
for (const [key, value] of Object.entries(body)) {
|
|
90
|
+
if (value && typeof value === 'object' && 'path' in value) {
|
|
91
|
+
// File field - use -F 'key=@filepath'
|
|
92
|
+
const filePath = value.path;
|
|
93
|
+
result += ` -F '${key}=@${filePath}'`;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Regular field - use -F 'key=value'
|
|
97
|
+
const fieldValue = String(value).replace(/'/g, "'\\''");
|
|
98
|
+
result += ` -F '${key}=${fieldValue}'`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return result || ` --data '${this.escapeBodyString(JSON.stringify(body))}'`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Escape URL special characters (except for safe URL characters)
|
|
106
|
+
*/
|
|
107
|
+
static escapeUrl(url) {
|
|
108
|
+
return url.replace(/"/g, '\\"');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Escape string content in curl --data parameter
|
|
112
|
+
*/
|
|
113
|
+
static escapeBodyString(str) {
|
|
114
|
+
return str.replace(/'/g, "'\\''");
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Mask sensitive tokens/credentials (keep first 20 and last 10 chars)
|
|
118
|
+
*/
|
|
119
|
+
static maskToken(token) {
|
|
120
|
+
if (token.length <= 30) {
|
|
121
|
+
return '***masked***';
|
|
122
|
+
}
|
|
123
|
+
const start = token.substring(0, 20);
|
|
124
|
+
const end = token.substring(token.length - 10);
|
|
125
|
+
return `${start}...${end}`;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Normalize headers from various formats to array of tuples
|
|
129
|
+
*/
|
|
130
|
+
static normalizeHeaders(headers) {
|
|
131
|
+
const normalized = [];
|
|
132
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
133
|
+
if (!value || this.shouldSkipHeader(key)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const headerValue = Array.isArray(value) ? value.join(', ') : String(value);
|
|
137
|
+
if (headerValue) {
|
|
138
|
+
normalized.push([key, headerValue]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return normalized.sort((a, b) => a[0].localeCompare(b[0]));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Determine if a header should be included in curl
|
|
145
|
+
*/
|
|
146
|
+
static shouldSkipHeader(headerName) {
|
|
147
|
+
const skipHeaders = [
|
|
148
|
+
'connection',
|
|
149
|
+
'content-length',
|
|
150
|
+
'host',
|
|
151
|
+
'origin',
|
|
152
|
+
'referer',
|
|
153
|
+
'user-agent',
|
|
154
|
+
];
|
|
155
|
+
return skipHeaders.includes(headerName.toLowerCase());
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Determine if HTTP method typically includes a request body
|
|
159
|
+
*/
|
|
160
|
+
static shouldIncludeBody(method) {
|
|
161
|
+
const methodsWithBody = ['POST', 'PUT', 'PATCH'];
|
|
162
|
+
return methodsWithBody.includes(method.toUpperCase());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Format curl command for display (no-op, kept for API compatibility)
|
|
166
|
+
*/
|
|
167
|
+
static formatForDisplay(curl) {
|
|
168
|
+
return curl;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Format curl command as single line (for easy copying)
|
|
172
|
+
*/
|
|
173
|
+
static formatAsOneLine(curl) {
|
|
174
|
+
return curl.replace(/\s+\\\n\s+/g, ' ');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.CurlGenerator = CurlGenerator;
|
|
178
|
+
//# sourceMappingURL=CurlGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CurlGenerator.js","sourceRoot":"","sources":["../src/CurlGenerator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ApiLogger, createApiLogger, createSetupLogger, createTeardownLogger } from './ApiLogger';
|
|
2
|
+
export { CurlGenerator } from './CurlGenerator';
|
|
3
|
+
export type { LoggerConfig, RequestLogData, ResponseLogData, LogEntry, LogContext } from './types';
|
|
4
|
+
export type { RequestData } from './CurlGenerator';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CurlGenerator = exports.createTeardownLogger = exports.createSetupLogger = exports.createApiLogger = exports.ApiLogger = void 0;
|
|
4
|
+
var ApiLogger_1 = require("./ApiLogger");
|
|
5
|
+
Object.defineProperty(exports, "ApiLogger", { enumerable: true, get: function () { return ApiLogger_1.ApiLogger; } });
|
|
6
|
+
Object.defineProperty(exports, "createApiLogger", { enumerable: true, get: function () { return ApiLogger_1.createApiLogger; } });
|
|
7
|
+
Object.defineProperty(exports, "createSetupLogger", { enumerable: true, get: function () { return ApiLogger_1.createSetupLogger; } });
|
|
8
|
+
Object.defineProperty(exports, "createTeardownLogger", { enumerable: true, get: function () { return ApiLogger_1.createTeardownLogger; } });
|
|
9
|
+
var CurlGenerator_1 = require("./CurlGenerator");
|
|
10
|
+
Object.defineProperty(exports, "CurlGenerator", { enumerable: true, get: function () { return CurlGenerator_1.CurlGenerator; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type LogContext = 'setup' | 'test' | 'teardown';
|
|
2
|
+
export interface LoggerConfig {
|
|
3
|
+
testName?: string;
|
|
4
|
+
context?: LogContext;
|
|
5
|
+
logDirectory?: string;
|
|
6
|
+
maskAuthTokens?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface RequestLogData {
|
|
9
|
+
method: string;
|
|
10
|
+
url: string;
|
|
11
|
+
headers?: Record<string, string | string[]>;
|
|
12
|
+
body?: any;
|
|
13
|
+
contentType?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ResponseLogData {
|
|
16
|
+
status: number;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
body?: any;
|
|
19
|
+
}
|
|
20
|
+
export interface LogEntry {
|
|
21
|
+
timestamp: string;
|
|
22
|
+
testName: string;
|
|
23
|
+
context: LogContext;
|
|
24
|
+
request: RequestLogData;
|
|
25
|
+
response: ResponseLogData;
|
|
26
|
+
duration: number;
|
|
27
|
+
curl: string;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;AAEvD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "playwright-api-logger",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Comprehensive API request/response logger with curl export for Playwright tests",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"playwright",
|
|
16
|
+
"api",
|
|
17
|
+
"logger",
|
|
18
|
+
"curl",
|
|
19
|
+
"testing",
|
|
20
|
+
"postman",
|
|
21
|
+
"form-data",
|
|
22
|
+
"multipart"
|
|
23
|
+
],
|
|
24
|
+
"author": "AZANIR",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/AZANIR/playwright-api-logger.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/AZANIR/playwright-api-logger/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/AZANIR/playwright-api-logger#readme",
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@playwright/test": ">=1.40.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@playwright/test": "^1.58.0",
|
|
39
|
+
"@types/node": "^25.5.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|