executable-stories-formatters 0.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.
@@ -0,0 +1,1468 @@
1
+ import { S as StoryMeta, a as StoryStep, D as DocEntry, R as RawStatus, b as RawAttachment, c as RawRun, d as RawCIInfo, e as adaptJestRun, f as adaptPlaywrightRun, g as adaptVitestRun } from './index-DCJ0NvAp.js';
2
+ export { h as DocPhase, J as JestAdapterOptions, i as JestAggregatedResult, j as JestFileResult, k as JestTestResult, P as PlaywrightAdapterOptions, l as PlaywrightAnnotation, m as PlaywrightAttachment, n as PlaywrightError, o as PlaywrightLocation, p as PlaywrightStatus, q as PlaywrightTestCase, r as PlaywrightTestResult, s as RawStepEvent, t as RawTestCase, u as STORY_META_KEY, v as StepKeyword, w as StepMode, x as StoryFileReport, V as VitestAdapterOptions, y as VitestSerializedError, z as VitestState, A as VitestTestCase, B as VitestTestModule, C as VitestTestResult } from './index-DCJ0NvAp.js';
3
+
4
+ /**
5
+ * Canonical types for Layer 2: Anti-Corruption Layer output.
6
+ *
7
+ * These types are strict and have all required fields populated.
8
+ * Formatters (Layer 3) accept only these canonical types.
9
+ */
10
+
11
+ /** Canonical test status (Cucumber-compatible) */
12
+ type TestStatus = "passed" | "failed" | "skipped" | "pending";
13
+ /** Step result with status and timing */
14
+ interface StepResult {
15
+ /** Step index (0-based) */
16
+ index: number;
17
+ /** Step status */
18
+ status: TestStatus;
19
+ /** Duration in milliseconds (default 0) */
20
+ durationMs: number;
21
+ /** Error message if step failed */
22
+ errorMessage?: string;
23
+ }
24
+ /** Resolved attachment (always has body) */
25
+ interface Attachment {
26
+ /** Attachment name */
27
+ name: string;
28
+ /** MIME type */
29
+ mediaType: string;
30
+ /** Content (base64-encoded or URL) */
31
+ body: string;
32
+ /** Content encoding */
33
+ contentEncoding: "BASE64" | "IDENTITY";
34
+ }
35
+ /** Single test attempt for retry tracking */
36
+ interface TestCaseAttempt {
37
+ /** Attempt number (0-based) */
38
+ attempt: number;
39
+ /** Status of this attempt */
40
+ status: TestStatus;
41
+ /** Duration of this attempt in milliseconds */
42
+ durationMs: number;
43
+ /** Error message if this attempt failed */
44
+ errorMessage?: string;
45
+ /** Error stack trace if this attempt failed */
46
+ errorStack?: string;
47
+ }
48
+ /** Canonical test case result */
49
+ interface TestCaseResult {
50
+ /** Unique deterministic ID */
51
+ id: string;
52
+ /** Story metadata (required) */
53
+ story: StoryMeta;
54
+ /** Source file path (required) */
55
+ sourceFile: string;
56
+ /** Source line number (required, default 1) */
57
+ sourceLine: number;
58
+ /** Test status (required) */
59
+ status: TestStatus;
60
+ /** Duration in milliseconds (required, default 0) */
61
+ durationMs: number;
62
+ /** Error message if failed */
63
+ errorMessage?: string;
64
+ /** Error stack trace if failed */
65
+ errorStack?: string;
66
+ /** Attachments (required, empty array if none) */
67
+ attachments: Attachment[];
68
+ /** Step results (required, always populated via fallback rules) */
69
+ stepResults: StepResult[];
70
+ /** Full title path from suite/describe blocks (required, empty array if none) */
71
+ titlePath: string[];
72
+ /** Playwright project name (optional) */
73
+ projectName?: string;
74
+ /** Retry attempt number (required, default 0) */
75
+ retry: number;
76
+ /** Total retries configured (required, default 0) */
77
+ retries: number;
78
+ /** Normalized tags from story (required, empty array if none) */
79
+ tags: string[];
80
+ /** All retry attempts (optional, includes details per attempt) */
81
+ attempts?: TestCaseAttempt[];
82
+ }
83
+ /** CI environment info */
84
+ interface CIInfo {
85
+ name: string;
86
+ url?: string;
87
+ buildNumber?: string;
88
+ }
89
+ /** Coverage summary for the test run */
90
+ interface CoverageSummary {
91
+ /** Line coverage percentage (0-100) */
92
+ linesPct?: number;
93
+ /** Branch coverage percentage (0-100) */
94
+ branchesPct?: number;
95
+ /** Function coverage percentage (0-100) */
96
+ functionsPct?: number;
97
+ /** Statement coverage percentage (0-100) */
98
+ statementsPct?: number;
99
+ }
100
+ /** Canonical test run result */
101
+ interface TestRunResult {
102
+ /** All test case results */
103
+ testCases: TestCaseResult[];
104
+ /** Run start time (epoch ms, required) */
105
+ startedAtMs: number;
106
+ /** Run finish time (epoch ms, required) */
107
+ finishedAtMs: number;
108
+ /** Total duration in milliseconds (required) */
109
+ durationMs: number;
110
+ /** Project root directory (required) */
111
+ projectRoot: string;
112
+ /** Unique run ID (required, generated) */
113
+ runId: string;
114
+ /** Package version */
115
+ packageVersion?: string;
116
+ /** Git commit SHA */
117
+ gitSha?: string;
118
+ /** CI environment info */
119
+ ci?: CIInfo;
120
+ /** Coverage summary for the run */
121
+ coverage?: CoverageSummary;
122
+ }
123
+
124
+ /**
125
+ * Configuration options for ACL and formatters.
126
+ */
127
+ /** Options for canonicalizing raw run data */
128
+ interface CanonicalizeOptions {
129
+ /** Attachment handling options */
130
+ attachments?: {
131
+ /** Max bytes before attachment becomes external link. Default: 512KB (524288) */
132
+ maxEmbedBytes?: number;
133
+ /** Directory for external attachments */
134
+ externalDir?: string;
135
+ };
136
+ /** Cucumber compatibility options */
137
+ cucumber?: {
138
+ /** Include trailing space in keywords (e.g., "Given "). Default: true */
139
+ keywordSpacing?: boolean;
140
+ /** Generate deterministic line numbers. Default: true */
141
+ deterministicLines?: boolean;
142
+ };
143
+ /** Default timestamps if not provided in raw data */
144
+ defaults?: {
145
+ /** Default start time (epoch ms). Default: Date.now() */
146
+ startedAtMs?: number;
147
+ /** Default finish time (epoch ms). Default: Date.now() */
148
+ finishedAtMs?: number;
149
+ };
150
+ }
151
+ /** Output format for report generation */
152
+ type OutputFormat = "cucumber-json" | "cucumber-messages" | "cucumber-html" | "html" | "junit" | "markdown";
153
+ /** Output mode for report routing */
154
+ type OutputMode = "aggregated" | "colocated";
155
+ /** Colocated output style */
156
+ type ColocatedStyle = "mirrored" | "adjacent";
157
+ /** Output rule for routing reports based on source file patterns */
158
+ interface OutputRule {
159
+ /** Glob pattern to match sourceFile (uses micromatch, forward slashes) */
160
+ match: string;
161
+ /** Output mode for matched files */
162
+ mode?: OutputMode;
163
+ /** Colocated style (only applicable when mode is "colocated") */
164
+ colocatedStyle?: ColocatedStyle;
165
+ /** Output directory override */
166
+ outputDir?: string;
167
+ /** Output filename override (without extension) */
168
+ outputName?: string;
169
+ /** Formats to generate for matched files */
170
+ formats?: OutputFormat[];
171
+ }
172
+ /** Output configuration for report routing */
173
+ interface OutputConfig {
174
+ /** Default output mode. Default: "aggregated" */
175
+ mode?: OutputMode;
176
+ /** Default colocated style. Default: "mirrored" */
177
+ colocatedStyle?: ColocatedStyle;
178
+ /** Rules for routing reports based on source file patterns */
179
+ rules?: OutputRule[];
180
+ /** Default output filename (without extension) */
181
+ outputName?: string;
182
+ }
183
+ /** Logger interface for dependency injection */
184
+ interface Logger {
185
+ warn(msg: string): void;
186
+ }
187
+ /** File writer function type for dependency injection */
188
+ type WriteFile = (path: string, contents: string) => Promise<void>;
189
+ /** Formatter options for report generation */
190
+ interface FormatterOptions {
191
+ /** Glob patterns to include test cases by sourceFile (forward slashes). If empty, all are considered. */
192
+ include?: string[];
193
+ /** Glob patterns to exclude test cases by sourceFile (forward slashes). Applied after include. */
194
+ exclude?: string[];
195
+ /** Output formats to generate. Default: ["cucumber-json"] */
196
+ formats?: OutputFormat[];
197
+ /** Output directory for generated reports. Default: "reports" */
198
+ outputDir?: string;
199
+ /** Base filename (without extension). Default: "test-results" */
200
+ outputName?: string;
201
+ /** Output routing configuration */
202
+ output?: OutputConfig;
203
+ /** Cucumber JSON specific options */
204
+ cucumberJson?: {
205
+ /** Pretty-print JSON output. Default: false */
206
+ pretty?: boolean;
207
+ };
208
+ /** HTML specific options */
209
+ html?: {
210
+ /** Report title. Default: "Test Results" */
211
+ title?: string;
212
+ /** Include dark mode toggle. Default: true */
213
+ darkMode?: boolean;
214
+ /** Include search/filter functionality. Default: true */
215
+ searchable?: boolean;
216
+ /** Start with scenarios collapsed. Default: false */
217
+ startCollapsed?: boolean;
218
+ /** Embed screenshots inline (base64). Default: true */
219
+ embedScreenshots?: boolean;
220
+ /** Enable syntax highlighting for code blocks (via highlight.js CDN). Default: true */
221
+ syntaxHighlighting?: boolean;
222
+ /** Enable live Mermaid diagram rendering (via Mermaid.js CDN). Default: true */
223
+ mermaidEnabled?: boolean;
224
+ /** Enable Markdown parsing for section doc entries (via marked.js CDN). Default: true */
225
+ markdownEnabled?: boolean;
226
+ };
227
+ /** JUnit XML specific options */
228
+ junit?: {
229
+ /** Test suite name. Default: "Test Suite" */
230
+ suiteName?: string;
231
+ /** Include system-out/system-err. Default: true */
232
+ includeOutput?: boolean;
233
+ };
234
+ /** Cucumber Messages (NDJSON) specific options */
235
+ cucumberMessages?: {
236
+ /** Strategy for deriving Source.uri. Default: "sourceFile" */
237
+ uriStrategy?: "sourceFile" | "virtual";
238
+ /** Whether to emit Source/GherkinDocument for synthesized features. Default: true */
239
+ includeSynthetics?: boolean;
240
+ /** Salt for deterministic IDs. Default: "" */
241
+ idSalt?: string;
242
+ /** Tool metadata for Meta envelope */
243
+ meta?: {
244
+ toolName?: string;
245
+ toolVersion?: string;
246
+ };
247
+ };
248
+ /** Markdown specific options */
249
+ markdown?: MarkdownFormatterOptions;
250
+ /** Logger for warnings and info. Default: console */
251
+ logger?: Logger;
252
+ /** File writer function. Default: fs.promises.writeFile */
253
+ writeFile?: WriteFile;
254
+ }
255
+ /** Markdown formatter options (extended for feature parity) */
256
+ interface MarkdownFormatterOptions {
257
+ /** Report title. Default: "User Stories" */
258
+ title?: string;
259
+ /** Include status icons. Default: true */
260
+ includeStatusIcons?: boolean;
261
+ /** Include metadata table. Default: true */
262
+ includeMetadata?: boolean;
263
+ /** Include error details. Default: true */
264
+ includeErrors?: boolean;
265
+ /** Scenario heading level. Default: 3 */
266
+ scenarioHeadingLevel?: 2 | 3 | 4;
267
+ /** Step style. Default: "bullets" */
268
+ stepStyle?: "bullets" | "gherkin";
269
+ /** Group scenarios by. Default: "file" */
270
+ groupBy?: "file" | "suite" | "none";
271
+ /** Sort scenarios. Default: "source" */
272
+ sortScenarios?: "alpha" | "source" | "none";
273
+ /** Suite path separator. Default: " - " */
274
+ suiteSeparator?: string;
275
+ /** Include YAML front-matter for machine parsing. Default: false */
276
+ includeFrontMatter?: boolean;
277
+ /** Include summary table (counts, duration). Default: false */
278
+ includeSummaryTable?: boolean;
279
+ /** Base URL for source permalinks. E.g., "https://github.com/user/repo/blob" */
280
+ permalinkBaseUrl?: string;
281
+ /** URL template for ticket links. Use {ticket} as placeholder. E.g., "https://jira.example.com/browse/{ticket}" */
282
+ ticketUrlTemplate?: string;
283
+ /** Include source links when permalinkBaseUrl is set. Default: true */
284
+ includeSourceLinks?: boolean;
285
+ /** Custom renderers for doc entries */
286
+ customRenderers?: MarkdownRenderers;
287
+ }
288
+
289
+ /** Custom renderers for markdown doc entries */
290
+ interface MarkdownRenderers {
291
+ /** Custom renderer for scenario header */
292
+ renderScenarioHeader?: (tc: TestCaseResult) => string | null;
293
+ /** Custom renderer for step */
294
+ renderStep?: (step: StoryStep) => string | null;
295
+ /** Custom renderer for doc entry */
296
+ renderDocEntry?: (entry: DocEntry) => string | null;
297
+ /** Custom renderer for footer */
298
+ renderFooter?: (run: TestRunResult) => string | null;
299
+ }
300
+ /** Resolved formatter options with all defaults applied */
301
+ interface ResolvedFormatterOptions {
302
+ include: string[];
303
+ exclude: string[];
304
+ formats: OutputFormat[];
305
+ outputDir: string;
306
+ outputName: string;
307
+ output: {
308
+ mode: OutputMode;
309
+ colocatedStyle: ColocatedStyle;
310
+ rules: OutputRule[];
311
+ outputName?: string;
312
+ };
313
+ cucumberJson: {
314
+ pretty: boolean;
315
+ };
316
+ cucumberMessages: {
317
+ uriStrategy: "sourceFile" | "virtual";
318
+ includeSynthetics: boolean;
319
+ idSalt: string;
320
+ meta?: {
321
+ toolName?: string;
322
+ toolVersion?: string;
323
+ };
324
+ };
325
+ html: {
326
+ title: string;
327
+ darkMode: boolean;
328
+ searchable: boolean;
329
+ startCollapsed: boolean;
330
+ embedScreenshots: boolean;
331
+ syntaxHighlighting: boolean;
332
+ mermaidEnabled: boolean;
333
+ markdownEnabled: boolean;
334
+ };
335
+ junit: {
336
+ suiteName: string;
337
+ includeOutput: boolean;
338
+ };
339
+ markdown: {
340
+ title: string;
341
+ includeStatusIcons: boolean;
342
+ includeMetadata: boolean;
343
+ includeErrors: boolean;
344
+ scenarioHeadingLevel: 2 | 3 | 4;
345
+ stepStyle: "bullets" | "gherkin";
346
+ groupBy: "file" | "suite" | "none";
347
+ sortScenarios: "alpha" | "source" | "none";
348
+ suiteSeparator: string;
349
+ includeFrontMatter: boolean;
350
+ includeSummaryTable: boolean;
351
+ permalinkBaseUrl?: string;
352
+ ticketUrlTemplate?: string;
353
+ includeSourceLinks: boolean;
354
+ customRenderers?: MarkdownRenderers;
355
+ };
356
+ }
357
+
358
+ /**
359
+ * Cucumber JSON format types.
360
+ *
361
+ * Based on cucumber-js v11.x JSON formatter output.
362
+ * @see https://github.com/cucumber/cucumber-js/blob/main/src/formatter/json_formatter.ts
363
+ */
364
+ /** Cucumber JSON tag */
365
+ interface IJsonTag {
366
+ name: string;
367
+ line?: number;
368
+ }
369
+ /** Cucumber JSON doc string (step argument) */
370
+ interface IJsonDocString {
371
+ content: string;
372
+ content_type?: string;
373
+ line: number;
374
+ }
375
+ /** Cucumber JSON data table row */
376
+ interface IJsonTableRow {
377
+ cells: string[];
378
+ }
379
+ /** Cucumber JSON data table (step argument) */
380
+ interface IJsonDataTable {
381
+ rows: IJsonTableRow[];
382
+ }
383
+ /** Cucumber JSON step argument */
384
+ interface IJsonStepArgument {
385
+ doc_string?: IJsonDocString;
386
+ rows?: IJsonTableRow[];
387
+ }
388
+ /** Cucumber JSON embedding (attachment) */
389
+ interface IJsonEmbedding {
390
+ data: string;
391
+ mime_type: string;
392
+ name?: string;
393
+ }
394
+ /** Cucumber JSON step result */
395
+ interface IJsonStepResult {
396
+ /** Duration in nanoseconds */
397
+ duration?: number;
398
+ /** Error message if failed */
399
+ error_message?: string;
400
+ /** Status string */
401
+ status: "passed" | "failed" | "skipped" | "pending" | "undefined" | "ambiguous";
402
+ }
403
+ /** Cucumber JSON step */
404
+ interface IJsonStep {
405
+ /** Step arguments (doc strings, data tables) */
406
+ arguments?: IJsonStepArgument[];
407
+ /** Embeddings (attachments) */
408
+ embeddings?: IJsonEmbedding[];
409
+ /** Step keyword with trailing space (e.g., "Given ") */
410
+ keyword: string;
411
+ /** Hidden step (background step, hook) */
412
+ hidden?: boolean;
413
+ /** Line number in feature file */
414
+ line: number;
415
+ /** Match info (step definition location) */
416
+ match?: {
417
+ location?: string;
418
+ };
419
+ /** Step name/text */
420
+ name: string;
421
+ /** Step result */
422
+ result: IJsonStepResult;
423
+ }
424
+ /** Cucumber JSON scenario (element) */
425
+ interface IJsonScenario {
426
+ /** Scenario description */
427
+ description: string;
428
+ /** Scenario ID (slugified) */
429
+ id: string;
430
+ /** Scenario keyword */
431
+ keyword: string;
432
+ /** Line number in feature file */
433
+ line: number;
434
+ /** Scenario name */
435
+ name: string;
436
+ /** Scenario steps */
437
+ steps: IJsonStep[];
438
+ /** Scenario tags */
439
+ tags: IJsonTag[];
440
+ /** Element type (always "scenario" for us) */
441
+ type: "scenario" | "background";
442
+ }
443
+ /** Cucumber JSON feature */
444
+ interface IJsonFeature {
445
+ /** Feature description */
446
+ description: string;
447
+ /** Feature elements (scenarios) */
448
+ elements: IJsonScenario[];
449
+ /** Feature ID (slugified) */
450
+ id: string;
451
+ /** Feature keyword */
452
+ keyword: string;
453
+ /** Line number (typically 1) */
454
+ line: number;
455
+ /** Feature name */
456
+ name: string;
457
+ /** Feature tags */
458
+ tags: IJsonTag[];
459
+ /** Feature file URI/path */
460
+ uri: string;
461
+ }
462
+
463
+ /**
464
+ * Status mapping from raw framework statuses to canonical TestStatus.
465
+ */
466
+
467
+ /**
468
+ * Convert a raw status to canonical TestStatus.
469
+ *
470
+ * @param raw - The raw status from a framework
471
+ * @returns The canonical TestStatus
472
+ */
473
+ declare function normalizeStatus(raw: RawStatus): TestStatus;
474
+
475
+ /**
476
+ * ID generation and slug helpers for deterministic, Cucumber-compatible IDs.
477
+ */
478
+ /**
479
+ * Generate a deterministic test case ID from source file and scenario name.
480
+ *
481
+ * @param sourceFile - The source file path
482
+ * @param scenario - The scenario name
483
+ * @returns A 12-character hex ID
484
+ */
485
+ declare function generateTestCaseId(sourceFile: string, scenario: string): string;
486
+ /**
487
+ * Generate a deterministic run ID from timestamp and project root.
488
+ *
489
+ * @param startedAtMs - Run start timestamp
490
+ * @param projectRoot - Project root directory
491
+ * @returns A 16-character hex ID
492
+ */
493
+ declare function generateRunId(startedAtMs: number, projectRoot: string): string;
494
+ /**
495
+ * Slugify a string for Cucumber JSON IDs.
496
+ *
497
+ * Converts to lowercase, replaces path separators/spaces with hyphens,
498
+ * removes other special chars, and trims leading/trailing hyphens.
499
+ *
500
+ * @param text - The text to slugify
501
+ * @returns A URL-safe slug
502
+ */
503
+ declare function slugify(text: string): string;
504
+
505
+ /**
506
+ * Step fallback rules for deriving step results from scenario status.
507
+ *
508
+ * When frameworks don't provide step-level results, we derive them
509
+ * from the overall scenario status using these rules.
510
+ */
511
+
512
+ /**
513
+ * Derive step results from story steps and scenario status.
514
+ *
515
+ * Rules:
516
+ * - Passed: All steps are passed
517
+ * - Skipped/Pending: All steps are skipped/pending
518
+ * - Failed: Steps up to failure are passed, failing step is failed, rest are skipped
519
+ * (Heuristic: last step is the failure, or use error info if available)
520
+ *
521
+ * @param steps - Story steps with keywords and text
522
+ * @param scenarioStatus - Overall scenario status
523
+ * @param error - Optional error information to help identify failing step
524
+ * @returns Array of step results
525
+ */
526
+ declare function deriveStepResults(steps: StoryStep[], scenarioStatus: TestStatus, error?: {
527
+ message?: string;
528
+ stack?: string;
529
+ }): StepResult[];
530
+ /**
531
+ * Merge raw step events with derived step results.
532
+ *
533
+ * When we have partial step data from the framework, merge it with
534
+ * the derived results, preferring actual data over derived.
535
+ *
536
+ * @param derived - Derived step results from fallback rules
537
+ * @param events - Raw step events from framework (if any)
538
+ * @returns Merged step results
539
+ */
540
+ declare function mergeStepResults(derived: StepResult[], events?: Array<{
541
+ index?: number;
542
+ status?: string;
543
+ durationMs?: number;
544
+ errorMessage?: string;
545
+ }>): StepResult[];
546
+
547
+ /**
548
+ * Attachment resolution: embed vs link decision.
549
+ *
550
+ * Attachments can either be embedded inline (base64) or linked to
551
+ * external files based on size thresholds.
552
+ */
553
+
554
+ /** Options for attachment resolution */
555
+ interface AttachmentOptions {
556
+ /** Max bytes before attachment becomes external link. Default: 512KB */
557
+ maxEmbedBytes?: number;
558
+ /** Directory for external attachments */
559
+ externalDir?: string;
560
+ /** Project root for relative paths */
561
+ projectRoot?: string;
562
+ }
563
+ /**
564
+ * Resolve a raw attachment to a canonical attachment.
565
+ *
566
+ * Decision logic:
567
+ * 1. If body is already provided, use it (check size for encoding decision)
568
+ * 2. If path is provided, read file and decide embed vs link
569
+ * 3. For large files, return a URL reference instead of embedding
570
+ *
571
+ * @param raw - Raw attachment from framework
572
+ * @param options - Resolution options
573
+ * @returns Resolved canonical attachment
574
+ */
575
+ declare function resolveAttachment(raw: RawAttachment, options?: AttachmentOptions): Attachment;
576
+ /**
577
+ * Resolve multiple attachments.
578
+ *
579
+ * @param attachments - Raw attachments array
580
+ * @param options - Resolution options
581
+ * @returns Resolved canonical attachments
582
+ */
583
+ declare function resolveAttachments(attachments: RawAttachment[] | undefined, options?: AttachmentOptions): Attachment[];
584
+
585
+ /**
586
+ * Anti-Corruption Layer (ACL) - Layer 2.
587
+ *
588
+ * Transforms permissive RawRun data from framework adapters into
589
+ * strict canonical TestRunResult for formatters.
590
+ */
591
+
592
+ /**
593
+ * Canonicalize a raw run into a strict TestRunResult.
594
+ *
595
+ * This is the main entry point for the ACL. It:
596
+ * - Enforces required fields with defaults
597
+ * - Normalizes statuses to TestStatus enum
598
+ * - Applies step fallback rules
599
+ * - Resolves attachments (embed vs link)
600
+ * - Generates deterministic IDs
601
+ *
602
+ * @param raw - Raw run data from a framework adapter
603
+ * @param options - Canonicalization options
604
+ * @returns Strict canonical TestRunResult
605
+ */
606
+ declare function canonicalizeRun(raw: RawRun, options?: CanonicalizeOptions): TestRunResult;
607
+
608
+ /**
609
+ * Validation helpers for canonical TestRunResult.
610
+ *
611
+ * Used in tests to verify ACL output meets all invariants.
612
+ */
613
+
614
+ /** Validation result */
615
+ interface ValidationResult {
616
+ /** Whether the run is valid */
617
+ valid: boolean;
618
+ /** List of validation errors */
619
+ errors: string[];
620
+ }
621
+ /**
622
+ * Validate a canonical TestRunResult.
623
+ *
624
+ * Checks:
625
+ * - All required fields are present
626
+ * - stepResults length matches story.steps length
627
+ * - stepResults indexes are valid and unique
628
+ * - Durations are non-negative
629
+ * - Timestamps are valid
630
+ *
631
+ * @param run - The TestRunResult to validate
632
+ * @returns Validation result with errors if any
633
+ */
634
+ declare function validateCanonicalRun(run: TestRunResult): ValidationResult;
635
+ /**
636
+ * Assert that a run is valid, throwing if not.
637
+ *
638
+ * Useful in tests.
639
+ *
640
+ * @param run - The TestRunResult to validate
641
+ * @throws Error if validation fails
642
+ */
643
+ declare function assertValidRun(run: TestRunResult): void;
644
+
645
+ /**
646
+ * Cucumber JSON Formatter - Layer 3.
647
+ *
648
+ * Transforms canonical TestRunResult into Cucumber JSON format
649
+ * compatible with cucumber-js v11.x output.
650
+ */
651
+
652
+ /** Options for Cucumber JSON formatting */
653
+ interface CucumberJsonOptions {
654
+ /** Pretty-print JSON output. Default: false */
655
+ pretty?: boolean;
656
+ /** Include trailing space in keywords. Default: true */
657
+ keywordSpacing?: boolean;
658
+ }
659
+ /**
660
+ * Cucumber JSON Formatter.
661
+ *
662
+ * Transforms TestRunResult into an array of IJsonFeature objects
663
+ * that match the Cucumber JSON format specification.
664
+ */
665
+ declare class CucumberJsonFormatter {
666
+ private options;
667
+ constructor(options?: CucumberJsonOptions);
668
+ /**
669
+ * Format a test run into Cucumber JSON features.
670
+ *
671
+ * Groups test cases by source file (one feature per file).
672
+ *
673
+ * @param run - Canonical test run result
674
+ * @returns Array of Cucumber JSON features
675
+ */
676
+ format(run: TestRunResult): IJsonFeature[];
677
+ /**
678
+ * Format and serialize to JSON string.
679
+ *
680
+ * @param run - Canonical test run result
681
+ * @returns JSON string
682
+ */
683
+ formatToString(run: TestRunResult): string;
684
+ /**
685
+ * Build a single feature from test cases in the same file.
686
+ */
687
+ private buildFeature;
688
+ /**
689
+ * Extract feature name from URI or test cases.
690
+ *
691
+ * Uses the top-level suite path if available, otherwise file name.
692
+ */
693
+ private extractFeatureName;
694
+ /**
695
+ * Extract feature-level tags from all test cases.
696
+ */
697
+ private extractFeatureTags;
698
+ /**
699
+ * Build a scenario from a test case.
700
+ */
701
+ private buildScenario;
702
+ /**
703
+ * Build steps from story steps and step results.
704
+ */
705
+ private buildSteps;
706
+ /**
707
+ * Build a single step.
708
+ */
709
+ private buildStep;
710
+ /**
711
+ * Build embeddings from screenshot doc entries.
712
+ */
713
+ private buildScreenshotEmbeddings;
714
+ /**
715
+ * Build step result.
716
+ */
717
+ private buildStepResult;
718
+ /**
719
+ * Build embeddings (attachments) for a step.
720
+ *
721
+ * Cucumber convention: attach to the failing step, or last step if no failure.
722
+ */
723
+ private buildEmbeddings;
724
+ /**
725
+ * Build step arguments from step docs.
726
+ *
727
+ * Converts doc entries to Cucumber step arguments (doc strings, data tables).
728
+ */
729
+ private buildStepArguments;
730
+ /**
731
+ * Convert a doc entry to a Cucumber step argument.
732
+ */
733
+ private docEntryToArgument;
734
+ }
735
+
736
+ /**
737
+ * HTML Formatter - Layer 3.
738
+ *
739
+ * Transforms canonical TestRunResult into a standalone HTML report.
740
+ * Implemented via createHtmlFormatter (fn(args, deps) pattern).
741
+ */
742
+
743
+ /** Options for HTML formatting */
744
+ interface HtmlOptions {
745
+ /** Report title. Default: "Test Results" */
746
+ title?: string;
747
+ /** Include dark mode toggle. Default: true */
748
+ darkMode?: boolean;
749
+ /** Include search/filter functionality. Default: true */
750
+ searchable?: boolean;
751
+ /** Start scenarios collapsed. Default: false */
752
+ startCollapsed?: boolean;
753
+ /** Embed screenshots inline (base64). Default: true */
754
+ embedScreenshots?: boolean;
755
+ /** Enable syntax highlighting for code blocks (via highlight.js CDN). Default: true */
756
+ syntaxHighlighting?: boolean;
757
+ /** Enable live Mermaid diagram rendering (via Mermaid.js CDN). Default: true */
758
+ mermaidEnabled?: boolean;
759
+ /** Enable Markdown parsing for section doc entries (via marked.js CDN). Default: true */
760
+ markdownEnabled?: boolean;
761
+ }
762
+ /**
763
+ * HTML Formatter.
764
+ *
765
+ * Transforms TestRunResult into a standalone HTML report with:
766
+ * - Dark/light mode toggle
767
+ * - Search/filter functionality
768
+ * - Collapsible features and scenarios
769
+ * - Modern, accessible design
770
+ *
771
+ * Thin wrapper around createHtmlFormatter for backward compatibility.
772
+ */
773
+ declare class HtmlFormatter {
774
+ private formatFn;
775
+ constructor(options?: HtmlOptions);
776
+ /**
777
+ * Format a test run into standalone HTML.
778
+ *
779
+ * @param run - Canonical test run result
780
+ * @returns HTML string
781
+ */
782
+ format(run: TestRunResult): string;
783
+ }
784
+
785
+ /**
786
+ * JUnit XML Formatter - Layer 3.
787
+ *
788
+ * Transforms canonical TestRunResult into JUnit XML format
789
+ * for CI system integration.
790
+ */
791
+
792
+ /** Options for JUnit XML formatting */
793
+ interface JUnitOptions {
794
+ /** Test suite name. Default: "Test Suite" */
795
+ suiteName?: string;
796
+ /** Include system-out/system-err. Default: true */
797
+ includeOutput?: boolean;
798
+ /** Pretty-print XML output. Default: true */
799
+ pretty?: boolean;
800
+ }
801
+ /**
802
+ * JUnit XML Formatter.
803
+ *
804
+ * Transforms TestRunResult into JUnit XML format for CI integrations.
805
+ * Compatible with Jenkins, GitHub Actions, and other CI systems.
806
+ */
807
+ declare class JUnitFormatter {
808
+ private options;
809
+ constructor(options?: JUnitOptions);
810
+ /**
811
+ * Format a test run into JUnit XML.
812
+ *
813
+ * @param run - Canonical test run result
814
+ * @returns JUnit XML string
815
+ */
816
+ format(run: TestRunResult): string;
817
+ /**
818
+ * Build a testsuite element for a file.
819
+ */
820
+ private buildTestSuite;
821
+ /**
822
+ * Build a testcase element.
823
+ */
824
+ private buildTestCase;
825
+ /**
826
+ * Build system-out content with steps and docs.
827
+ */
828
+ private buildSystemOut;
829
+ /**
830
+ * Render a step with its docs.
831
+ */
832
+ private renderStep;
833
+ /**
834
+ * Render a doc entry as plain text.
835
+ */
836
+ private renderDocEntry;
837
+ }
838
+
839
+ /**
840
+ * Markdown Formatter - Layer 3.
841
+ *
842
+ * Transforms canonical TestRunResult into Markdown documentation.
843
+ * Compatible with existing markdown output from framework reporters.
844
+ */
845
+
846
+ /** Options for Markdown formatting */
847
+ interface MarkdownOptions {
848
+ /** Report title. Default: "User Stories" */
849
+ title?: string;
850
+ /** Include status icons on scenarios. Default: true */
851
+ includeStatusIcons?: boolean;
852
+ /** Include metadata table (date, version). Default: true */
853
+ includeMetadata?: boolean;
854
+ /** Include error details for failed scenarios. Default: true */
855
+ includeErrors?: boolean;
856
+ /** Scenario heading level. Default: 3 */
857
+ scenarioHeadingLevel?: 2 | 3 | 4;
858
+ /** Step rendering style. Default: "bullets" */
859
+ stepStyle?: "bullets" | "gherkin";
860
+ /** Group scenarios by. Default: "file" */
861
+ groupBy?: "file" | "suite" | "none";
862
+ /** Sort scenarios. Default: "source" */
863
+ sortScenarios?: "alpha" | "source" | "none";
864
+ /** Suite path separator. Default: " - " */
865
+ suiteSeparator?: string;
866
+ /** Include YAML front-matter for machine parsing. Default: false */
867
+ includeFrontMatter?: boolean;
868
+ /** Include summary table (counts, duration). Default: false */
869
+ includeSummaryTable?: boolean;
870
+ /** Base URL for source permalinks. E.g., "https://github.com/user/repo/blob" */
871
+ permalinkBaseUrl?: string;
872
+ /** URL template for ticket links. Use {ticket} as placeholder */
873
+ ticketUrlTemplate?: string;
874
+ /** Include source links when permalinkBaseUrl is set. Default: true */
875
+ includeSourceLinks?: boolean;
876
+ /** Custom renderers for doc entries */
877
+ customRenderers?: MarkdownRenderers;
878
+ }
879
+ /**
880
+ * Markdown Formatter.
881
+ *
882
+ * Transforms TestRunResult into Markdown documentation that matches
883
+ * the output format of existing framework reporters.
884
+ */
885
+ declare class MarkdownFormatter {
886
+ private options;
887
+ constructor(options?: MarkdownOptions);
888
+ /**
889
+ * Format a test run into Markdown.
890
+ *
891
+ * @param run - Canonical test run result
892
+ * @returns Markdown string
893
+ */
894
+ format(run: TestRunResult): string;
895
+ /**
896
+ * Render YAML front-matter.
897
+ */
898
+ private renderFrontMatter;
899
+ /**
900
+ * Render summary table.
901
+ */
902
+ private renderSummaryTable;
903
+ /**
904
+ * Format duration in human-readable form.
905
+ */
906
+ private formatDuration;
907
+ /**
908
+ * Render metadata table.
909
+ */
910
+ private renderMetadata;
911
+ /**
912
+ * Render scenarios grouped by file.
913
+ */
914
+ private renderByFile;
915
+ /**
916
+ * Render scenarios grouped by suite path.
917
+ */
918
+ private renderBySuite;
919
+ /**
920
+ * Render suite groups.
921
+ */
922
+ private renderSuiteGroups;
923
+ /**
924
+ * Render flat list of scenarios.
925
+ */
926
+ private renderFlatList;
927
+ /**
928
+ * Render a single scenario.
929
+ */
930
+ private renderScenario;
931
+ /**
932
+ * Render scenario body (docs, steps, errors).
933
+ */
934
+ private renderScenarioBody;
935
+ /**
936
+ * Build permalink URL for a test case.
937
+ */
938
+ private buildPermalink;
939
+ /**
940
+ * Render a step.
941
+ */
942
+ private renderStep;
943
+ /**
944
+ * Render a documentation entry.
945
+ */
946
+ private renderDocEntry;
947
+ /**
948
+ * Get status icon for a status.
949
+ */
950
+ private getStatusIcon;
951
+ /**
952
+ * Sort scenarios based on options.
953
+ */
954
+ private sortScenarios;
955
+ /**
956
+ * Sort suite groups.
957
+ */
958
+ private sortSuiteGroups;
959
+ }
960
+
961
+ /**
962
+ * Cucumber Messages types for NDJSON output.
963
+ *
964
+ * Minimal own types (no @cucumber/messages dependency) — consistent with
965
+ * the project's zero-external-deps-at-runtime approach.
966
+ *
967
+ * Based on the Cucumber Messages protocol:
968
+ * https://github.com/cucumber/messages
969
+ */
970
+ /** Protobuf-style timestamp { seconds, nanos } */
971
+ interface Timestamp {
972
+ seconds: number;
973
+ nanos: number;
974
+ }
975
+ /** Protobuf-style duration { seconds, nanos } */
976
+ interface Duration {
977
+ seconds: number;
978
+ nanos: number;
979
+ }
980
+ /** Location in a source file */
981
+ interface Location {
982
+ line: number;
983
+ column?: number;
984
+ }
985
+ interface Meta {
986
+ protocolVersion: string;
987
+ implementation: {
988
+ name: string;
989
+ version: string;
990
+ };
991
+ runtime: {
992
+ name: string;
993
+ version: string;
994
+ };
995
+ os: {
996
+ name: string;
997
+ };
998
+ cpu: {
999
+ name: string;
1000
+ };
1001
+ }
1002
+ interface Source {
1003
+ uri: string;
1004
+ data: string;
1005
+ mediaType: "text/x.cucumber.gherkin+plain";
1006
+ }
1007
+ interface Tag {
1008
+ location: Location;
1009
+ name: string;
1010
+ id: string;
1011
+ }
1012
+ type KeywordType = "Unknown" | "Context" | "Action" | "Outcome" | "Conjunction";
1013
+ interface DocString {
1014
+ location: Location;
1015
+ mediaType?: string;
1016
+ content: string;
1017
+ delimiter: string;
1018
+ }
1019
+ interface TableCell {
1020
+ location: Location;
1021
+ value: string;
1022
+ }
1023
+ interface TableRow {
1024
+ location: Location;
1025
+ cells: TableCell[];
1026
+ id: string;
1027
+ }
1028
+ interface DataTable {
1029
+ location: Location;
1030
+ rows: TableRow[];
1031
+ }
1032
+ interface Step {
1033
+ location: Location;
1034
+ keyword: string;
1035
+ keywordType: KeywordType;
1036
+ text: string;
1037
+ id: string;
1038
+ docString?: DocString;
1039
+ dataTable?: DataTable;
1040
+ }
1041
+ interface Scenario {
1042
+ location: Location;
1043
+ tags: Tag[];
1044
+ keyword: string;
1045
+ name: string;
1046
+ description: string;
1047
+ steps: Step[];
1048
+ id: string;
1049
+ }
1050
+ interface Background {
1051
+ location: Location;
1052
+ keyword: string;
1053
+ name: string;
1054
+ description: string;
1055
+ steps: Step[];
1056
+ id: string;
1057
+ }
1058
+ type FeatureChild = {
1059
+ background: Background;
1060
+ scenario?: undefined;
1061
+ } | {
1062
+ scenario: Scenario;
1063
+ background?: undefined;
1064
+ };
1065
+ interface Feature {
1066
+ location: Location;
1067
+ tags: Tag[];
1068
+ language: string;
1069
+ keyword: string;
1070
+ name: string;
1071
+ description: string;
1072
+ children: FeatureChild[];
1073
+ }
1074
+ interface GherkinDocument {
1075
+ uri: string;
1076
+ feature: Feature;
1077
+ }
1078
+ type PickleStepType = "Unknown" | "Context" | "Action" | "Outcome";
1079
+ interface PickleDocString {
1080
+ mediaType?: string;
1081
+ content: string;
1082
+ }
1083
+ interface PickleTableCell {
1084
+ value: string;
1085
+ }
1086
+ interface PickleTableRow {
1087
+ cells: PickleTableCell[];
1088
+ }
1089
+ interface PickleTable {
1090
+ rows: PickleTableRow[];
1091
+ }
1092
+ interface PickleStepArgument {
1093
+ docString?: PickleDocString;
1094
+ dataTable?: PickleTable;
1095
+ }
1096
+ interface PickleStep {
1097
+ astNodeIds: string[];
1098
+ id: string;
1099
+ type: PickleStepType;
1100
+ text: string;
1101
+ argument?: PickleStepArgument;
1102
+ }
1103
+ interface PickleTag {
1104
+ name: string;
1105
+ astNodeId: string;
1106
+ }
1107
+ interface Pickle {
1108
+ id: string;
1109
+ uri: string;
1110
+ name: string;
1111
+ language: string;
1112
+ steps: PickleStep[];
1113
+ tags: PickleTag[];
1114
+ astNodeIds: string[];
1115
+ }
1116
+ type TestStepResultStatus = "UNKNOWN" | "PASSED" | "SKIPPED" | "PENDING" | "UNDEFINED" | "AMBIGUOUS" | "FAILED";
1117
+ interface TestStepResult {
1118
+ duration: Duration;
1119
+ status: TestStepResultStatus;
1120
+ message?: string;
1121
+ }
1122
+ interface TestStep {
1123
+ id: string;
1124
+ pickleStepId?: string;
1125
+ stepDefinitionIds?: string[];
1126
+ }
1127
+ interface TestCase {
1128
+ id: string;
1129
+ pickleId: string;
1130
+ testSteps: TestStep[];
1131
+ }
1132
+ interface TestRunStarted {
1133
+ timestamp: Timestamp;
1134
+ }
1135
+ interface TestCaseStarted {
1136
+ id: string;
1137
+ testCaseId: string;
1138
+ timestamp: Timestamp;
1139
+ attempt: number;
1140
+ }
1141
+ interface TestStepStarted {
1142
+ testCaseStartedId: string;
1143
+ testStepId: string;
1144
+ timestamp: Timestamp;
1145
+ }
1146
+ interface TestStepFinished {
1147
+ testCaseStartedId: string;
1148
+ testStepId: string;
1149
+ testStepResult: TestStepResult;
1150
+ timestamp: Timestamp;
1151
+ }
1152
+ interface TestCaseFinished {
1153
+ testCaseStartedId: string;
1154
+ timestamp: Timestamp;
1155
+ willBeRetried: boolean;
1156
+ }
1157
+ interface TestRunFinished {
1158
+ timestamp: Timestamp;
1159
+ success: boolean;
1160
+ }
1161
+ type AttachmentContentEncoding = "IDENTITY" | "BASE64";
1162
+ interface CucumberAttachment {
1163
+ testCaseStartedId: string;
1164
+ testStepId?: string;
1165
+ body: string;
1166
+ mediaType: string;
1167
+ contentEncoding: AttachmentContentEncoding;
1168
+ }
1169
+ /**
1170
+ * Each NDJSON line is one Envelope with exactly one field set.
1171
+ */
1172
+ type Envelope = {
1173
+ meta: Meta;
1174
+ } | {
1175
+ source: Source;
1176
+ } | {
1177
+ gherkinDocument: GherkinDocument;
1178
+ } | {
1179
+ pickle: Pickle;
1180
+ } | {
1181
+ testRunStarted: TestRunStarted;
1182
+ } | {
1183
+ testCase: TestCase;
1184
+ } | {
1185
+ testCaseStarted: TestCaseStarted;
1186
+ } | {
1187
+ testStepStarted: TestStepStarted;
1188
+ } | {
1189
+ testStepFinished: TestStepFinished;
1190
+ } | {
1191
+ testCaseFinished: TestCaseFinished;
1192
+ } | {
1193
+ testRunFinished: TestRunFinished;
1194
+ } | {
1195
+ attachment: CucumberAttachment;
1196
+ };
1197
+
1198
+ /**
1199
+ * CucumberMessagesFormatter — produces NDJSON compatible with @cucumber/html-formatter.
1200
+ *
1201
+ * Message stream order:
1202
+ * Meta → Source* → GherkinDocument* → Pickle* → TestRunStarted →
1203
+ * [TestCase, TestCaseStarted, TestStep*, TestCaseFinished]* → TestRunFinished
1204
+ */
1205
+
1206
+ interface CucumberMessagesOptions {
1207
+ /** Strategy for deriving Source.uri. Default: "sourceFile" */
1208
+ uriStrategy?: "sourceFile" | "virtual";
1209
+ /** Whether to emit Source/GherkinDocument for synthesized features. Default: true */
1210
+ includeSynthetics?: boolean;
1211
+ /** Salt for deterministic IDs. Default: "" */
1212
+ idSalt?: string;
1213
+ /** Tool metadata for Meta envelope */
1214
+ meta?: {
1215
+ toolName?: string;
1216
+ toolVersion?: string;
1217
+ };
1218
+ }
1219
+ declare class CucumberMessagesFormatter {
1220
+ private options;
1221
+ constructor(options?: CucumberMessagesOptions);
1222
+ /**
1223
+ * Format a TestRunResult into an array of Envelope objects.
1224
+ */
1225
+ format(run: TestRunResult): Envelope[];
1226
+ /**
1227
+ * Format as NDJSON string (one JSON line per envelope).
1228
+ */
1229
+ formatToString(run: TestRunResult): string;
1230
+ /**
1231
+ * Build the Meta envelope.
1232
+ */
1233
+ private buildMetaEnvelope;
1234
+ /**
1235
+ * Group test cases by source file, preserving order.
1236
+ */
1237
+ private groupBySourceFile;
1238
+ }
1239
+
1240
+ /**
1241
+ * CucumberHtmlFormatter — produces the official Cucumber HTML report.
1242
+ *
1243
+ * Thin wrapper: reuses CucumberMessagesFormatter to generate NDJSON envelopes,
1244
+ * then pipes them through @cucumber/html-formatter's CucumberHtmlStream.
1245
+ */
1246
+
1247
+ interface CucumberHtmlOptions {
1248
+ /** Options forwarded to the underlying CucumberMessagesFormatter */
1249
+ messages?: CucumberMessagesOptions;
1250
+ }
1251
+ declare class CucumberHtmlFormatter {
1252
+ private messagesFormatter;
1253
+ constructor(options?: CucumberHtmlOptions);
1254
+ /**
1255
+ * Format a TestRunResult into official Cucumber HTML.
1256
+ *
1257
+ * Returns a Promise because CucumberHtmlStream is a Node.js Transform stream.
1258
+ */
1259
+ format(run: TestRunResult): Promise<string>;
1260
+ /**
1261
+ * Format a TestRunResult into official Cucumber HTML string.
1262
+ */
1263
+ formatToString(run: TestRunResult): Promise<string>;
1264
+ }
1265
+
1266
+ /**
1267
+ * NDJSON-to-TestRunResult parser.
1268
+ *
1269
+ * Parses Cucumber Messages NDJSON (one JSON envelope per line) back into
1270
+ * a TestRunResult suitable for rendering by HtmlFormatter or other formatters.
1271
+ *
1272
+ * This is the NDJSON compat path: it produces a minimal but sufficient
1273
+ * TestRunResult. Fields not present in the NDJSON stream are given
1274
+ * sensible defaults.
1275
+ */
1276
+
1277
+ /**
1278
+ * Parse an NDJSON string into a TestRunResult.
1279
+ *
1280
+ * @param ndjson - NDJSON string (one JSON envelope per line)
1281
+ * @returns TestRunResult reconstructed from the envelopes
1282
+ */
1283
+ declare function parseNdjson(ndjson: string): TestRunResult;
1284
+ /**
1285
+ * Parse an array of Envelope objects into a TestRunResult.
1286
+ *
1287
+ * @param envelopes - Array of Cucumber Messages envelopes
1288
+ * @returns TestRunResult reconstructed from the envelopes
1289
+ */
1290
+ declare function parseEnvelopes(envelopes: Envelope[]): TestRunResult;
1291
+
1292
+ /**
1293
+ * Git information utilities.
1294
+ *
1295
+ * Read git SHA and other info from the local repository.
1296
+ */
1297
+ /**
1298
+ * Read the current git SHA.
1299
+ *
1300
+ * Checks environment variables first (for CI), then reads from .git directory.
1301
+ *
1302
+ * @param cwd - Working directory to start search from
1303
+ * @returns Git SHA or undefined if not in a git repo
1304
+ */
1305
+ declare function readGitSha(cwd?: string): string | undefined;
1306
+ /**
1307
+ * Find the .git directory starting from a given directory.
1308
+ *
1309
+ * @param start - Directory to start search from
1310
+ * @returns Path to .git directory or undefined if not found
1311
+ */
1312
+ declare function findGitDir(start: string): string | undefined;
1313
+ /**
1314
+ * Read the current branch name.
1315
+ *
1316
+ * @param cwd - Working directory
1317
+ * @returns Branch name or undefined
1318
+ */
1319
+ declare function readBranchName(cwd?: string): string | undefined;
1320
+
1321
+ /**
1322
+ * Duration formatting utilities.
1323
+ */
1324
+ /**
1325
+ * Format milliseconds as human-readable duration.
1326
+ *
1327
+ * @param ms - Duration in milliseconds
1328
+ * @returns Formatted string (e.g., "123 ms", "1.5 s", "2m 30s")
1329
+ */
1330
+ declare function formatDuration(ms: number): string;
1331
+ /**
1332
+ * Convert milliseconds to nanoseconds.
1333
+ *
1334
+ * Used for Cucumber JSON format which expects nanoseconds.
1335
+ *
1336
+ * @param ms - Duration in milliseconds
1337
+ * @returns Duration in nanoseconds
1338
+ */
1339
+ declare function msToNanoseconds(ms: number): number;
1340
+ /**
1341
+ * Convert nanoseconds to milliseconds.
1342
+ *
1343
+ * @param ns - Duration in nanoseconds
1344
+ * @returns Duration in milliseconds
1345
+ */
1346
+ declare function nanosecondsToMs(ns: number): number;
1347
+
1348
+ /**
1349
+ * Metadata utilities for reading package information.
1350
+ */
1351
+ /**
1352
+ * Read the package version from the closest package.json.
1353
+ *
1354
+ * Results are cached by root directory.
1355
+ *
1356
+ * @param root - Directory to start searching from
1357
+ * @returns Package version string or undefined if not found
1358
+ */
1359
+ declare function readPackageVersion(root: string): string | undefined;
1360
+ /**
1361
+ * Clear the package version cache.
1362
+ * Useful for testing.
1363
+ */
1364
+ declare function clearVersionCache(): void;
1365
+
1366
+ /**
1367
+ * CI environment auto-detection utility.
1368
+ *
1369
+ * Detects known CI providers from environment variables.
1370
+ * Precedence: GitHub Actions > CircleCI > Jenkins > Travis > GitLab CI > generic CI
1371
+ */
1372
+
1373
+ /**
1374
+ * Detect CI environment from process.env.
1375
+ * Returns undefined when not running in CI.
1376
+ */
1377
+ declare function detectCI(env?: Record<string, string | undefined>): RawCIInfo | undefined;
1378
+
1379
+ /**
1380
+ * @executable-stories/formatters
1381
+ *
1382
+ * Cucumber-compatible report formats (JSON, HTML, JUnit, Markdown)
1383
+ * for Jest, Vitest, and Playwright test results.
1384
+ *
1385
+ * Architecture:
1386
+ * - Layer 1: Framework Adapters (adaptJestRun, adaptVitestRun, adaptPlaywrightRun)
1387
+ * - Layer 2: Anti-Corruption Layer (canonicalizeRun)
1388
+ * - Layer 3: Formatters (CucumberJsonFormatter, HtmlFormatter, JUnitFormatter, MarkdownFormatter)
1389
+ */
1390
+
1391
+ /** Arguments for generate function */
1392
+ interface GenerateArgs {
1393
+ /** Canonical test run result */
1394
+ run: TestRunResult;
1395
+ /** Optional options override */
1396
+ options?: FormatterOptions;
1397
+ }
1398
+ /** Dependencies for generate function (injectable for testing) */
1399
+ interface GenerateDeps {
1400
+ /** Logger for warnings */
1401
+ logger: Logger;
1402
+ /** File writer function */
1403
+ writeFile: WriteFile;
1404
+ }
1405
+ /** Result of generate function: Map of format to array of file paths */
1406
+ type GenerateResult = Map<OutputFormat, string[]>;
1407
+ /**
1408
+ * High-level report generator that combines multiple formatters.
1409
+ *
1410
+ * Accepts ONLY canonical TestRunResult - use adapters + canonicalizeRun first.
1411
+ *
1412
+ * Supports output routing:
1413
+ * - Aggregated: All test cases in a single file
1414
+ * - Colocated mirrored: Files mirrored under outputDir preserving directory structure
1415
+ * - Colocated adjacent: Files written next to source files
1416
+ * - Rule-based: Different routing based on source file patterns
1417
+ */
1418
+ declare class ReportGenerator {
1419
+ private options;
1420
+ private deps;
1421
+ constructor(options?: FormatterOptions, deps?: Partial<GenerateDeps>);
1422
+ /**
1423
+ * Resolve options with defaults.
1424
+ */
1425
+ private resolveOptions;
1426
+ /**
1427
+ * Generate reports for a test run.
1428
+ *
1429
+ * @param run - Canonical TestRunResult (use canonicalizeRun to create from RawRun)
1430
+ * @returns Map of output format to generated file paths
1431
+ */
1432
+ generate(run: TestRunResult): Promise<GenerateResult>;
1433
+ /**
1434
+ * Generate reports for a single format.
1435
+ */
1436
+ private generateFormat;
1437
+ /**
1438
+ * Format content for a specific format.
1439
+ */
1440
+ private formatContent;
1441
+ }
1442
+ /**
1443
+ * Factory function to create a ReportGenerator with dependency injection.
1444
+ *
1445
+ * Useful for testing and custom configurations.
1446
+ */
1447
+ declare function createReportGenerator(options?: FormatterOptions, deps?: Partial<GenerateDeps>): ReportGenerator;
1448
+
1449
+ /**
1450
+ * Normalize Jest results to canonical TestRunResult.
1451
+ *
1452
+ * Combines adaptJestRun + canonicalizeRun.
1453
+ */
1454
+ declare function normalizeJestResults(jestResults: Parameters<typeof adaptJestRun>[0], storyReports: Parameters<typeof adaptJestRun>[1], adapterOptions?: Parameters<typeof adaptJestRun>[2], canonicalizeOptions?: CanonicalizeOptions): TestRunResult;
1455
+ /**
1456
+ * Normalize Vitest results to canonical TestRunResult.
1457
+ *
1458
+ * Combines adaptVitestRun + canonicalizeRun.
1459
+ */
1460
+ declare function normalizeVitestResults(testModules: Parameters<typeof adaptVitestRun>[0], adapterOptions?: Parameters<typeof adaptVitestRun>[1], canonicalizeOptions?: CanonicalizeOptions): TestRunResult;
1461
+ /**
1462
+ * Normalize Playwright results to canonical TestRunResult.
1463
+ *
1464
+ * Combines adaptPlaywrightRun + canonicalizeRun.
1465
+ */
1466
+ declare function normalizePlaywrightResults(testResults: Parameters<typeof adaptPlaywrightRun>[0], adapterOptions?: Parameters<typeof adaptPlaywrightRun>[1], canonicalizeOptions?: CanonicalizeOptions): TestRunResult;
1467
+
1468
+ export { type Attachment, type CIInfo, type CanonicalizeOptions, type ColocatedStyle, type CoverageSummary, CucumberHtmlFormatter, type CucumberHtmlOptions, CucumberJsonFormatter, type CucumberJsonOptions, CucumberMessagesFormatter, type CucumberMessagesOptions, DocEntry, type FormatterOptions, type GenerateArgs, type GenerateDeps, type GenerateResult, HtmlFormatter, type HtmlOptions, type IJsonDataTable, type IJsonDocString, type IJsonEmbedding, type IJsonFeature, type IJsonScenario, type IJsonStep, type IJsonStepArgument, type IJsonStepResult, type IJsonTableRow, type IJsonTag, JUnitFormatter, type JUnitOptions, type Logger, MarkdownFormatter, type MarkdownFormatterOptions, type MarkdownOptions, type MarkdownRenderers, type OutputConfig, type OutputFormat, type OutputMode, type OutputRule, RawAttachment, RawCIInfo, RawRun, RawStatus, ReportGenerator, type ResolvedFormatterOptions, type StepResult, StoryMeta, StoryStep, type TestCaseAttempt, type TestCaseResult, type TestRunResult, type TestStatus, type ValidationResult, type WriteFile, adaptJestRun, adaptPlaywrightRun, adaptVitestRun, assertValidRun, canonicalizeRun, clearVersionCache, createReportGenerator, deriveStepResults, detectCI, findGitDir, formatDuration, generateRunId, generateTestCaseId, mergeStepResults, msToNanoseconds, nanosecondsToMs, normalizeJestResults, normalizePlaywrightResults, normalizeStatus, normalizeVitestResults, parseEnvelopes, parseNdjson, readBranchName, readGitSha, readPackageVersion, resolveAttachment, resolveAttachments, slugify, validateCanonicalRun };