playwright-api-logger 1.0.0 → 2.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/README.md CHANGED
@@ -21,45 +21,45 @@
21
21
  ## How It Works
22
22
 
23
23
  ```mermaid
24
- flowchart TD
24
+ flowchart LR
25
25
  subgraph T[Playwright Test]
26
26
  F[Fixture setup]
27
- C[API Call (GET / POST)]
27
+ C[API Call GET/POST/PUT/DELETE]
28
28
  end
29
29
 
30
- subgraph L[Logging & API layer]
31
- AL[ApiLogger (per test)]
32
- B[BaseApi Controller]
33
- CG[Curl Generator]
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 FS[File system]
37
- LOG[logs/TEST_*.log { request, response, curl, duration }]
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
- %% test flow
42
- F --> AL
43
- C --> B
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
- `API_LOGS=true` → **Logging ON** (files created in `logs/`)
55
- `API_LOGS=false` **Logging OFF** (zero overhead, default)
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
 
59
- - **Full Logging** — method, URL, headers, request/response body, status, timing
58
+ - **One-line integration** — just wrap `request` with `withApiLogging()`, zero changes to controllers/clients
59
+ - **Structured logs** — one JSON document per test with `preconditions`, `steps`, and `teardown` sections
60
+ - **Step descriptions** — describe what each API call does with `.describe()`
60
61
  - **Curl Export** — copy from log, paste into terminal or import into Postman
61
62
  - **Env Control** — `API_LOGS=true/false` (default: `false`, zero overhead when off)
62
- - **Context Tracking** — setup / test / teardown phases
63
63
  - **Token Masking** — Authorization headers are automatically masked
64
64
  - **Form Data** — JSON, URL-encoded, and multipart/form-data support
65
65
  - **Error Resilient** — logging never breaks your tests
@@ -72,58 +72,46 @@ npm install playwright-api-logger
72
72
 
73
73
  ## Quick Start
74
74
 
75
- ### Step 1. Add logger to your fixture
75
+ ### One line in your fixture that's it!
76
76
 
77
77
  ```typescript
78
- import { createApiLogger } from 'playwright-api-logger';
78
+ import { withApiLogging } from 'playwright-api-logger';
79
79
 
80
80
  export const test = base.extend({
81
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
- }
82
+ const loggedRequest = withApiLogging(request, testInfo);
83
+ const apiClient = new ApiClient(loggedRequest);
84
+ await use(apiClient);
85
+ loggedRequest.__logger.finalize(
86
+ testInfo.status === 'passed' ? 'PASSED' : 'FAILED'
87
+ );
92
88
  },
93
89
  });
94
90
  ```
95
91
 
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
- }
92
+ No changes to your controllers, clients, or test files.
107
93
 
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
- }
94
+ ### With preconditions and step descriptions
120
95
 
121
- return response;
122
- }
123
- }
96
+ ```typescript
97
+ test('GET Without token (401)', async ({ apiClient, request }) => {
98
+ const loggedRequest = (request as any).__logger as ApiLogger;
99
+
100
+ // Mark following calls as preconditions
101
+ loggedRequest.startPreconditions();
102
+ loggedRequest.describe('Get employee ID for test');
103
+ const employees = await apiClient.getEmployees({ page: 1, size: 1 });
104
+ const employeeId = employees.items[0].id;
105
+
106
+ // Switch to test steps
107
+ loggedRequest.startTest();
108
+ loggedRequest.describe('Get children without auth token');
109
+ const response = await apiClient.getChildrenWithoutAuth(employeeId);
110
+ expect(response.status).toBe(401);
111
+ });
124
112
  ```
125
113
 
126
- ### Step 3. Enable via environment variable
114
+ ### Enable via environment variable
127
115
 
128
116
  ```bash
129
117
  # .env
@@ -137,56 +125,93 @@ API_LOGS=true npx playwright test
137
125
 
138
126
  ## Log Output
139
127
 
140
- Logs are saved to `logs/` directory:
128
+ One structured JSON document per test:
141
129
 
142
130
  ```
143
131
  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
132
+ get-without-token-401_2026-03-16T18-33-03.log
133
+ create-employee_2026-03-16T18-35-10.log
147
134
  ```
148
135
 
149
- Each log entry is a JSON object:
136
+ ### Example log:
150
137
 
151
138
  ```json
152
139
  {
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" }
140
+ "test": {
141
+ "name": "GET Without token (401)",
142
+ "file": "tests/api/employees/children.spec.ts",
143
+ "startedAt": "2026-03-16T18:33:03.654Z",
144
+ "finishedAt": "2026-03-16T18:33:04.300Z",
145
+ "duration": 646,
146
+ "result": "PASSED"
165
147
  },
166
- "duration": 150,
167
- "curl": "curl -X POST 'https://api.example.com/users' -H 'Content-Type: application/json' --data '{\"name\":\"John\"}'"
148
+ "preconditions": [
149
+ {
150
+ "step": 1,
151
+ "description": "Get employee ID for test",
152
+ "timestamp": "2026-03-16T18:33:04.174Z",
153
+ "request": {
154
+ "method": "GET",
155
+ "url": "https://api.example.com/employees?page=1&size=1"
156
+ },
157
+ "response": {
158
+ "status": 200,
159
+ "body": { "items": [{ "id": "abc-123" }], "total": 27 }
160
+ },
161
+ "duration": 501,
162
+ "curl": "curl -X GET 'https://api.example.com/employees?page=1&size=1' -H 'Accept: application/json'"
163
+ }
164
+ ],
165
+ "steps": [
166
+ {
167
+ "step": 1,
168
+ "description": "Get children without auth token",
169
+ "timestamp": "2026-03-16T18:33:04.242Z",
170
+ "request": {
171
+ "method": "GET",
172
+ "url": "https://api.example.com/employees/abc-123/children"
173
+ },
174
+ "response": {
175
+ "status": 401,
176
+ "body": { "detail": "Not authenticated" }
177
+ },
178
+ "duration": 67,
179
+ "curl": "curl -X GET 'https://api.example.com/employees/abc-123/children'"
180
+ }
181
+ ],
182
+ "teardown": [],
183
+ "summary": {
184
+ "totalRequests": 2,
185
+ "preconditions": 1,
186
+ "testSteps": 1,
187
+ "teardown": 0,
188
+ "totalDuration": 568
189
+ }
168
190
  }
169
191
  ```
170
192
 
171
193
  ## API Reference
172
194
 
173
- ### Factory Functions
195
+ ### `withApiLogging(request, testInfoOrOptions?)` ⭐
174
196
 
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 |
197
+ Main integration point. Wraps `APIRequestContext` with a Proxy that logs all HTTP calls.
180
198
 
181
- ### `ApiLogger`
199
+ ```typescript
200
+ const loggedRequest = withApiLogging(request, testInfo);
201
+ loggedRequest.__logger // access the ApiLogger instance
202
+ ```
203
+
204
+ ### `ApiLogger` — context & description
182
205
 
183
206
  | Method | Description |
184
207
  |--------|-------------|
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`) |
208
+ | `describe(text)` | Set description for the **next** API call |
209
+ | `startPreconditions()` | Following calls `preconditions` section |
210
+ | `startTest()` | Following calls `steps` section |
211
+ | `startTeardown()` | Following calls → `teardown` section |
212
+ | `setContext(ctx)` | Set context directly (`'preconditions'` / `'test'` / `'teardown'`) |
213
+ | `finalize(result, info?)` | Write structured JSON document to file |
188
214
  | `isEnabled()` | Check if logging is active |
189
- | `finalize(result, additionalInfo?)` | Write test result (`PASSED` / `FAILED` / `SKIPPED`) |
190
215
  | `getLogFilePath()` | Get current log file path |
191
216
 
192
217
  ### `CurlGenerator`
@@ -201,16 +226,31 @@ Each log entry is a JSON object:
201
226
  |-------------|---------|-------------|
202
227
  | `API_LOGS` | `false` | Set to `'true'` to enable logging |
203
228
 
229
+ ### `ApiLoggingOptions`
230
+
204
231
  ```typescript
205
- // LoggerConfig
206
232
  {
207
233
  testName?: string; // Test name (default: 'unknown-test')
208
- context?: LogContext; // 'setup' | 'test' | 'teardown'
234
+ testFile?: string; // Test file path
235
+ context?: LogContext; // 'preconditions' | 'test' | 'teardown'
209
236
  logDirectory?: string; // Custom log dir (default: 'logs/')
210
237
  maskAuthTokens?: boolean; // Mask auth headers (default: true)
238
+ logger?: ApiLogger; // Share logger across phases
211
239
  }
212
240
  ```
213
241
 
242
+ ## Migration from v1 → v2
243
+
244
+ ```diff
245
+ - // v1: manual logger setup in controllers and clients
246
+ - const logger = createApiLogger(testInfo.title);
247
+ - apiClient.setApiLogger(logger);
248
+
249
+ + // v2: one line, structured logs with sections
250
+ + const loggedRequest = withApiLogging(request, testInfo);
251
+ + const apiClient = new ApiClient(loggedRequest);
252
+ ```
253
+
214
254
  ## License
215
255
 
216
256
  MIT
@@ -1,74 +1,83 @@
1
1
  /**
2
2
  * API Logger for capturing and logging HTTP requests/responses
3
+ * Produces a single structured test document with preconditions, steps, and teardown sections.
4
+ *
3
5
  * Features:
4
- * - Comprehensive request/response logging
6
+ * - Structured test document (one JSON per test)
7
+ * - Preconditions / Steps / Teardown sections
8
+ * - Step descriptions and numbering
5
9
  * - Curl command generation for manual testing
6
10
  * - Environment-based enable/disable via API_LOGS
7
- * - Automatic file logging with JSON format
8
- * - Test context tracking (setup/test/teardown)
11
+ * - Test context tracking and switching
9
12
  */
10
13
  import { LogContext, LoggerConfig } from './types';
11
14
  export declare class ApiLogger {
12
15
  private enabled;
13
16
  private testName;
14
- private context;
17
+ private testFile?;
18
+ private currentContext;
15
19
  private logDirectory;
16
20
  private logFilePath;
17
21
  private maskAuthTokens;
18
- private currentRequest;
19
- private requestStartTime;
22
+ private startedAt;
23
+ private preconditions;
24
+ private steps;
25
+ private teardownSteps;
26
+ private nextDescription?;
20
27
  constructor(config?: LoggerConfig);
21
- /**
22
- * Get default log directory path
23
- */
24
28
  private getDefaultLogDirectory;
25
- /**
26
- * Initialize log file and directory
27
- */
28
29
  private initializeLogFile;
29
30
  /**
30
- * Log an API call with request and response
31
+ * Set description for the next API call
32
+ * @example
33
+ * logger.describe('Get employee ID for testing');
34
+ * await apiClient.getEmployees(); // this call gets the description
31
35
  */
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;
36
+ describe(description: string): void;
33
37
  /**
34
- * Log just the request part (when response isn't available yet)
38
+ * Switch current context (preconditions test teardown)
39
+ * All subsequent API calls will be logged to the new context section
35
40
  */
36
- logRequest(method: string, url: string, headers?: Record<string, string | string[]>, body?: any): void;
41
+ setContext(context: LogContext): void;
37
42
  /**
38
- * Log just the response part (pairs with logRequest)
43
+ * Shortcut: switch to preconditions context
39
44
  */
40
- logResponse(status: number, headers?: Record<string, string>, body?: any): void;
45
+ startPreconditions(): void;
41
46
  /**
42
- * Write log entry to file
47
+ * Shortcut: switch to test steps context
43
48
  */
44
- private writeLogEntry;
49
+ startTest(): void;
45
50
  /**
46
- * Generate ISO timestamp for filenames
51
+ * Shortcut: switch to teardown context
47
52
  */
48
- private generateTimestamp;
53
+ startTeardown(): void;
49
54
  /**
50
- * Sanitize test name for use in filename
55
+ * Log an API call with request and response
51
56
  */
52
- private sanitizeTestName;
57
+ 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;
53
58
  /**
54
- * Get the current log file path
59
+ * Get the target array for current context
55
60
  */
56
- getLogFilePath(): string | null;
61
+ private getTargetArray;
57
62
  /**
58
- * Check if logger is enabled
63
+ * Finalize and write the structured test document to file
59
64
  */
60
- isEnabled(): boolean;
65
+ finalize(result: 'PASSED' | 'FAILED' | 'SKIPPED', additionalInfo?: Record<string, any>): void;
61
66
  /**
62
- * Finalize logging for test completion
67
+ * Write structured document to file
63
68
  */
64
- finalize(result: 'PASSED' | 'FAILED' | 'SKIPPED', additionalInfo?: Record<string, any>): void;
69
+ private writeDocument;
70
+ private generateTimestamp;
71
+ private sanitizeTestName;
72
+ getLogFilePath(): string | null;
73
+ isEnabled(): boolean;
65
74
  }
66
75
  /**
67
76
  * Factory function for creating test-context loggers
68
77
  */
69
78
  export declare function createApiLogger(testName: string, context?: LogContext): ApiLogger;
70
79
  /**
71
- * Factory for setup context
80
+ * Factory for setup/preconditions context
72
81
  */
73
82
  export declare function createSetupLogger(testName: string): ApiLogger;
74
83
  /**
@@ -1 +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"}
1
+ {"version":3,"file":"ApiLogger.d.ts","sourceRoot":"","sources":["../src/ApiLogger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EACL,UAAU,EACV,YAAY,EAKb,MAAM,SAAS,CAAC;AAEjB,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,aAAa,CAAsB;IAG3C,OAAO,CAAC,eAAe,CAAC,CAAS;gBAErB,MAAM,GAAE,YAAiB;IAerC,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,iBAAiB;IAgBzB;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAInC;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI;IAIrC;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAI1B;;OAEG;IACH,SAAS,IAAI,IAAI;IAIjB;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;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;IAuDP;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAyC7F;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,gBAAgB;IASxB,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,OAAO;CAGrB;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"}
package/dist/ApiLogger.js CHANGED
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  /**
3
3
  * API Logger for capturing and logging HTTP requests/responses
4
+ * Produces a single structured test document with preconditions, steps, and teardown sections.
5
+ *
4
6
  * Features:
5
- * - Comprehensive request/response logging
7
+ * - Structured test document (one JSON per test)
8
+ * - Preconditions / Steps / Teardown sections
9
+ * - Step descriptions and numbering
6
10
  * - Curl command generation for manual testing
7
11
  * - Environment-based enable/disable via API_LOGS
8
- * - Automatic file logging with JSON format
9
- * - Test context tracking (setup/test/teardown)
12
+ * - Test context tracking and switching
10
13
  */
11
14
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
15
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -22,27 +25,24 @@ const CurlGenerator_1 = require("./CurlGenerator");
22
25
  class ApiLogger {
23
26
  constructor(config = {}) {
24
27
  this.logFilePath = null;
25
- this.currentRequest = null;
26
- this.requestStartTime = null;
27
- // Check if logging is enabled via environment variable
28
+ // Structured storage
29
+ this.preconditions = [];
30
+ this.steps = [];
31
+ this.teardownSteps = [];
28
32
  this.enabled = process.env.API_LOGS === 'true';
29
33
  this.testName = config.testName || 'unknown-test';
30
- this.context = config.context || 'test';
34
+ this.testFile = config.testFile;
35
+ this.currentContext = config.context || 'test';
31
36
  this.logDirectory = config.logDirectory || this.getDefaultLogDirectory();
32
37
  this.maskAuthTokens = config.maskAuthTokens ?? true;
38
+ this.startedAt = new Date().toISOString();
33
39
  if (this.enabled) {
34
40
  this.initializeLogFile();
35
41
  }
36
42
  }
37
- /**
38
- * Get default log directory path
39
- */
40
43
  getDefaultLogDirectory() {
41
44
  return path_1.default.join(process.cwd(), 'logs');
42
45
  }
43
- /**
44
- * Initialize log file and directory
45
- */
46
46
  initializeLogFile() {
47
47
  try {
48
48
  if (!fs_1.default.existsSync(this.logDirectory)) {
@@ -50,17 +50,48 @@ class ApiLogger {
50
50
  }
51
51
  const timestamp = this.generateTimestamp();
52
52
  const sanitizedTestName = this.sanitizeTestName(this.testName);
53
- const filename = `${this.context.toUpperCase()}_${sanitizedTestName}_${timestamp}.log`;
53
+ const filename = `${sanitizedTestName}_${timestamp}.log`;
54
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
55
  }
59
56
  catch (error) {
60
57
  console.warn('[ApiLogger] Failed to initialize log file:', error);
61
58
  this.logFilePath = null;
62
59
  }
63
60
  }
61
+ /**
62
+ * Set description for the next API call
63
+ * @example
64
+ * logger.describe('Get employee ID for testing');
65
+ * await apiClient.getEmployees(); // this call gets the description
66
+ */
67
+ describe(description) {
68
+ this.nextDescription = description;
69
+ }
70
+ /**
71
+ * Switch current context (preconditions → test → teardown)
72
+ * All subsequent API calls will be logged to the new context section
73
+ */
74
+ setContext(context) {
75
+ this.currentContext = context;
76
+ }
77
+ /**
78
+ * Shortcut: switch to preconditions context
79
+ */
80
+ startPreconditions() {
81
+ this.currentContext = 'preconditions';
82
+ }
83
+ /**
84
+ * Shortcut: switch to test steps context
85
+ */
86
+ startTest() {
87
+ this.currentContext = 'test';
88
+ }
89
+ /**
90
+ * Shortcut: switch to teardown context
91
+ */
92
+ startTeardown() {
93
+ this.currentContext = 'teardown';
94
+ }
64
95
  /**
65
96
  * Log an API call with request and response
66
97
  */
@@ -69,7 +100,6 @@ class ApiLogger {
69
100
  return;
70
101
  }
71
102
  try {
72
- // Extract content type from headers
73
103
  let contentType;
74
104
  if (requestHeaders) {
75
105
  for (const [key, value] of Object.entries(requestHeaders)) {
@@ -91,75 +121,101 @@ class ApiLogger {
91
121
  headers: responseHeaders,
92
122
  body: responseBody,
93
123
  };
94
- // Generate curl command
95
124
  const curl = CurlGenerator_1.CurlGenerator.generate(requestData, this.maskAuthTokens);
96
- // Create log entry
97
- const logEntry = {
125
+ // Get target array and step number
126
+ const targetArray = this.getTargetArray();
127
+ const stepNumber = targetArray.length + 1;
128
+ const stepEntry = {
129
+ step: stepNumber,
130
+ description: this.nextDescription,
98
131
  timestamp: new Date().toISOString(),
99
- testName: this.testName,
100
- context: this.context,
101
132
  request: requestData,
102
133
  response: responseData,
103
134
  duration,
104
135
  curl,
105
136
  };
106
- this.writeLogEntry(logEntry);
137
+ // Clear description after use
138
+ this.nextDescription = undefined;
139
+ targetArray.push(stepEntry);
107
140
  }
108
141
  catch (error) {
109
142
  console.warn('[ApiLogger] Error logging API call:', error);
110
143
  }
111
144
  }
112
145
  /**
113
- * Log just the request part (when response isn't available yet)
146
+ * Get the target array for current context
114
147
  */
115
- logRequest(method, url, headers, body) {
116
- if (!this.enabled) {
117
- return;
148
+ getTargetArray() {
149
+ switch (this.currentContext) {
150
+ case 'preconditions':
151
+ return this.preconditions;
152
+ case 'teardown':
153
+ return this.teardownSteps;
154
+ case 'test':
155
+ default:
156
+ return this.steps;
118
157
  }
119
- this.currentRequest = { method, url, headers, body };
120
- this.requestStartTime = Date.now();
121
158
  }
122
159
  /**
123
- * Log just the response part (pairs with logRequest)
160
+ * Finalize and write the structured test document to file
124
161
  */
125
- logResponse(status, headers, body) {
126
- if (!this.enabled || !this.currentRequest) {
162
+ finalize(result, additionalInfo) {
163
+ if (!this.enabled) {
127
164
  return;
128
165
  }
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;
166
+ try {
167
+ const finishedAt = new Date().toISOString();
168
+ const startTime = new Date(this.startedAt).getTime();
169
+ const endTime = new Date(finishedAt).getTime();
170
+ // Calculate total API duration
171
+ const allSteps = [...this.preconditions, ...this.steps, ...this.teardownSteps];
172
+ const totalApiDuration = allSteps.reduce((sum, s) => sum + s.duration, 0);
173
+ const document = {
174
+ test: {
175
+ name: this.testName,
176
+ file: this.testFile || additionalInfo?.testFile,
177
+ startedAt: this.startedAt,
178
+ finishedAt,
179
+ duration: endTime - startTime,
180
+ result,
181
+ },
182
+ preconditions: this.preconditions,
183
+ steps: this.steps,
184
+ teardown: this.teardownSteps,
185
+ summary: {
186
+ totalRequests: allSteps.length,
187
+ preconditions: this.preconditions.length,
188
+ testSteps: this.steps.length,
189
+ teardown: this.teardownSteps.length,
190
+ totalDuration: totalApiDuration,
191
+ },
192
+ };
193
+ this.writeDocument(document);
194
+ }
195
+ catch (error) {
196
+ console.warn('[ApiLogger] Error finalizing log:', error);
197
+ }
133
198
  }
134
199
  /**
135
- * Write log entry to file
200
+ * Write structured document to file
136
201
  */
137
- writeLogEntry(entry) {
202
+ writeDocument(document) {
138
203
  try {
139
204
  if (!this.logFilePath) {
140
205
  return;
141
206
  }
142
- const logLine = JSON.stringify(entry, null, 2);
143
- fs_1.default.appendFileSync(this.logFilePath, logLine + '\n\n');
207
+ fs_1.default.writeFileSync(this.logFilePath, JSON.stringify(document, null, 2) + '\n');
144
208
  }
145
209
  catch (error) {
146
210
  console.warn('[ApiLogger] Failed to write log file:', error);
147
211
  }
148
212
  }
149
- /**
150
- * Generate ISO timestamp for filenames
151
- */
152
213
  generateTimestamp() {
153
- const now = new Date();
154
- return now
214
+ return new Date()
155
215
  .toISOString()
156
216
  .replace(/[:.]/g, '-')
157
- .replace('T', 'T')
158
217
  .split('.')[0];
159
218
  }
160
- /**
161
- * Sanitize test name for use in filename
162
- */
163
219
  sanitizeTestName(name) {
164
220
  return name
165
221
  .toLowerCase()
@@ -168,43 +224,12 @@ class ApiLogger {
168
224
  .replace(/^-|-$/g, '')
169
225
  .substring(0, 80);
170
226
  }
171
- /**
172
- * Get the current log file path
173
- */
174
227
  getLogFilePath() {
175
228
  return this.logFilePath;
176
229
  }
177
- /**
178
- * Check if logger is enabled
179
- */
180
230
  isEnabled() {
181
231
  return this.enabled;
182
232
  }
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
233
  }
209
234
  exports.ApiLogger = ApiLogger;
210
235
  /**
@@ -214,10 +239,10 @@ function createApiLogger(testName, context = 'test') {
214
239
  return new ApiLogger({ testName, context });
215
240
  }
216
241
  /**
217
- * Factory for setup context
242
+ * Factory for setup/preconditions context
218
243
  */
219
244
  function createSetupLogger(testName) {
220
- return new ApiLogger({ testName, context: 'setup' });
245
+ return new ApiLogger({ testName, context: 'preconditions' });
221
246
  }
222
247
  /**
223
248
  * Factory for teardown context
@@ -1 +1 @@
1
- {"version":3,"file":"ApiLogger.js","sourceRoot":"","sources":["../src/ApiLogger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ApiLogger.js","sourceRoot":"","sources":["../src/ApiLogger.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;;;;AAkRH,0CAEC;AAKD,8CAEC;AAKD,oDAEC;AAhSD,4CAAoB;AACpB,gDAAwB;AACxB,mDAAgD;AAUhD,MAAa,SAAS;IAkBpB,YAAY,SAAuB,EAAE;QAZ7B,gBAAW,GAAkB,IAAI,CAAC;QAI1C,qBAAqB;QACb,kBAAa,GAAmB,EAAE,CAAC;QACnC,UAAK,GAAmB,EAAE,CAAC;QAC3B,kBAAa,GAAmB,EAAE,CAAC;QAMzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC;QAE/C,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,cAAc,CAAC;QAClD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;QAC/C,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC,sBAAsB,EAAE,CAAC;QACzE,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,IAAI,CAAC;QACpD,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAE1C,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAEO,sBAAsB;QAC5B,OAAO,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IAEO,iBAAiB;QACvB,IAAI,CAAC;YACH,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;gBACtC,YAAE,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,CAAC;YAED,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3C,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/D,MAAM,QAAQ,GAAG,GAAG,iBAAiB,IAAI,SAAS,MAAM,CAAC;YACzD,IAAI,CAAC,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,4CAA4C,EAAE,KAAK,CAAC,CAAC;YAClE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,QAAQ,CAAC,WAAmB;QAC1B,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,UAAU,CAAC,OAAmB;QAC5B,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,kBAAkB;QAChB,IAAI,CAAC,cAAc,GAAG,eAAe,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,SAAS;QACP,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,aAAa;QACX,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,UAAU,CACR,MAAc,EACd,GAAW,EACX,cAA6D,EAC7D,WAAgB,EAChB,MAAc,EACd,eAAmD,EACnD,YAAiB,EACjB,QAAgB;QAEhB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,IAAI,WAA+B,CAAC;YACpC,IAAI,cAAc,EAAE,CAAC;gBACnB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;oBAC1D,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,cAAc,EAAE,CAAC;wBACzC,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;wBACtD,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;YAED,MAAM,WAAW,GAAmB;gBAClC,MAAM;gBACN,GAAG;gBACH,OAAO,EAAE,cAAc;gBACvB,IAAI,EAAE,WAAW;gBACjB,WAAW;aACZ,CAAC;YAEF,MAAM,YAAY,GAAoB;gBACpC,MAAM;gBACN,OAAO,EAAE,eAAe;gBACxB,IAAI,EAAE,YAAY;aACnB,CAAC;YAEF,MAAM,IAAI,GAAG,6BAAa,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;YAEtE,mCAAmC;YACnC,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1C,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;YAE1C,MAAM,SAAS,GAAiB;gBAC9B,IAAI,EAAE,UAAU;gBAChB,WAAW,EAAE,IAAI,CAAC,eAAe;gBACjC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,OAAO,EAAE,WAAW;gBACpB,QAAQ,EAAE,YAAY;gBACtB,QAAQ;gBACR,IAAI;aACL,CAAC;YAEF,8BAA8B;YAC9B,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YAEjC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED;;OAEG;IACK,cAAc;QACpB,QAAQ,IAAI,CAAC,cAAc,EAAE,CAAC;YAC5B,KAAK,eAAe;gBAClB,OAAO,IAAI,CAAC,aAAa,CAAC;YAC5B,KAAK,UAAU;gBACb,OAAO,IAAI,CAAC,aAAa,CAAC;YAC5B,KAAK,MAAM,CAAC;YACZ;gBACE,OAAO,IAAI,CAAC,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,MAAuC,EAAE,cAAoC;QACpF,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC5C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;YACrD,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;YAE/C,+BAA+B;YAC/B,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;YAC/E,MAAM,gBAAgB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAE1E,MAAM,QAAQ,GAAoB;gBAChC,IAAI,EAAE;oBACJ,IAAI,EAAE,IAAI,CAAC,QAAQ;oBACnB,IAAI,EAAE,IAAI,CAAC,QAAQ,IAAI,cAAc,EAAE,QAAQ;oBAC/C,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,UAAU;oBACV,QAAQ,EAAE,OAAO,GAAG,SAAS;oBAC7B,MAAM;iBACP;gBACD,aAAa,EAAE,IAAI,CAAC,aAAa;gBACjC,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,IAAI,CAAC,aAAa;gBAC5B,OAAO,EAAE;oBACP,aAAa,EAAE,QAAQ,CAAC,MAAM;oBAC9B,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM;oBACxC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;oBAC5B,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM;oBACnC,aAAa,EAAE,gBAAgB;iBAChC;aACF,CAAC;YAEF,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED;;OAEG;IACK,aAAa,CAAC,QAAyB;QAC7C,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACtB,OAAO;YACT,CAAC;YACD,YAAE,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC/E,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,OAAO,IAAI,IAAI,EAAE;aACd,WAAW,EAAE;aACb,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;aACrB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IAEO,gBAAgB,CAAC,IAAY;QACnC,OAAO,IAAI;aACR,WAAW,EAAE;aACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;aAC3B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;aACrB,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AA/PD,8BA+PC;AAED;;GAEG;AACH,SAAgB,eAAe,CAAC,QAAgB,EAAE,UAAsB,MAAM;IAC5E,OAAO,IAAI,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,QAAgB;IAChD,OAAO,IAAI,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB,CAAC,QAAgB;IACnD,OAAO,IAAI,SAAS,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;AAC1D,CAAC"}
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 type { LoggerConfig, RequestLogData, ResponseLogData, LogEntry, LogContext } from './types';
3
+ export { withApiLogging } from './withApiLogging';
4
+ export type { ApiLoggingOptions } from './withApiLogging';
5
+ export type { LoggerConfig, RequestLogData, ResponseLogData, StepLogEntry, TestLogDocument, LogEntry, LogContext, } from './types';
4
6
  export type { RequestData } from './CurlGenerator';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -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,EACV,YAAY,EACZ,cAAc,EACd,eAAe,EACf,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,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"}
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- export type LogContext = 'setup' | 'test' | 'teardown';
1
+ export type LogContext = 'preconditions' | 'test' | 'teardown';
2
2
  export interface LoggerConfig {
3
3
  testName?: string;
4
+ testFile?: string;
4
5
  context?: LogContext;
5
6
  logDirectory?: string;
6
7
  maskAuthTokens?: boolean;
@@ -17,6 +18,36 @@ export interface ResponseLogData {
17
18
  headers?: Record<string, string>;
18
19
  body?: any;
19
20
  }
21
+ export interface StepLogEntry {
22
+ step: number;
23
+ description?: string;
24
+ timestamp: string;
25
+ request: RequestLogData;
26
+ response: ResponseLogData;
27
+ duration: number;
28
+ curl: string;
29
+ }
30
+ export interface TestLogDocument {
31
+ test: {
32
+ name: string;
33
+ file?: string;
34
+ startedAt: string;
35
+ finishedAt?: string;
36
+ duration?: number;
37
+ result?: 'PASSED' | 'FAILED' | 'SKIPPED';
38
+ };
39
+ preconditions: StepLogEntry[];
40
+ steps: StepLogEntry[];
41
+ teardown: StepLogEntry[];
42
+ summary: {
43
+ totalRequests: number;
44
+ preconditions: number;
45
+ testSteps: number;
46
+ teardown: number;
47
+ totalDuration: number;
48
+ };
49
+ }
50
+ /** @deprecated Use StepLogEntry instead */
20
51
  export interface LogEntry {
21
52
  timestamp: string;
22
53
  testName: string;
@@ -1 +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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,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,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,eAAe,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;KAC1C,CAAC;IACF,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,OAAO,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED,2CAA2C;AAC3C,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"}
@@ -0,0 +1,49 @@
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
+ /** Test file path (auto-detected from testInfo if provided) */
12
+ testFile?: string;
13
+ /** Log context: 'preconditions' | 'test' | 'teardown' (default: 'test') */
14
+ context?: LogContext;
15
+ /** Custom log directory (default: 'logs/') */
16
+ logDirectory?: string;
17
+ /** Mask Authorization headers (default: true) */
18
+ maskAuthTokens?: boolean;
19
+ /** Existing logger to share across setup/test/teardown phases */
20
+ logger?: ApiLogger;
21
+ }
22
+ /**
23
+ * Wrap Playwright's APIRequestContext with automatic logging.
24
+ * All HTTP calls (get, post, put, patch, delete, head, fetch) are intercepted and logged.
25
+ *
26
+ * @param request - Playwright APIRequestContext
27
+ * @param testInfoOrOptions - TestInfo object or ApiLoggingOptions
28
+ * @returns Proxied APIRequestContext with logging + logger reference via `__logger`
29
+ *
30
+ * @example
31
+ * // Minimal — just pass testInfo:
32
+ * const loggedRequest = withApiLogging(request, testInfo);
33
+ * const apiClient = new ApiClient(loggedRequest);
34
+ *
35
+ * @example
36
+ * // With preconditions and test steps:
37
+ * const loggedRequest = withApiLogging(request, testInfo);
38
+ * loggedRequest.__logger.startPreconditions();
39
+ * loggedRequest.__logger.describe('Get employee for test');
40
+ * await apiClient.getEmployees();
41
+ * loggedRequest.__logger.startTest();
42
+ * loggedRequest.__logger.describe('Try to access without token');
43
+ * await apiClient.getWithoutAuth();
44
+ * loggedRequest.__logger.finalize('PASSED');
45
+ */
46
+ export declare function withApiLogging(request: APIRequestContext, testInfoOrOptions?: TestInfo | ApiLoggingOptions): APIRequestContext & {
47
+ __logger: ApiLogger;
48
+ };
49
+ //# 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,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iEAAiE;IACjE,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,iBAAiB,CAAC,EAAE,QAAQ,GAAG,iBAAiB,GAC/C,iBAAiB,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,CAmG7C"}
@@ -0,0 +1,133 @@
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 preconditions and test steps:
24
+ * const loggedRequest = withApiLogging(request, testInfo);
25
+ * loggedRequest.__logger.startPreconditions();
26
+ * loggedRequest.__logger.describe('Get employee for test');
27
+ * await apiClient.getEmployees();
28
+ * loggedRequest.__logger.startTest();
29
+ * loggedRequest.__logger.describe('Try to access without token');
30
+ * await apiClient.getWithoutAuth();
31
+ * loggedRequest.__logger.finalize('PASSED');
32
+ */
33
+ function withApiLogging(request, testInfoOrOptions) {
34
+ let options;
35
+ if (testInfoOrOptions && 'title' in testInfoOrOptions) {
36
+ const testInfo = testInfoOrOptions;
37
+ options = {
38
+ testName: testInfo.title,
39
+ testFile: testInfo.file,
40
+ context: 'test',
41
+ };
42
+ }
43
+ else {
44
+ options = testInfoOrOptions || {};
45
+ }
46
+ // Use shared logger or create new
47
+ let logger;
48
+ if (options.logger) {
49
+ logger = options.logger;
50
+ if (options.context) {
51
+ logger.setContext(options.context);
52
+ }
53
+ }
54
+ else {
55
+ const config = {
56
+ testName: options.testName,
57
+ testFile: options.testFile,
58
+ context: options.context || 'test',
59
+ logDirectory: options.logDirectory,
60
+ maskAuthTokens: options.maskAuthTokens,
61
+ };
62
+ logger = new ApiLogger_1.ApiLogger(config);
63
+ }
64
+ // If logging is disabled, return original request with logger ref
65
+ if (!logger.isEnabled()) {
66
+ return Object.assign(request, { __logger: logger });
67
+ }
68
+ const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'fetch'];
69
+ const proxy = new Proxy(request, {
70
+ get(target, prop, receiver) {
71
+ const propName = typeof prop === 'string' ? prop : '';
72
+ if (propName === '__logger') {
73
+ return logger;
74
+ }
75
+ if (HTTP_METHODS.includes(propName)) {
76
+ return async (url, reqOptions) => {
77
+ const method = propName === 'fetch' ? (reqOptions?.method || 'GET') : propName;
78
+ const startTime = Date.now();
79
+ const requestHeaders = reqOptions?.headers;
80
+ const requestBody = extractBody(reqOptions);
81
+ try {
82
+ const response = await target[propName](url, reqOptions);
83
+ const duration = Date.now() - startTime;
84
+ const responseBody = await safeParseResponseBody(response);
85
+ const responseHeaders = extractResponseHeaders(response);
86
+ logger.logApiCall(method.toUpperCase(), response.url(), requestHeaders, requestBody, response.status(), responseHeaders, responseBody, duration);
87
+ return response;
88
+ }
89
+ catch (error) {
90
+ const duration = Date.now() - startTime;
91
+ logger.logApiCall(method.toUpperCase(), url, requestHeaders, requestBody, 0, undefined, { error: String(error) }, duration);
92
+ throw error;
93
+ }
94
+ };
95
+ }
96
+ return Reflect.get(target, prop, receiver);
97
+ },
98
+ });
99
+ return Object.assign(proxy, { __logger: logger });
100
+ }
101
+ function extractBody(options) {
102
+ if (!options)
103
+ return undefined;
104
+ if (options.data)
105
+ return options.data;
106
+ if (options.form)
107
+ return options.form;
108
+ if (options.multipart)
109
+ return options.multipart;
110
+ return undefined;
111
+ }
112
+ async function safeParseResponseBody(response) {
113
+ try {
114
+ return await response.json();
115
+ }
116
+ catch {
117
+ try {
118
+ return await response.text();
119
+ }
120
+ catch {
121
+ return null;
122
+ }
123
+ }
124
+ }
125
+ function extractResponseHeaders(response) {
126
+ try {
127
+ return response.headers();
128
+ }
129
+ catch {
130
+ return undefined;
131
+ }
132
+ }
133
+ //# sourceMappingURL=withApiLogging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withApiLogging.js","sourceRoot":"","sources":["../src/withApiLogging.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playwright-api-logger",
3
- "version": "1.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Comprehensive API request/response logger with curl export for Playwright tests",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",