executable-stories-formatters 0.6.2 → 0.7.1
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/dist/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/cli.js +13522 -2093
- package/dist/cli.js.map +1 -1
- package/dist/{index-C4QO-SVT.d.cts → index-C0OOaaiK.d.cts} +19 -2
- package/dist/{index-C4QO-SVT.d.ts → index-C0OOaaiK.d.ts} +19 -2
- package/dist/index.cjs +12706 -2093
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +412 -48
- package/dist/index.d.ts +412 -48
- package/dist/index.js +12696 -2092
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/schemas/raw-run.schema.json +48 -12
- package/skills/formatters-cli/SKILL.md +80 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "executable-stories-formatters",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Cucumber-compatible report formats (HTML, Markdown, JUnit XML, Cucumber JSON) for executable-stories test results.",
|
|
5
5
|
"author": "Jag Reehal <jag@jagreehal.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,16 +57,16 @@
|
|
|
57
57
|
],
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@cucumber/html-formatter": "^23.0.0",
|
|
60
|
-
"@cucumber/messages": "^32.0
|
|
60
|
+
"@cucumber/messages": "^32.2.0",
|
|
61
61
|
"ajv": "^8.18.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@faker-js/faker": "^10.3.0",
|
|
65
|
-
"@types/node": "^25.
|
|
65
|
+
"@types/node": "^25.5.0",
|
|
66
66
|
"tsup": "^8.5.1",
|
|
67
67
|
"tsx": "^4.21.0",
|
|
68
68
|
"typescript": "~5.9.3",
|
|
69
|
-
"vitest": "^4.0
|
|
69
|
+
"vitest": "^4.1.0",
|
|
70
70
|
"vitest-mock-extended": "^3.1.0"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
@@ -164,8 +164,21 @@
|
|
|
164
164
|
},
|
|
165
165
|
"tickets": {
|
|
166
166
|
"type": "array",
|
|
167
|
-
"items": {
|
|
168
|
-
|
|
167
|
+
"items": {
|
|
168
|
+
"oneOf": [
|
|
169
|
+
{ "type": "string" },
|
|
170
|
+
{
|
|
171
|
+
"type": "object",
|
|
172
|
+
"properties": {
|
|
173
|
+
"id": { "type": "string" },
|
|
174
|
+
"url": { "type": "string" }
|
|
175
|
+
},
|
|
176
|
+
"required": ["id"],
|
|
177
|
+
"additionalProperties": false
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
"description": "Ticket/issue references. Each item is either a string ID or an object with id and optional url."
|
|
169
182
|
},
|
|
170
183
|
"meta": {
|
|
171
184
|
"type": "object",
|
|
@@ -208,6 +221,19 @@
|
|
|
208
221
|
"type": "array",
|
|
209
222
|
"items": { "$ref": "#/$defs/DocEntry" },
|
|
210
223
|
"description": "Rich documentation entries attached to this step."
|
|
224
|
+
},
|
|
225
|
+
"id": {
|
|
226
|
+
"type": "string",
|
|
227
|
+
"description": "Unique step identifier within the scenario (e.g., 'step-0')."
|
|
228
|
+
},
|
|
229
|
+
"wrapped": {
|
|
230
|
+
"type": "boolean",
|
|
231
|
+
"description": "Whether this step wraps a function call (Fn/Expect pattern)."
|
|
232
|
+
},
|
|
233
|
+
"durationMs": {
|
|
234
|
+
"type": "number",
|
|
235
|
+
"minimum": 0,
|
|
236
|
+
"description": "Step-level duration in milliseconds (from startTimer/endTimer)."
|
|
211
237
|
}
|
|
212
238
|
},
|
|
213
239
|
"required": ["keyword", "text"],
|
|
@@ -237,7 +263,8 @@
|
|
|
237
263
|
"properties": {
|
|
238
264
|
"kind": { "const": "note" },
|
|
239
265
|
"text": { "type": "string" },
|
|
240
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
266
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
267
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
241
268
|
},
|
|
242
269
|
"required": ["kind", "text", "phase"],
|
|
243
270
|
"additionalProperties": false
|
|
@@ -251,7 +278,8 @@
|
|
|
251
278
|
"type": "array",
|
|
252
279
|
"items": { "type": "string" }
|
|
253
280
|
},
|
|
254
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
281
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
282
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
255
283
|
},
|
|
256
284
|
"required": ["kind", "names", "phase"],
|
|
257
285
|
"additionalProperties": false
|
|
@@ -263,7 +291,8 @@
|
|
|
263
291
|
"kind": { "const": "kv" },
|
|
264
292
|
"label": { "type": "string" },
|
|
265
293
|
"value": {},
|
|
266
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
294
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
295
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
267
296
|
},
|
|
268
297
|
"required": ["kind", "label", "value", "phase"],
|
|
269
298
|
"additionalProperties": false
|
|
@@ -276,7 +305,8 @@
|
|
|
276
305
|
"label": { "type": "string" },
|
|
277
306
|
"content": { "type": "string" },
|
|
278
307
|
"lang": { "type": "string" },
|
|
279
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
308
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
309
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
280
310
|
},
|
|
281
311
|
"required": ["kind", "label", "content", "phase"],
|
|
282
312
|
"additionalProperties": false
|
|
@@ -298,7 +328,8 @@
|
|
|
298
328
|
"items": { "type": "string" }
|
|
299
329
|
}
|
|
300
330
|
},
|
|
301
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
331
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
332
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
302
333
|
},
|
|
303
334
|
"required": ["kind", "label", "columns", "rows", "phase"],
|
|
304
335
|
"additionalProperties": false
|
|
@@ -310,7 +341,8 @@
|
|
|
310
341
|
"kind": { "const": "link" },
|
|
311
342
|
"label": { "type": "string" },
|
|
312
343
|
"url": { "type": "string" },
|
|
313
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
344
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
345
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
314
346
|
},
|
|
315
347
|
"required": ["kind", "label", "url", "phase"],
|
|
316
348
|
"additionalProperties": false
|
|
@@ -322,7 +354,8 @@
|
|
|
322
354
|
"kind": { "const": "section" },
|
|
323
355
|
"title": { "type": "string" },
|
|
324
356
|
"markdown": { "type": "string" },
|
|
325
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
357
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
358
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
326
359
|
},
|
|
327
360
|
"required": ["kind", "title", "markdown", "phase"],
|
|
328
361
|
"additionalProperties": false
|
|
@@ -334,7 +367,8 @@
|
|
|
334
367
|
"kind": { "const": "mermaid" },
|
|
335
368
|
"code": { "type": "string" },
|
|
336
369
|
"title": { "type": "string" },
|
|
337
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
370
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
371
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
338
372
|
},
|
|
339
373
|
"required": ["kind", "code", "phase"],
|
|
340
374
|
"additionalProperties": false
|
|
@@ -346,7 +380,8 @@
|
|
|
346
380
|
"kind": { "const": "screenshot" },
|
|
347
381
|
"path": { "type": "string" },
|
|
348
382
|
"alt": { "type": "string" },
|
|
349
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
383
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
384
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
350
385
|
},
|
|
351
386
|
"required": ["kind", "path", "phase"],
|
|
352
387
|
"additionalProperties": false
|
|
@@ -358,7 +393,8 @@
|
|
|
358
393
|
"kind": { "const": "custom" },
|
|
359
394
|
"type": { "type": "string" },
|
|
360
395
|
"data": {},
|
|
361
|
-
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
396
|
+
"phase": { "$ref": "#/$defs/DocPhase" },
|
|
397
|
+
"children": { "type": "array", "items": { "$ref": "#/$defs/DocEntry" }, "description": "Nested child doc entries for grouping." }
|
|
362
398
|
},
|
|
363
399
|
"required": ["kind", "type", "data", "phase"],
|
|
364
400
|
"additionalProperties": false
|
|
@@ -36,6 +36,12 @@ executable-stories format raw-run.json --format html,markdown,junit
|
|
|
36
36
|
# Read from stdin
|
|
37
37
|
cat raw-run.json | executable-stories format --stdin --format markdown
|
|
38
38
|
|
|
39
|
+
# Compare two canonical runs for review-friendly output
|
|
40
|
+
executable-stories compare baseline.json current.json \
|
|
41
|
+
--input-type canonical \
|
|
42
|
+
--format html,markdown \
|
|
43
|
+
--output-name review-diff
|
|
44
|
+
|
|
39
45
|
# Validate JSON against schema
|
|
40
46
|
executable-stories validate raw-run.json
|
|
41
47
|
```
|
|
@@ -55,6 +61,8 @@ const generator = new ReportGenerator({
|
|
|
55
61
|
formats: ["markdown", "html"],
|
|
56
62
|
outputDir: "docs",
|
|
57
63
|
outputName: "user-stories",
|
|
64
|
+
outputNameTimestamp: true, // optional: unique filenames per run (e.g. user-stories-1739123456.md)
|
|
65
|
+
sortTestCases: "id", // optional: stable order for diff-friendly reports
|
|
58
66
|
});
|
|
59
67
|
|
|
60
68
|
const outputs = await generator.generate(canonical);
|
|
@@ -99,6 +107,8 @@ const cucumberJson = new CucumberJsonFormatter().formatToString(canonical);
|
|
|
99
107
|
--format html,markdown,junit,cucumber-json,cucumber-html,cucumber-messages
|
|
100
108
|
--output-dir reports # Base directory (default: reports)
|
|
101
109
|
--output-name test-results # Base filename (default: test-results)
|
|
110
|
+
--output-name-timestamp # Append run timestamp (UTC seconds) to filename for before/after diffs
|
|
111
|
+
--sort-test-cases id|source|none # Deterministic scenario order (default: none). Use id for diff-friendly output
|
|
102
112
|
--input-type raw # raw | canonical | ndjson
|
|
103
113
|
|
|
104
114
|
# Filtering
|
|
@@ -118,8 +128,49 @@ const cucumberJson = new CucumberJsonFormatter().formatToString(canonical);
|
|
|
118
128
|
# Machine output
|
|
119
129
|
--json-summary # Print JSON summary to stdout
|
|
120
130
|
--emit-canonical path.json # Write canonical JSON
|
|
131
|
+
|
|
132
|
+
# Asset Bundling
|
|
133
|
+
--asset-mode none|copy # Asset bundling strategy (default: none)
|
|
134
|
+
--allow-missing-assets # Warn on missing assets instead of failing
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Asset Bundling
|
|
138
|
+
|
|
139
|
+
Use `--asset-mode copy` to produce a portable report directory. All locally-referenced assets
|
|
140
|
+
(Playwright videos, screenshots, attachment files) are copied into an `assets/` subdirectory
|
|
141
|
+
and HTML paths are rewritten.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
executable-stories format raw-run.json --format html --output-dir report --asset-mode copy
|
|
121
145
|
```
|
|
122
146
|
|
|
147
|
+
Output:
|
|
148
|
+
```
|
|
149
|
+
report/
|
|
150
|
+
test-results.html # paths rewritten to assets/
|
|
151
|
+
assets/
|
|
152
|
+
video-3f2c1a7b.webm
|
|
153
|
+
step-1-91ab22de.png
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### GitHub Actions usage
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
- run: npx executable-stories format .executable-stories/raw-run.json --format html --output-dir report --asset-mode copy
|
|
160
|
+
- uses: actions/upload-artifact@v4
|
|
161
|
+
with:
|
|
162
|
+
name: test-report
|
|
163
|
+
path: report/
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Options
|
|
167
|
+
|
|
168
|
+
| Flag | Description |
|
|
169
|
+
|------|-------------|
|
|
170
|
+
| `--asset-mode none` | Default. No asset bundling. |
|
|
171
|
+
| `--asset-mode copy` | Copy local assets to `assets/`, rewrite paths. |
|
|
172
|
+
| `--allow-missing-assets` | Warn on missing assets instead of failing. |
|
|
173
|
+
|
|
123
174
|
### Validation
|
|
124
175
|
|
|
125
176
|
```typescript
|
|
@@ -138,6 +189,35 @@ const result = validateCanonicalRun(canonical);
|
|
|
138
189
|
assertValidRun(canonical);
|
|
139
190
|
```
|
|
140
191
|
|
|
192
|
+
### Before/after diffs (evolution of tests)
|
|
193
|
+
|
|
194
|
+
To compare reports across runs (e.g. in CI or locally), use timestamped filenames and deterministic ordering so diffs show real changes instead of random reordering from parallel test execution:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
executable-stories format raw-run.json --format markdown,html \
|
|
198
|
+
--output-name-timestamp \
|
|
199
|
+
--sort-test-cases id
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- `--output-name-timestamp`: appends run start time in UTC seconds (e.g. `test-results-1739123456.md`), so each run produces a unique, chronologically sortable file.
|
|
203
|
+
- `--sort-test-cases id`: sorts scenarios by deterministic id (hash of source file + scenario name) so report content order is stable across runs.
|
|
204
|
+
|
|
205
|
+
Programmatic: set `outputNameTimestamp: true` and `sortTestCases: "id"` (or `"source"` for file/line order) on `FormatterOptions`.
|
|
206
|
+
|
|
207
|
+
For first-class run comparisons, use the dedicated compare subcommand:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
executable-stories compare baseline.json current.json \
|
|
211
|
+
--input-type canonical \
|
|
212
|
+
--format html,markdown \
|
|
213
|
+
--output-dir reports \
|
|
214
|
+
--output-name test-results-diff
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
- Generates a standalone HTML review report with filter chips for `Regressed`, `Fixed`, `Added`, `Removed`, and `Changed`.
|
|
218
|
+
- Generates Markdown with per-scenario before/after summaries for PR discussion or artifact storage.
|
|
219
|
+
- Use canonical input when you already persist prior runs; raw and ndjson inputs are also supported as long as both files use the same `--input-type`.
|
|
220
|
+
|
|
141
221
|
### Notifications
|
|
142
222
|
|
|
143
223
|
```bash
|