@michalfidor/playswag 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Michał Fidor
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 CHANGED
@@ -52,7 +52,7 @@ export default defineConfig({
52
52
 
53
53
  // Optional
54
54
  outputDir: './playswag-coverage',
55
- outputFormats: ['console', 'json'], // default
55
+ outputFormats: ['console', 'json'], // also: 'html', 'badge'
56
56
 
57
57
  threshold: {
58
58
  endpoints: 80, // warn / fail if < 80% of endpoints are hit
@@ -74,6 +74,8 @@ npx playwright test
74
74
  ```
75
75
 
76
76
  Coverage is printed to the terminal and written to `./playswag-coverage/playswag-coverage.json`.
77
+ To also get an interactive HTML report, add `'html'` to `outputFormats` — the reporter will print a
78
+ clickable `file://` link at the end of the run (suppressed when `CI=true`).
77
79
 
78
80
  ---
79
81
 
@@ -94,7 +96,7 @@ interface PlayswagConfig {
94
96
  outputDir?: string;
95
97
 
96
98
  /** Which output formats to produce. @default ['console', 'json'] */
97
- outputFormats?: Array<'console' | 'json'>;
99
+ outputFormats?: Array<'console' | 'json' | 'html' | 'badge'>;
98
100
 
99
101
  /**
100
102
  * Base URL of the API under test.
@@ -111,7 +113,7 @@ interface PlayswagConfig {
111
113
  consoleOutput?: {
112
114
  enabled?: boolean; // @default true
113
115
  showUncoveredOnly?: boolean; // @default false
114
- showDetails?: boolean; // @default true — per-operation table
116
+ showOperations?: boolean; // @default true — per-operation table
115
117
  showParams?: boolean; // @default false
116
118
  showBodyProperties?: boolean; // @default false
117
119
  };
@@ -122,11 +124,34 @@ interface PlayswagConfig {
122
124
  pretty?: boolean; // @default true
123
125
  };
124
126
 
127
+ /**
128
+ * Options for the standalone HTML coverage report.
129
+ * Enable by adding 'html' to outputFormats.
130
+ */
131
+ htmlOutput?: {
132
+ enabled?: boolean; // @default true
133
+ fileName?: string; // @default 'playswag-coverage.html'
134
+ title?: string; // @default 'API Coverage Report'
135
+ };
136
+
137
+ /**
138
+ * Options for the SVG coverage badge.
139
+ * Enable by adding 'badge' to outputFormats.
140
+ */
141
+ badge?: {
142
+ enabled?: boolean; // @default true
143
+ fileName?: string; // @default 'playswag-badge.svg'
144
+ /** Which coverage dimension drives the badge percentage. */
145
+ dimension?: 'endpoints' | 'statusCodes' | 'parameters' | 'bodyProperties'; // @default 'endpoints'
146
+ label?: string; // @default 'API Coverage'
147
+ };
148
+
125
149
  threshold?: {
126
- endpoints?: number; // 0–100
127
- statusCodes?: number;
128
- parameters?: number;
129
- bodyProperties?: number;
150
+ // Plain number: informational warning only (respects failOnThreshold globally)
151
+ endpoints?: number | { min: number; fail?: boolean };
152
+ statusCodes?: number | { min: number; fail?: boolean };
153
+ parameters?: number | { min: number; fail?: boolean };
154
+ bodyProperties?: number | { min: number; fail?: boolean };
130
155
  };
131
156
 
132
157
  /**
@@ -152,6 +177,48 @@ projects: [
152
177
  test.use({ playswagEnabled: false });
153
178
  ```
154
179
 
180
+ ## Tracking custom request contexts
181
+
182
+ The built-in `request` fixture is wrapped automatically. If your tests use **additional
183
+ `APIRequestContext` instances** — for example contexts created with `request.newContext()`
184
+ or returned by a custom fixture — use the `trackRequest` fixture to wrap them:
185
+
186
+ ```ts
187
+ import { test, expect } from '@michalfidor/playswag';
188
+
189
+ // request.newContext()
190
+ test('uses a second context', async ({ request, trackRequest }) => {
191
+ const adminCtx = trackRequest(await request.newContext({ extraHTTPHeaders: { 'x-role': 'admin' } }));
192
+ const res = await adminCtx.get('/api/admin/stats');
193
+ expect(res.ok()).toBeTruthy();
194
+ });
195
+ ```
196
+
197
+ `trackRequest` is most useful inside **custom fixtures** that create their own contexts:
198
+
199
+ ```ts
200
+ import { test as base } from '@michalfidor/playswag';
201
+
202
+ const test = base.extend<{ adminRequest: import('@playwright/test').APIRequestContext }>({
203
+ adminRequest: async ({ trackRequest }, use) => {
204
+ const raw = await ContextFactory.getContextByUserAccessToken('admin');
205
+ await use(trackRequest(raw));
206
+ },
207
+ });
208
+
209
+ export { test };
210
+
211
+ // Then in tests:
212
+ test('admin can list users', async ({ adminRequest }) => {
213
+ const res = await adminRequest.get('/api/admin/users');
214
+ expect(res.ok()).toBeTruthy();
215
+ });
216
+ ```
217
+
218
+ All calls made through any `trackRequest`-wrapped context are recorded alongside
219
+ calls from the main `request` fixture. They all end up in the same per-test
220
+ attachment and contribute to the coverage report.
221
+
155
222
  ---
156
223
 
157
224
  ## Multiple spec files
@@ -168,6 +235,61 @@ Duplicate `method + path` entries across files are de-duplicated (last one wins,
168
235
 
169
236
  ---
170
237
 
238
+ ---
239
+
240
+ ## HTML report
241
+
242
+ Add `'html'` to `outputFormats` to generate a self-contained, zero-dependency HTML file alongside
243
+ the JSON report:
244
+
245
+ ```ts
246
+ outputFormats: ['console', 'json', 'html'],
247
+ htmlOutput: {
248
+ fileName: 'playswag-coverage.html', // written to outputDir
249
+ title: 'My API Coverage',
250
+ },
251
+ ```
252
+
253
+ After the run, the reporter prints a clickable link:
254
+
255
+ ```
256
+ [playswag] HTML report → file:///path/to/playswag-coverage/playswag-coverage.html
257
+ ```
258
+
259
+ (On CI the `file://` link is omitted; only the relative path is logged.)
260
+
261
+ The report includes:
262
+ - Summary cards with progress bars for all four dimensions
263
+ - Operations table with **All / Covered / Uncovered** filter buttons and per-tag filtering
264
+ - Click any row to expand status codes, parameters, body properties, and the tests that hit it
265
+ - Unmatched hits section (calls that matched no spec operation)
266
+ - Dark / light theme toggle (persisted to `localStorage`)
267
+
268
+ ---
269
+
270
+ ## SVG badge
271
+
272
+ Add `'badge'` to `outputFormats` to write a shields.io-style SVG badge:
273
+
274
+ ```ts
275
+ outputFormats: ['console', 'json', 'badge'],
276
+ badge: {
277
+ dimension: 'endpoints', // the percentage shown on the badge
278
+ label: 'API coverage',
279
+ fileName: 'playswag-badge.svg',
280
+ },
281
+ ```
282
+
283
+ Commit the badge and embed it in your README:
284
+
285
+ ```markdown
286
+ ![API coverage](./playswag-coverage/playswag-badge.svg)
287
+ ```
288
+
289
+ Colour thresholds: **green** ≥ 80 % · **orange** ≥ 50 % · **red** < 50 %.
290
+
291
+ ---
292
+
171
293
  ## Console output example
172
294
 
173
295
  ```
@@ -193,6 +315,9 @@ Duplicate `method + path` entries across files are de-duplicated (last one wins,
193
315
  {
194
316
  "specFiles": ["./openapi.yaml"],
195
317
  "timestamp": "2026-03-04T12:00:00.000Z",
318
+ "playwrightVersion": "1.50.0",
319
+ "playswagVersion": "1.0.0",
320
+ "totalTestCount": 12,
196
321
  "summary": {
197
322
  "endpoints": { "total": 6, "covered": 5, "percentage": 83.3 },
198
323
  "statusCodes": { "total": 11, "covered": 7, "percentage": 63.6 },
@@ -239,7 +364,7 @@ testInfo.attach( onTestEnd():
239
364
  onEnd():
240
365
  parse OpenAPI spec(s)
241
366
  match hits → path templates
242
- calculate 3-tier coverage
367
+ calculate 4-dimension coverage
243
368
  print console report
244
369
  write JSON file
245
370
  ```
@@ -0,0 +1 @@
1
+ Drop your logo file (logo.png) into this directory. It is referenced from README.md.
Binary file