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.
- package/README.md +205 -0
- package/dist/adapters.cjs +324 -0
- package/dist/adapters.cjs.map +1 -0
- package/dist/adapters.d.cts +1 -0
- package/dist/adapters.d.ts +1 -0
- package/dist/adapters.js +295 -0
- package/dist/adapters.js.map +1 -0
- package/dist/cli.js +5399 -0
- package/dist/cli.js.map +1 -0
- package/dist/index-DCJ0NvAp.d.cts +378 -0
- package/dist/index-DCJ0NvAp.d.ts +378 -0
- package/dist/index.cjs +5519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1468 -0
- package/dist/index.d.ts +1468 -0
- package/dist/index.js +5448 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
- package/schemas/README.md +663 -0
- package/schemas/examples/dotnet.json +108 -0
- package/schemas/examples/full.json +254 -0
- package/schemas/examples/go.json +108 -0
- package/schemas/examples/junit5.json +108 -0
- package/schemas/examples/minimal.json +18 -0
- package/schemas/examples/pytest.json +108 -0
- package/schemas/examples/rust.json +108 -0
- package/schemas/raw-run.schema.json +437 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/converters/acl/status.ts","../src/converters/acl/ids.ts","../src/converters/acl/steps.ts","../src/converters/acl/attachments.ts","../src/converters/acl/index.ts","../src/converters/acl/lines.ts","../src/formatters/cucumber-json.ts","../src/formatters/html/template.ts","../src/formatters/html/styles.ts","../src/formatters/html/renderers/status.ts","../src/formatters/html/renderers/meta.ts","../src/formatters/html/renderers/summary.ts","../src/formatters/html/renderers/error-box.ts","../src/formatters/html/renderers/attachments.ts","../src/formatters/html/renderers/doc-entries.ts","../src/formatters/html/renderers/steps.ts","../src/formatters/html/renderers/scenario.ts","../src/formatters/html/renderers/feature.ts","../src/formatters/html/renderers/body.ts","../src/formatters/html/renderers/index.ts","../src/formatters/html/index.ts","../src/formatters/junit-xml.ts","../src/formatters/markdown.ts","../src/formatters/cucumber-messages/synthesize-feature.ts","../src/utils/cucumber-messages.ts","../src/formatters/cucumber-messages/build-gherkin-document.ts","../src/formatters/cucumber-messages/build-pickles.ts","../src/formatters/cucumber-messages/build-execution.ts","../src/formatters/cucumber-messages/index.ts","../src/formatters/cucumber-html.ts","../src/converters/adapters/jest.ts","../src/converters/adapters/vitest.ts","../src/converters/adapters/playwright.ts","../src/types/story.ts","../src/converters/acl/validate.ts","../src/converters/ndjson-parser.ts","../src/utils/git-info.ts","../src/utils/duration.ts","../src/utils/metadata.ts","../src/utils/ci-detect.ts"],"sourcesContent":["/**\n * @executable-stories/formatters\n *\n * Cucumber-compatible report formats (JSON, HTML, JUnit, Markdown)\n * for Jest, Vitest, and Playwright test results.\n *\n * Architecture:\n * - Layer 1: Framework Adapters (adaptJestRun, adaptVitestRun, adaptPlaywrightRun)\n * - Layer 2: Anti-Corruption Layer (canonicalizeRun)\n * - Layer 3: Formatters (CucumberJsonFormatter, HtmlFormatter, JUnitFormatter, MarkdownFormatter)\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport * as fsPromises from \"node:fs/promises\";\nimport type { TestRunResult, TestCaseResult } from \"./types/test-result\";\nimport type {\n FormatterOptions,\n ResolvedFormatterOptions,\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n Logger,\n WriteFile,\n CanonicalizeOptions,\n} from \"./types/options\";\nimport type { RawRun } from \"./types/raw\";\n\nimport { canonicalizeRun } from \"./converters/acl/index\";\nimport { CucumberJsonFormatter } from \"./formatters/cucumber-json\";\nimport { HtmlFormatter } from \"./formatters/html/index\";\nimport { JUnitFormatter } from \"./formatters/junit-xml\";\nimport { MarkdownFormatter } from \"./formatters/markdown\";\nimport { CucumberMessagesFormatter } from \"./formatters/cucumber-messages/index\";\nimport { CucumberHtmlFormatter } from \"./formatters/cucumber-html\";\n\n// Import adapters for convenience functions\nimport { adaptJestRun } from \"./converters/adapters/jest\";\nimport { adaptVitestRun } from \"./converters/adapters/vitest\";\nimport { adaptPlaywrightRun } from \"./converters/adapters/playwright\";\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\n// Story types (shared vocabulary for all adapters)\nexport type {\n StepKeyword,\n StepMode,\n DocPhase,\n DocEntry,\n StoryStep,\n StoryMeta,\n} from \"./types/story\";\nexport { STORY_META_KEY } from \"./types/story\";\n\n// Canonical types (Layer 2 output - what formatters accept)\nexport type {\n TestStatus,\n StepResult,\n Attachment,\n TestCaseResult,\n TestCaseAttempt,\n CIInfo,\n CoverageSummary,\n TestRunResult,\n} from \"./types/test-result\";\n\n// Raw types (Layer 1 - for adapter authors)\nexport type {\n RawStatus,\n RawAttachment,\n RawStepEvent,\n RawTestCase,\n RawCIInfo,\n RawRun,\n} from \"./types/raw\";\n\n// Cucumber JSON types (Layer 3 output)\nexport type {\n IJsonTag,\n IJsonDocString,\n IJsonTableRow,\n IJsonDataTable,\n IJsonStepArgument,\n IJsonEmbedding,\n IJsonStepResult,\n IJsonStep,\n IJsonScenario,\n IJsonFeature,\n} from \"./types/cucumber-json\";\n\n// Options types\nexport type {\n CanonicalizeOptions,\n OutputFormat,\n OutputMode,\n ColocatedStyle,\n OutputRule,\n OutputConfig,\n Logger,\n WriteFile,\n MarkdownFormatterOptions,\n MarkdownRenderers,\n FormatterOptions,\n ResolvedFormatterOptions,\n} from \"./types/options\";\n\n// ============================================================================\n// ACL Exports\n// ============================================================================\n\nexport { canonicalizeRun } from \"./converters/acl/index\";\n\n/** @internal */\nexport { normalizeStatus } from \"./converters/acl/index\";\n/** @internal */\nexport { generateTestCaseId } from \"./converters/acl/index\";\n/** @internal */\nexport { generateRunId } from \"./converters/acl/index\";\n/** @internal */\nexport { slugify } from \"./converters/acl/index\";\n/** @internal */\nexport { deriveStepResults } from \"./converters/acl/index\";\n/** @internal */\nexport { mergeStepResults } from \"./converters/acl/index\";\n/** @internal */\nexport { resolveAttachment } from \"./converters/acl/index\";\n/** @internal */\nexport { resolveAttachments } from \"./converters/acl/index\";\n\nexport {\n validateCanonicalRun,\n assertValidRun,\n type ValidationResult,\n} from \"./converters/acl/validate\";\n\n// ============================================================================\n// Formatter Exports\n// ============================================================================\n\nexport {\n CucumberJsonFormatter,\n type CucumberJsonOptions,\n} from \"./formatters/cucumber-json\";\n\nexport {\n HtmlFormatter,\n type HtmlOptions,\n} from \"./formatters/html/index\";\n\nexport {\n JUnitFormatter,\n type JUnitOptions,\n} from \"./formatters/junit-xml\";\n\nexport {\n MarkdownFormatter,\n type MarkdownOptions,\n} from \"./formatters/markdown\";\n\nexport {\n CucumberMessagesFormatter,\n type CucumberMessagesOptions,\n} from \"./formatters/cucumber-messages/index\";\n\nexport {\n CucumberHtmlFormatter,\n type CucumberHtmlOptions,\n} from \"./formatters/cucumber-html\";\n\n// ============================================================================\n// NDJSON Parser (compat path: NDJSON → TestRunResult)\n// ============================================================================\n\nexport { parseNdjson, parseEnvelopes } from \"./converters/ndjson-parser\";\n\n// ============================================================================\n// Utility Exports\n// ============================================================================\n\n/** @internal */\nexport { readGitSha } from \"./utils/git-info\";\n/** @internal */\nexport { findGitDir } from \"./utils/git-info\";\n/** @internal */\nexport { readBranchName } from \"./utils/git-info\";\n/** @internal */\nexport { formatDuration } from \"./utils/duration\";\n/** @internal */\nexport { msToNanoseconds } from \"./utils/duration\";\n/** @internal */\nexport { nanosecondsToMs } from \"./utils/duration\";\n/** @internal */\nexport { readPackageVersion } from \"./utils/metadata\";\n/** @internal */\nexport { clearVersionCache } from \"./utils/metadata\";\nexport { detectCI } from \"./utils/ci-detect\";\n\n// ============================================================================\n// ReportGenerator Types (fn(args, deps) pattern)\n// ============================================================================\n\n/** Arguments for generate function */\nexport interface GenerateArgs {\n /** Canonical test run result */\n run: TestRunResult;\n /** Optional options override */\n options?: FormatterOptions;\n}\n\n/** Dependencies for generate function (injectable for testing) */\nexport interface GenerateDeps {\n /** Logger for warnings */\n logger: Logger;\n /** File writer function */\n writeFile: WriteFile;\n}\n\n/** Result of generate function: Map of format to array of file paths */\nexport type GenerateResult = Map<OutputFormat, string[]>;\n\n/** Extension map for output formats */\nconst FORMAT_EXTENSIONS: Record<OutputFormat, string> = {\n markdown: \".md\",\n html: \".html\",\n \"cucumber-html\": \".cucumber.html\",\n junit: \".junit.xml\",\n \"cucumber-json\": \".cucumber.json\",\n \"cucumber-messages\": \".ndjson\",\n};\n\n/** Known test file extensions to strip for colocated naming */\nconst TEST_EXTENSIONS = [\n \".test.ts\", \".test.tsx\", \".spec.ts\", \".spec.tsx\",\n \".test.js\", \".spec.js\", \".story.test.ts\", \".story.spec.ts\",\n];\n\n// ============================================================================\n// Pure Functions for Output Routing\n// ============================================================================\n\n/**\n * Check if a pattern matches a source file using simple glob matching.\n * Supports: **, *, and literal matching.\n */\nfunction matchesPattern(pattern: string, sourceFile: string): boolean {\n // Normalize both to forward slashes\n const normalizedPattern = pattern.replace(/\\\\/g, \"/\");\n const normalizedFile = sourceFile.replace(/\\\\/g, \"/\");\n\n // Convert glob pattern to regex\n const regexStr = normalizedPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\") // Escape special regex chars except * and ?\n .replace(/\\*\\*/g, \"{{GLOBSTAR}}\") // Protect **\n .replace(/\\*/g, \"[^/]*\") // * matches anything except /\n .replace(/{{GLOBSTAR}}/g, \".*\"); // ** matches anything including /\n\n const regex = new RegExp(`^${regexStr}$`);\n return regex.test(normalizedFile);\n}\n\n/**\n * Find the first matching rule for a source file.\n */\nfunction findMatchingRule(\n sourceFile: string,\n rules: OutputRule[]\n): OutputRule | undefined {\n for (const rule of rules) {\n if (matchesPattern(rule.match, sourceFile)) {\n return rule;\n }\n }\n return undefined;\n}\n\n/**\n * Filter test cases by include/exclude glob patterns on sourceFile.\n * Uses same glob semantics as output rules (** and *).\n */\nfunction filterTestCasesByGlobs(\n testCases: TestCaseResult[],\n include: string[],\n exclude: string[],\n logger: Logger\n): TestCaseResult[] {\n if (include.length === 0 && exclude.length === 0) return testCases;\n\n const filtered: TestCaseResult[] = [];\n for (const tc of testCases) {\n const sourceFile = tc.sourceFile.replace(/\\\\/g, \"/\");\n\n if (include.length > 0) {\n const included = include.some((p) => matchesPattern(p, sourceFile));\n if (!included) continue;\n }\n if (exclude.length > 0) {\n const excluded = exclude.some((p) => matchesPattern(p, sourceFile));\n if (excluded) continue;\n }\n filtered.push(tc);\n }\n\n const dropped = testCases.length - filtered.length;\n if (dropped > 0) {\n logger.warn(\n `Filtered ${dropped} test case(s) by include/exclude globs (${filtered.length} included)`\n );\n }\n return filtered;\n}\n\n/**\n * Normalize path to posix format (forward slashes).\n */\nfunction toPosix(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Compute output path for a test case based on mode and settings.\n */\nfunction computeOutputPath(\n sourceFile: string,\n format: OutputFormat,\n mode: OutputMode,\n colocatedStyle: ColocatedStyle,\n baseOutputDir: string,\n outputName: string\n): string {\n const ext = FORMAT_EXTENSIONS[format];\n\n if (mode === \"aggregated\") {\n // Aggregated: single file in outputDir\n return toPosix(path.join(baseOutputDir, `${outputName}${ext}`));\n }\n\n // Colocated mode - normalize source file to posix first\n const normalizedSource = toPosix(sourceFile);\n const dirOfSource = path.posix.dirname(normalizedSource);\n let baseName = path.posix.basename(normalizedSource);\n\n // Strip test extension\n for (const testExt of TEST_EXTENSIONS) {\n if (baseName.endsWith(testExt)) {\n baseName = baseName.slice(0, -testExt.length);\n break;\n }\n }\n\n const fileName = `${baseName}.${outputName}${ext}`;\n\n if (colocatedStyle === \"adjacent\") {\n // Adjacent: write next to source file (ignores outputDir)\n return toPosix(path.posix.join(dirOfSource, fileName));\n }\n\n // Mirrored: preserve directory structure under outputDir\n return toPosix(path.posix.join(baseOutputDir, dirOfSource, fileName));\n}\n\n/**\n * Group test cases by their computed output path.\n */\nfunction groupTestCasesByOutput(\n testCases: TestCaseResult[],\n format: OutputFormat,\n options: ResolvedFormatterOptions,\n logger: Logger\n): Map<string, TestCaseResult[]> {\n const groups = new Map<string, TestCaseResult[]>();\n const rules = options.output.rules;\n const defaultMode = options.output.mode;\n const defaultColocatedStyle = options.output.colocatedStyle;\n const defaultFormats = options.formats;\n const defaultOutputDir = options.outputDir;\n const defaultOutputName = options.outputName;\n\n for (const tc of testCases) {\n const sourceFile = tc.sourceFile;\n\n // Check if colocated mode but missing sourceFile\n if (defaultMode === \"colocated\" && sourceFile === \"unknown\") {\n logger.warn(\n `Test case \"${tc.story.scenario}\" missing sourceFile, falling back to aggregated`\n );\n }\n\n // Find matching rule\n const rule = findMatchingRule(sourceFile, rules);\n\n // Determine effective settings (first match wins, fall back to defaults)\n const mode = rule?.mode ?? defaultMode;\n const colocatedStyle = rule?.colocatedStyle ?? defaultColocatedStyle;\n const formats = rule?.formats ?? defaultFormats;\n const outputDir = rule?.outputDir ?? defaultOutputDir;\n const outputName = rule?.outputName ?? options.output.outputName ?? defaultOutputName;\n\n // Warn if rule sets both adjacent style and outputDir\n if (\n rule &&\n rule.colocatedStyle === \"adjacent\" &&\n rule.outputDir !== undefined\n ) {\n logger.warn(\n `Rule for \"${rule.match}\" sets both colocatedStyle: \"adjacent\" and outputDir. outputDir will be ignored for adjacent mode.`\n );\n }\n\n // Skip if format not in effective formats\n if (!formats.includes(format)) {\n continue;\n }\n\n // Handle missing sourceFile in colocated mode\n const effectiveMode =\n mode === \"colocated\" && sourceFile === \"unknown\" ? \"aggregated\" : mode;\n\n const outputPath = computeOutputPath(\n sourceFile,\n format,\n effectiveMode,\n colocatedStyle,\n outputDir,\n outputName\n );\n\n const existing = groups.get(outputPath);\n if (existing) {\n existing.push(tc);\n } else {\n groups.set(outputPath, [tc]);\n }\n }\n\n return groups;\n}\n\n// ============================================================================\n// ReportGenerator\n// ============================================================================\n\n/**\n * High-level report generator that combines multiple formatters.\n *\n * Accepts ONLY canonical TestRunResult - use adapters + canonicalizeRun first.\n *\n * Supports output routing:\n * - Aggregated: All test cases in a single file\n * - Colocated mirrored: Files mirrored under outputDir preserving directory structure\n * - Colocated adjacent: Files written next to source files\n * - Rule-based: Different routing based on source file patterns\n */\nexport class ReportGenerator {\n private options: ResolvedFormatterOptions;\n private deps: GenerateDeps;\n\n constructor(options: FormatterOptions = {}, deps?: Partial<GenerateDeps>) {\n this.options = this.resolveOptions(options);\n this.deps = {\n logger: deps?.logger ?? console,\n writeFile: deps?.writeFile ?? ((p, c) => fsPromises.writeFile(p, c, \"utf8\")),\n };\n }\n\n /**\n * Resolve options with defaults.\n */\n private resolveOptions(options: FormatterOptions): ResolvedFormatterOptions {\n return {\n include: options.include ?? [],\n exclude: options.exclude ?? [],\n formats: options.formats ?? [\"cucumber-json\"],\n outputDir: options.outputDir ?? \"reports\",\n outputName: options.outputName ?? \"test-results\",\n output: {\n mode: options.output?.mode ?? \"aggregated\",\n colocatedStyle: options.output?.colocatedStyle ?? \"mirrored\",\n rules: options.output?.rules ?? [],\n outputName: options.output?.outputName,\n },\n cucumberJson: {\n pretty: options.cucumberJson?.pretty ?? false,\n },\n cucumberMessages: {\n uriStrategy: options.cucumberMessages?.uriStrategy ?? \"sourceFile\",\n includeSynthetics: options.cucumberMessages?.includeSynthetics ?? true,\n idSalt: options.cucumberMessages?.idSalt ?? \"\",\n meta: options.cucumberMessages?.meta,\n },\n html: {\n title: options.html?.title ?? \"Test Results\",\n darkMode: options.html?.darkMode ?? true,\n searchable: options.html?.searchable ?? true,\n startCollapsed: options.html?.startCollapsed ?? false,\n embedScreenshots: options.html?.embedScreenshots ?? true,\n syntaxHighlighting: options.html?.syntaxHighlighting ?? true,\n mermaidEnabled: options.html?.mermaidEnabled ?? true,\n markdownEnabled: options.html?.markdownEnabled ?? true,\n },\n junit: {\n suiteName: options.junit?.suiteName ?? \"Test Suite\",\n includeOutput: options.junit?.includeOutput ?? true,\n },\n markdown: {\n title: options.markdown?.title ?? \"User Stories\",\n includeStatusIcons: options.markdown?.includeStatusIcons ?? true,\n includeMetadata: options.markdown?.includeMetadata ?? true,\n includeErrors: options.markdown?.includeErrors ?? true,\n scenarioHeadingLevel: options.markdown?.scenarioHeadingLevel ?? 3,\n stepStyle: options.markdown?.stepStyle ?? \"bullets\",\n groupBy: options.markdown?.groupBy ?? \"file\",\n sortScenarios: options.markdown?.sortScenarios ?? \"source\",\n suiteSeparator: options.markdown?.suiteSeparator ?? \" - \",\n includeFrontMatter: options.markdown?.includeFrontMatter ?? false,\n includeSummaryTable: options.markdown?.includeSummaryTable ?? false,\n permalinkBaseUrl: options.markdown?.permalinkBaseUrl,\n ticketUrlTemplate: options.markdown?.ticketUrlTemplate,\n includeSourceLinks: options.markdown?.includeSourceLinks ?? true,\n customRenderers: options.markdown?.customRenderers,\n },\n };\n }\n\n /**\n * Generate reports for a test run.\n *\n * @param run - Canonical TestRunResult (use canonicalizeRun to create from RawRun)\n * @returns Map of output format to generated file paths\n */\n async generate(run: TestRunResult): Promise<GenerateResult> {\n const testCases = filterTestCasesByGlobs(\n run.testCases,\n this.options.include,\n this.options.exclude,\n this.deps.logger\n );\n const filteredRun: TestRunResult = { ...run, testCases };\n\n const results: GenerateResult = new Map();\n\n for (const format of this.options.formats) {\n const paths = await this.generateFormat(filteredRun, format);\n results.set(format, paths);\n }\n\n return results;\n }\n\n /**\n * Generate reports for a single format.\n */\n private async generateFormat(\n run: TestRunResult,\n format: OutputFormat\n ): Promise<string[]> {\n // Group test cases by output path\n const groups = groupTestCasesByOutput(\n run.testCases,\n format,\n this.options,\n this.deps.logger\n );\n\n // Handle empty runs in aggregated mode - write a single empty file\n if (groups.size === 0 && this.options.output.mode === \"aggregated\") {\n const ext = FORMAT_EXTENSIONS[format];\n const outputPath = toPosix(path.join(this.options.outputDir, `${this.options.outputName}${ext}`));\n const content = await this.formatContent(run, format);\n const dir = path.dirname(outputPath);\n await fsPromises.mkdir(dir, { recursive: true });\n await this.deps.writeFile(outputPath, content);\n return [outputPath];\n }\n\n const writtenPaths: string[] = [];\n\n for (const [outputPath, testCases] of groups) {\n // Create a run with just these test cases\n const groupRun: TestRunResult = {\n ...run,\n testCases,\n };\n\n // Format content\n const content = await this.formatContent(groupRun, format);\n\n // Ensure directory exists\n const dir = path.dirname(outputPath);\n await fsPromises.mkdir(dir, { recursive: true });\n\n // Write file\n await this.deps.writeFile(outputPath, content);\n writtenPaths.push(outputPath);\n }\n\n return writtenPaths;\n }\n\n /**\n * Format content for a specific format.\n */\n private formatContent(run: TestRunResult, format: OutputFormat): string | Promise<string> {\n switch (format) {\n case \"cucumber-json\": {\n const formatter = new CucumberJsonFormatter({\n pretty: this.options.cucumberJson.pretty,\n });\n return formatter.formatToString(run);\n }\n\n case \"html\": {\n const formatter = new HtmlFormatter({\n title: this.options.html.title,\n darkMode: this.options.html.darkMode,\n searchable: this.options.html.searchable,\n startCollapsed: this.options.html.startCollapsed,\n embedScreenshots: this.options.html.embedScreenshots,\n syntaxHighlighting: this.options.html.syntaxHighlighting,\n mermaidEnabled: this.options.html.mermaidEnabled,\n markdownEnabled: this.options.html.markdownEnabled,\n });\n return formatter.format(run);\n }\n\n case \"cucumber-html\": {\n const formatter = new CucumberHtmlFormatter({\n messages: {\n uriStrategy: this.options.cucumberMessages.uriStrategy,\n includeSynthetics: this.options.cucumberMessages.includeSynthetics,\n idSalt: this.options.cucumberMessages.idSalt,\n meta: this.options.cucumberMessages.meta,\n },\n });\n return formatter.formatToString(run);\n }\n\n case \"junit\": {\n const formatter = new JUnitFormatter({\n suiteName: this.options.junit.suiteName,\n includeOutput: this.options.junit.includeOutput,\n });\n return formatter.format(run);\n }\n\n case \"cucumber-messages\": {\n const formatter = new CucumberMessagesFormatter({\n uriStrategy: this.options.cucumberMessages.uriStrategy,\n includeSynthetics: this.options.cucumberMessages.includeSynthetics,\n idSalt: this.options.cucumberMessages.idSalt,\n meta: this.options.cucumberMessages.meta,\n });\n return formatter.formatToString(run);\n }\n\n case \"markdown\": {\n const formatter = new MarkdownFormatter({\n title: this.options.markdown.title,\n includeStatusIcons: this.options.markdown.includeStatusIcons,\n includeMetadata: this.options.markdown.includeMetadata,\n includeErrors: this.options.markdown.includeErrors,\n scenarioHeadingLevel: this.options.markdown.scenarioHeadingLevel,\n stepStyle: this.options.markdown.stepStyle,\n groupBy: this.options.markdown.groupBy,\n sortScenarios: this.options.markdown.sortScenarios,\n suiteSeparator: this.options.markdown.suiteSeparator,\n includeFrontMatter: this.options.markdown.includeFrontMatter,\n includeSummaryTable: this.options.markdown.includeSummaryTable,\n permalinkBaseUrl: this.options.markdown.permalinkBaseUrl,\n ticketUrlTemplate: this.options.markdown.ticketUrlTemplate,\n includeSourceLinks: this.options.markdown.includeSourceLinks,\n customRenderers: this.options.markdown.customRenderers,\n });\n return formatter.format(run);\n }\n\n default:\n throw new Error(`Unknown format: ${format}`);\n }\n }\n}\n\n/**\n * Factory function to create a ReportGenerator with dependency injection.\n *\n * Useful for testing and custom configurations.\n */\nexport function createReportGenerator(\n options?: FormatterOptions,\n deps?: Partial<GenerateDeps>\n): ReportGenerator {\n return new ReportGenerator(options, deps);\n}\n\n// ============================================================================\n// Convenience Functions\n// ============================================================================\n\n// Re-export adapters\nexport { adaptJestRun, adaptVitestRun, adaptPlaywrightRun };\n\n// Re-export adapter types\nexport type {\n JestTestResult,\n JestFileResult,\n JestAggregatedResult,\n StoryFileReport,\n JestAdapterOptions,\n VitestState,\n VitestSerializedError,\n VitestTestResult,\n VitestTestCase,\n VitestTestModule,\n VitestAdapterOptions,\n PlaywrightStatus,\n PlaywrightError,\n PlaywrightAttachment,\n PlaywrightTestResult,\n PlaywrightAnnotation,\n PlaywrightLocation,\n PlaywrightTestCase,\n PlaywrightAdapterOptions,\n} from \"./converters/adapters/index\";\n\n/**\n * Normalize Jest results to canonical TestRunResult.\n *\n * Combines adaptJestRun + canonicalizeRun.\n */\nexport function normalizeJestResults(\n jestResults: Parameters<typeof adaptJestRun>[0],\n storyReports: Parameters<typeof adaptJestRun>[1],\n adapterOptions?: Parameters<typeof adaptJestRun>[2],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptJestRun(jestResults, storyReports, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n\n/**\n * Normalize Vitest results to canonical TestRunResult.\n *\n * Combines adaptVitestRun + canonicalizeRun.\n */\nexport function normalizeVitestResults(\n testModules: Parameters<typeof adaptVitestRun>[0],\n adapterOptions?: Parameters<typeof adaptVitestRun>[1],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptVitestRun(testModules, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n\n/**\n * Normalize Playwright results to canonical TestRunResult.\n *\n * Combines adaptPlaywrightRun + canonicalizeRun.\n */\nexport function normalizePlaywrightResults(\n testResults: Parameters<typeof adaptPlaywrightRun>[0],\n adapterOptions?: Parameters<typeof adaptPlaywrightRun>[1],\n canonicalizeOptions?: CanonicalizeOptions\n): TestRunResult {\n const raw: RawRun = adaptPlaywrightRun(testResults, adapterOptions);\n return canonicalizeRun(raw, canonicalizeOptions);\n}\n","/**\n * Status mapping from raw framework statuses to canonical TestStatus.\n */\n\nimport type { RawStatus } from \"../../types/raw\";\nimport type { TestStatus } from \"../../types/test-result\";\n\n/** Map raw status to canonical TestStatus */\nconst STATUS_MAP: Record<RawStatus, TestStatus> = {\n pass: \"passed\",\n fail: \"failed\",\n skip: \"skipped\",\n pending: \"pending\",\n todo: \"pending\", // Map todo → pending\n timeout: \"failed\", // Map timeout → failed\n interrupted: \"failed\", // Map interrupted → failed\n unknown: \"skipped\", // Safest default\n};\n\n/**\n * Convert a raw status to canonical TestStatus.\n *\n * @param raw - The raw status from a framework\n * @returns The canonical TestStatus\n */\nexport function normalizeStatus(raw: RawStatus): TestStatus {\n return STATUS_MAP[raw] ?? \"skipped\";\n}\n\n/**\n * Convert a string to RawStatus, with fallback to \"unknown\".\n *\n * @param status - Any status string\n * @returns A valid RawStatus\n */\nexport function parseRawStatus(status: string | undefined): RawStatus {\n if (!status) return \"unknown\";\n const lower = status.toLowerCase();\n\n // Direct matches\n if (lower === \"pass\" || lower === \"passed\") return \"pass\";\n if (lower === \"fail\" || lower === \"failed\") return \"fail\";\n if (lower === \"skip\" || lower === \"skipped\") return \"skip\";\n if (lower === \"pending\") return \"pending\";\n if (lower === \"todo\") return \"todo\";\n if (lower === \"timeout\" || lower === \"timedout\" || lower === \"timed_out\") return \"timeout\";\n if (lower === \"interrupted\") return \"interrupted\";\n\n return \"unknown\";\n}\n","/**\n * ID generation and slug helpers for deterministic, Cucumber-compatible IDs.\n */\n\nimport { createHash } from \"node:crypto\";\n\n/**\n * Generate a deterministic test case ID from source file and scenario name.\n *\n * @param sourceFile - The source file path\n * @param scenario - The scenario name\n * @returns A 12-character hex ID\n */\nexport function generateTestCaseId(sourceFile: string, scenario: string): string {\n const input = `${sourceFile}::${scenario}`;\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 12);\n}\n\n/**\n * Generate a deterministic run ID from timestamp and project root.\n *\n * @param startedAtMs - Run start timestamp\n * @param projectRoot - Project root directory\n * @returns A 16-character hex ID\n */\nexport function generateRunId(startedAtMs: number, projectRoot: string): string {\n const input = `${startedAtMs}::${projectRoot}`;\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 16);\n}\n\n/**\n * Slugify a string for Cucumber JSON IDs.\n *\n * Converts to lowercase, replaces path separators/spaces with hyphens,\n * removes other special chars, and trims leading/trailing hyphens.\n *\n * @param text - The text to slugify\n * @returns A URL-safe slug\n */\nexport function slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[/\\\\]+/g, \"-\") // Convert path separators to hyphens\n .replace(/[^\\w\\s-]/g, \"\") // Remove other special characters\n .replace(/[\\s_]+/g, \"-\") // Replace spaces and underscores with hyphens\n .replace(/-+/g, \"-\") // Remove consecutive hyphens\n .replace(/^-+|-+$/g, \"\"); // Trim leading/trailing hyphens\n}\n\n/**\n * Generate a Cucumber-compatible feature ID from file path.\n *\n * Uses the full path (without extension) to ensure uniqueness for files\n * with the same basename in different directories.\n *\n * @param uri - The feature file URI/path\n * @returns A slugified feature ID\n */\nexport function generateFeatureId(uri: string): string {\n // Use full path without extension for uniqueness\n const pathWithoutExt = uri.replace(/\\.[^.]+$/, \"\");\n return slugify(pathWithoutExt);\n}\n\n/**\n * Generate a Cucumber-compatible scenario ID.\n *\n * Format: feature-id;scenario-name\n *\n * @param featureId - The feature ID\n * @param scenarioName - The scenario name\n * @returns A Cucumber-compatible scenario ID\n */\nexport function generateScenarioId(featureId: string, scenarioName: string): string {\n return `${featureId};${slugify(scenarioName)}`;\n}\n","/**\n * Step fallback rules for deriving step results from scenario status.\n *\n * When frameworks don't provide step-level results, we derive them\n * from the overall scenario status using these rules.\n */\n\nimport type { StoryStep } from \"../../types/story\";\nimport type { TestStatus, StepResult } from \"../../types/test-result\";\n\n/**\n * Derive step results from story steps and scenario status.\n *\n * Rules:\n * - Passed: All steps are passed\n * - Skipped/Pending: All steps are skipped/pending\n * - Failed: Steps up to failure are passed, failing step is failed, rest are skipped\n * (Heuristic: last step is the failure, or use error info if available)\n *\n * @param steps - Story steps with keywords and text\n * @param scenarioStatus - Overall scenario status\n * @param error - Optional error information to help identify failing step\n * @returns Array of step results\n */\nexport function deriveStepResults(\n steps: StoryStep[],\n scenarioStatus: TestStatus,\n error?: { message?: string; stack?: string }\n): StepResult[] {\n if (steps.length === 0) {\n return [];\n }\n\n // Passed: all steps passed\n if (scenarioStatus === \"passed\") {\n return steps.map((_, index) => ({\n index,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n }\n\n // Skipped or Pending: all steps have same status\n if (scenarioStatus === \"skipped\" || scenarioStatus === \"pending\") {\n return steps.map((_, index) => ({\n index,\n status: scenarioStatus,\n durationMs: 0,\n }));\n }\n\n // Failed: identify failing step and mark accordingly\n const failingIndex = findFailingStepIndex(steps, error);\n\n return steps.map((_, index) => {\n if (index < failingIndex) {\n // Steps before failure are passed\n return { index, status: \"passed\" as TestStatus, durationMs: 0 };\n } else if (index === failingIndex) {\n // Failing step\n return {\n index,\n status: \"failed\" as TestStatus,\n durationMs: 0,\n errorMessage: error?.message,\n };\n } else {\n // Steps after failure are skipped\n return { index, status: \"skipped\" as TestStatus, durationMs: 0 };\n }\n });\n}\n\n/**\n * Attempt to identify which step failed based on error information.\n *\n * Strategies:\n * 1. Look for step text in error message/stack\n * 2. Default to last step if no match found\n *\n * @param steps - Story steps\n * @param error - Error information\n * @returns Index of the failing step (0-based)\n */\nfunction findFailingStepIndex(\n steps: StoryStep[],\n error?: { message?: string; stack?: string }\n): number {\n if (!error || steps.length === 0) {\n // Default: last step failed\n return steps.length - 1;\n }\n\n const errorText = `${error.message ?? \"\"} ${error.stack ?? \"\"}`.toLowerCase();\n\n // Try to find a step mentioned in the error\n for (let i = 0; i < steps.length; i++) {\n const stepText = steps[i].text.toLowerCase();\n if (errorText.includes(stepText)) {\n return i;\n }\n }\n\n // Default: last step failed\n return steps.length - 1;\n}\n\n/**\n * Merge raw step events with derived step results.\n *\n * When we have partial step data from the framework, merge it with\n * the derived results, preferring actual data over derived.\n *\n * @param derived - Derived step results from fallback rules\n * @param events - Raw step events from framework (if any)\n * @returns Merged step results\n */\nexport function mergeStepResults(\n derived: StepResult[],\n events?: Array<{\n index?: number;\n status?: string;\n durationMs?: number;\n errorMessage?: string;\n }>\n): StepResult[] {\n if (!events || events.length === 0) {\n return derived;\n }\n\n // Create a map of actual results by index\n const actualByIndex = new Map<number, (typeof events)[0]>();\n for (const event of events) {\n if (event.index !== undefined) {\n actualByIndex.set(event.index, event);\n }\n }\n\n return derived.map((step) => {\n const actual = actualByIndex.get(step.index);\n if (!actual) {\n return step;\n }\n\n return {\n index: step.index,\n status: normalizeStepStatus(actual.status) ?? step.status,\n durationMs: actual.durationMs ?? step.durationMs,\n errorMessage: actual.errorMessage ?? step.errorMessage,\n };\n });\n}\n\n/**\n * Normalize step status string to TestStatus.\n */\nfunction normalizeStepStatus(status?: string): TestStatus | undefined {\n if (!status) return undefined;\n\n const lower = status.toLowerCase();\n if (lower === \"pass\" || lower === \"passed\") return \"passed\";\n if (lower === \"fail\" || lower === \"failed\") return \"failed\";\n if (lower === \"skip\" || lower === \"skipped\") return \"skipped\";\n if (lower === \"pending\") return \"pending\";\n\n return undefined;\n}\n","/**\n * Attachment resolution: embed vs link decision.\n *\n * Attachments can either be embedded inline (base64) or linked to\n * external files based on size thresholds.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { RawAttachment } from \"../../types/raw\";\nimport type { Attachment } from \"../../types/test-result\";\n\n/** Default max embed size: 512KB */\nconst DEFAULT_MAX_EMBED_BYTES = 512 * 1024;\n\n/** Options for attachment resolution */\nexport interface AttachmentOptions {\n /** Max bytes before attachment becomes external link. Default: 512KB */\n maxEmbedBytes?: number;\n /** Directory for external attachments */\n externalDir?: string;\n /** Project root for relative paths */\n projectRoot?: string;\n}\n\n/**\n * Resolve a raw attachment to a canonical attachment.\n *\n * Decision logic:\n * 1. If body is already provided, use it (check size for encoding decision)\n * 2. If path is provided, read file and decide embed vs link\n * 3. For large files, return a URL reference instead of embedding\n *\n * @param raw - Raw attachment from framework\n * @param options - Resolution options\n * @returns Resolved canonical attachment\n */\nexport function resolveAttachment(\n raw: RawAttachment,\n options: AttachmentOptions = {}\n): Attachment {\n const maxBytes = options.maxEmbedBytes ?? DEFAULT_MAX_EMBED_BYTES;\n\n // If we already have a body, use it\n if (raw.body) {\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: raw.body,\n contentEncoding: raw.encoding ?? \"BASE64\",\n };\n }\n\n // If we have a path, read and potentially embed\n if (raw.path) {\n return resolveFromPath(raw, maxBytes, options);\n }\n\n // No body or path - return empty attachment\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: \"\",\n contentEncoding: \"IDENTITY\",\n };\n}\n\n/**\n * Resolve attachment from file path.\n */\nfunction resolveFromPath(\n raw: RawAttachment,\n maxBytes: number,\n options: AttachmentOptions\n): Attachment {\n const filePath = raw.path!;\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(options.projectRoot ?? process.cwd(), filePath);\n\n // Check if file exists\n if (!fs.existsSync(absolutePath)) {\n // File doesn't exist - return path as URL reference\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: filePath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Get file size\n const stats = fs.statSync(absolutePath);\n const byteLength = raw.byteLength ?? stats.size;\n\n // If too large, return as URL reference\n if (byteLength > maxBytes) {\n // Copy to external dir if specified\n if (options.externalDir) {\n const destPath = copyToExternalDir(absolutePath, options.externalDir);\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: destPath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Return relative path as URL\n const relativePath = options.projectRoot\n ? path.relative(options.projectRoot, absolutePath)\n : filePath;\n\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: relativePath,\n contentEncoding: \"IDENTITY\",\n };\n }\n\n // Embed as base64\n const content = fs.readFileSync(absolutePath);\n return {\n name: raw.name,\n mediaType: raw.mediaType,\n body: content.toString(\"base64\"),\n contentEncoding: \"BASE64\",\n };\n}\n\n/**\n * Copy file to external directory and return the destination path.\n */\nfunction copyToExternalDir(sourcePath: string, externalDir: string): string {\n // Ensure external dir exists\n if (!fs.existsSync(externalDir)) {\n fs.mkdirSync(externalDir, { recursive: true });\n }\n\n const filename = path.basename(sourcePath);\n const destPath = path.join(externalDir, filename);\n\n // Handle filename conflicts\n let finalPath = destPath;\n let counter = 1;\n while (fs.existsSync(finalPath)) {\n const ext = path.extname(filename);\n const base = path.basename(filename, ext);\n finalPath = path.join(externalDir, `${base}-${counter}${ext}`);\n counter++;\n }\n\n fs.copyFileSync(sourcePath, finalPath);\n return finalPath;\n}\n\n/**\n * Resolve multiple attachments.\n *\n * @param attachments - Raw attachments array\n * @param options - Resolution options\n * @returns Resolved canonical attachments\n */\nexport function resolveAttachments(\n attachments: RawAttachment[] | undefined,\n options: AttachmentOptions = {}\n): Attachment[] {\n if (!attachments || attachments.length === 0) {\n return [];\n }\n\n return attachments.map((raw) => resolveAttachment(raw, options));\n}\n","/**\n * Anti-Corruption Layer (ACL) - Layer 2.\n *\n * Transforms permissive RawRun data from framework adapters into\n * strict canonical TestRunResult for formatters.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase } from \"../../types/raw\";\nimport type {\n TestRunResult,\n TestCaseResult,\n TestStatus,\n} from \"../../types/test-result\";\nimport type { CanonicalizeOptions } from \"../../types/options\";\nimport { normalizeStatus } from \"./status\";\nimport { generateTestCaseId, generateRunId } from \"./ids\";\nimport { deriveStepResults, mergeStepResults } from \"./steps\";\nimport { resolveAttachments } from \"./attachments\";\n\n/**\n * Canonicalize a raw run into a strict TestRunResult.\n *\n * This is the main entry point for the ACL. It:\n * - Enforces required fields with defaults\n * - Normalizes statuses to TestStatus enum\n * - Applies step fallback rules\n * - Resolves attachments (embed vs link)\n * - Generates deterministic IDs\n *\n * @param raw - Raw run data from a framework adapter\n * @param options - Canonicalization options\n * @returns Strict canonical TestRunResult\n */\nexport function canonicalizeRun(\n raw: RawRun,\n options: CanonicalizeOptions = {}\n): TestRunResult {\n const now = Date.now();\n const startedAtMs = raw.startedAtMs ?? options.defaults?.startedAtMs ?? now;\n const finishedAtMs = raw.finishedAtMs ?? options.defaults?.finishedAtMs ?? now;\n\n const runId = generateRunId(startedAtMs, raw.projectRoot);\n\n const testCases = raw.testCases\n .filter((tc) => tc.story != null)\n .map((tc) => canonicalizeTestCase(tc, options, raw.projectRoot));\n\n return {\n testCases,\n startedAtMs,\n finishedAtMs,\n durationMs: finishedAtMs - startedAtMs,\n projectRoot: raw.projectRoot,\n runId,\n packageVersion: raw.packageVersion,\n gitSha: raw.gitSha,\n ci: raw.ci,\n };\n}\n\n/**\n * Canonicalize a single test case.\n */\nfunction canonicalizeTestCase(\n raw: RawTestCase,\n options: CanonicalizeOptions,\n projectRoot: string\n): TestCaseResult {\n const story = raw.story!;\n const sourceFile = raw.sourceFile ?? \"unknown\";\n const scenario = story.scenario ?? raw.title ?? \"Unknown Scenario\";\n\n // Generate deterministic ID\n const id = generateTestCaseId(sourceFile, scenario);\n\n // Normalize status\n const status = normalizeStatus(raw.status);\n\n // Derive step results\n const derivedSteps = deriveStepResults(story.steps ?? [], status, raw.error);\n const stepResults = mergeStepResults(\n derivedSteps,\n raw.stepEvents?.map((e) => ({\n index: e.index,\n status: e.status,\n durationMs: e.durationMs,\n errorMessage: e.errorMessage,\n }))\n );\n\n // Resolve attachments\n const attachments = resolveAttachments(raw.attachments, {\n maxEmbedBytes: options.attachments?.maxEmbedBytes,\n externalDir: options.attachments?.externalDir,\n projectRoot,\n });\n\n // Normalize tags\n const tags = normalizeTags(story);\n\n // Build title path\n const titlePath = buildTitlePath(raw, story);\n\n return {\n id,\n story,\n sourceFile,\n sourceLine: raw.sourceLine ?? 1,\n status,\n durationMs: raw.durationMs ?? 0,\n errorMessage: raw.error?.message,\n errorStack: raw.error?.stack,\n attachments,\n stepResults,\n titlePath,\n projectName: raw.projectName,\n retry: raw.retry ?? 0,\n retries: raw.retries ?? 0,\n tags,\n };\n}\n\n/**\n * Normalize tags from story metadata.\n *\n * - Ensures array format\n * - Deduplicates\n * - Sorts alphabetically\n */\nfunction normalizeTags(story: StoryMeta): string[] {\n const tags = story.tags ?? [];\n return [...new Set(tags)].sort();\n}\n\n/**\n * Build title path from raw test case and story metadata.\n *\n * Prefers story.suitePath if available, falls back to raw.titlePath.\n */\nfunction buildTitlePath(raw: RawTestCase, story: StoryMeta): string[] {\n if (story.suitePath && story.suitePath.length > 0) {\n return story.suitePath;\n }\n\n if (raw.titlePath && raw.titlePath.length > 0) {\n // Exclude the last element (test name) if titlePath includes it\n const withoutTestName = raw.titlePath.slice(0, -1);\n return withoutTestName.length > 0 ? withoutTestName : [];\n }\n\n return [];\n}\n\n// Re-export helpers for advanced usage\nexport { normalizeStatus } from \"./status\";\nexport { generateTestCaseId, generateRunId, slugify } from \"./ids\";\nexport { deriveStepResults, mergeStepResults } from \"./steps\";\nexport { resolveAttachment, resolveAttachments } from \"./attachments\";\n","/**\n * Deterministic line number generation for Cucumber JSON compatibility.\n *\n * When actual line numbers aren't available, we generate deterministic\n * line numbers based on position in the file.\n */\n\n/**\n * Generate deterministic line numbers for scenarios in a file.\n *\n * Starts at line 2 (after feature declaration) and increments by\n * steps count + buffer for each scenario.\n *\n * @param scenarioIndex - Index of the scenario in the file (0-based)\n * @param stepsCount - Number of steps in the scenario\n * @param prevEndLine - End line of previous scenario (default 1)\n * @returns Deterministic line number\n */\nexport function generateScenarioLine(\n scenarioIndex: number,\n stepsCount: number,\n prevEndLine: number = 1\n): number {\n // Each scenario needs:\n // - 1 line for scenario declaration\n // - n lines for steps\n // - 1 blank line after\n const linesPerScenario = 1 + stepsCount + 1;\n\n if (scenarioIndex === 0) {\n // First scenario starts at line 3 (Feature on 1, blank on 2)\n return 3;\n }\n\n return prevEndLine + linesPerScenario;\n}\n\n/**\n * Generate step line numbers for a scenario.\n *\n * @param scenarioLine - The scenario's line number\n * @param stepIndex - Index of the step (0-based)\n * @returns Line number for the step\n */\nexport function generateStepLine(scenarioLine: number, stepIndex: number): number {\n // Steps start on the line after the scenario declaration\n return scenarioLine + 1 + stepIndex;\n}\n\n/**\n * Context for tracking line numbers across multiple scenarios.\n */\nexport interface LineContext {\n /** Current line number */\n currentLine: number;\n}\n\n/**\n * Create a new line context starting at line 1.\n */\nexport function createLineContext(): LineContext {\n return { currentLine: 1 };\n}\n\n/**\n * Advance line context past a feature declaration.\n *\n * @param ctx - Line context\n * @returns Updated line context\n */\nexport function advancePastFeature(ctx: LineContext): LineContext {\n // Feature keyword on line 1, blank line after\n return { currentLine: 3 };\n}\n\n/**\n * Get the current scenario line and advance past it.\n *\n * @param ctx - Line context\n * @param stepsCount - Number of steps in the scenario\n * @returns Tuple of [scenarioLine, updatedContext]\n */\nexport function advancePastScenario(\n ctx: LineContext,\n stepsCount: number\n): [number, LineContext] {\n const scenarioLine = ctx.currentLine;\n // Advance past: scenario keyword + steps + blank line\n const newLine = scenarioLine + 1 + stepsCount + 1;\n return [scenarioLine, { currentLine: newLine }];\n}\n","/**\n * Cucumber JSON Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into Cucumber JSON format\n * compatible with cucumber-js v11.x output.\n */\n\nimport type { StoryStep, DocEntry } from \"../types/story\";\nimport type {\n TestRunResult,\n TestCaseResult,\n StepResult,\n Attachment,\n} from \"../types/test-result\";\nimport type {\n IJsonFeature,\n IJsonScenario,\n IJsonStep,\n IJsonTag,\n IJsonEmbedding,\n IJsonStepResult,\n} from \"../types/cucumber-json\";\nimport { slugify, generateFeatureId, generateScenarioId } from \"../converters/acl/ids\";\nimport { createLineContext, advancePastFeature, advancePastScenario, generateStepLine } from \"../converters/acl/lines\";\n\n/** Options for Cucumber JSON formatting */\nexport interface CucumberJsonOptions {\n /** Pretty-print JSON output. Default: false */\n pretty?: boolean;\n /** Include trailing space in keywords. Default: true */\n keywordSpacing?: boolean;\n}\n\n/**\n * Cucumber JSON Formatter.\n *\n * Transforms TestRunResult into an array of IJsonFeature objects\n * that match the Cucumber JSON format specification.\n */\nexport class CucumberJsonFormatter {\n private options: Required<CucumberJsonOptions>;\n\n constructor(options: CucumberJsonOptions = {}) {\n this.options = {\n pretty: options.pretty ?? false,\n keywordSpacing: options.keywordSpacing ?? true,\n };\n }\n\n /**\n * Format a test run into Cucumber JSON features.\n *\n * Groups test cases by source file (one feature per file).\n *\n * @param run - Canonical test run result\n * @returns Array of Cucumber JSON features\n */\n format(run: TestRunResult): IJsonFeature[] {\n // Group test cases by source file\n const byFile = new Map<string, TestCaseResult[]>();\n for (const tc of run.testCases) {\n const file = tc.sourceFile;\n const existing = byFile.get(file);\n if (existing) {\n existing.push(tc);\n } else {\n byFile.set(file, [tc]);\n }\n }\n\n // Build features\n const features: IJsonFeature[] = [];\n for (const [uri, testCases] of byFile) {\n features.push(this.buildFeature(uri, testCases));\n }\n\n return features;\n }\n\n /**\n * Format and serialize to JSON string.\n *\n * @param run - Canonical test run result\n * @returns JSON string\n */\n formatToString(run: TestRunResult): string {\n const features = this.format(run);\n return this.options.pretty\n ? JSON.stringify(features, null, 2)\n : JSON.stringify(features);\n }\n\n /**\n * Build a single feature from test cases in the same file.\n */\n private buildFeature(uri: string, testCases: TestCaseResult[]): IJsonFeature {\n const featureName = this.extractFeatureName(uri, testCases);\n const featureId = generateFeatureId(uri);\n\n // Extract feature-level tags (union of all scenario tags)\n const featureTags = this.extractFeatureTags(testCases);\n\n // Build scenarios with deterministic line numbers\n let lineCtx = createLineContext();\n lineCtx = advancePastFeature(lineCtx);\n\n const elements: IJsonScenario[] = [];\n for (const tc of testCases) {\n const [scenarioLine, nextCtx] = advancePastScenario(lineCtx, tc.story.steps.length);\n elements.push(this.buildScenario(tc, featureId, scenarioLine));\n lineCtx = nextCtx;\n }\n\n return {\n description: \"\",\n elements,\n id: featureId,\n keyword: \"Feature\",\n line: 1,\n name: featureName,\n tags: featureTags,\n uri,\n };\n }\n\n /**\n * Extract feature name from URI or test cases.\n *\n * Uses the top-level suite path if available, otherwise file name.\n */\n private extractFeatureName(uri: string, testCases: TestCaseResult[]): string {\n // Try to get common suite path prefix\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n\n if (suitePaths.length > 0) {\n // Use the first element of the first suite path as feature name\n const firstPath = suitePaths[0];\n if (firstPath.length > 0) {\n return firstPath[0];\n }\n }\n\n // Fall back to filename without extension\n const filename = uri.split(\"/\").pop() ?? uri;\n return filename.replace(/\\.[^.]+$/, \"\").replace(/[._-]/g, \" \");\n }\n\n /**\n * Extract feature-level tags from all test cases.\n */\n private extractFeatureTags(testCases: TestCaseResult[]): IJsonTag[] {\n // Collect all unique tags\n const allTags = new Set<string>();\n for (const tc of testCases) {\n for (const tag of tc.tags) {\n allTags.add(tag);\n }\n }\n\n // Convert to IJsonTag format (with @ prefix if not present)\n return [...allTags].sort().map((tag) => ({\n name: tag.startsWith(\"@\") ? tag : `@${tag}`,\n line: 1,\n }));\n }\n\n /**\n * Build a scenario from a test case.\n */\n private buildScenario(\n tc: TestCaseResult,\n featureId: string,\n scenarioLine: number\n ): IJsonScenario {\n const scenarioName = tc.story.scenario;\n const scenarioId = generateScenarioId(featureId, scenarioName);\n\n // Build steps\n const steps = this.buildSteps(tc, scenarioLine);\n\n // Build scenario tags\n const tags: IJsonTag[] = tc.tags.map((tag) => ({\n name: tag.startsWith(\"@\") ? tag : `@${tag}`,\n line: scenarioLine,\n }));\n\n return {\n description: \"\",\n id: scenarioId,\n keyword: \"Scenario\",\n line: scenarioLine,\n name: scenarioName,\n steps,\n tags,\n type: \"scenario\",\n };\n }\n\n /**\n * Build steps from story steps and step results.\n */\n private buildSteps(tc: TestCaseResult, scenarioLine: number): IJsonStep[] {\n const storySteps = tc.story.steps;\n const stepResults = tc.stepResults;\n\n // Create a map of step results by index\n const resultsByIndex = new Map<number, StepResult>();\n for (const sr of stepResults) {\n resultsByIndex.set(sr.index, sr);\n }\n\n // Check if any step failed (used to decide if last step gets attachments)\n const hasFailedStep = stepResults.some(sr => sr.status === \"failed\");\n\n const totalSteps = storySteps.length;\n return storySteps.map((step, index) => {\n const stepResult = resultsByIndex.get(index);\n const stepLine = generateStepLine(scenarioLine, index);\n const isLastStep = index === totalSteps - 1;\n return this.buildStep(step, stepResult, stepLine, index, tc.attachments, isLastStep, hasFailedStep);\n });\n }\n\n /**\n * Build a single step.\n */\n private buildStep(\n step: StoryStep,\n result: StepResult | undefined,\n line: number,\n index: number,\n attachments: Attachment[],\n isLastStep: boolean,\n hasFailedStep: boolean\n ): IJsonStep {\n // Keyword with optional trailing space\n const keyword = this.options.keywordSpacing\n ? `${step.keyword} `\n : step.keyword;\n\n // Build result\n const stepResult = this.buildStepResult(result);\n\n // Build embeddings for attachments (attach to failed step, or last step if no failure)\n const embeddings = this.buildEmbeddings(attachments, result, isLastStep, hasFailedStep);\n\n // Add screenshot docs as embeddings (always include with their step)\n const screenshotEmbeddings = this.buildScreenshotEmbeddings(step);\n embeddings.push(...screenshotEmbeddings);\n\n const jsonStep: IJsonStep = {\n keyword,\n line,\n name: step.text,\n result: stepResult,\n };\n\n // Only include optional fields if they have values\n if (embeddings.length > 0) {\n jsonStep.embeddings = embeddings;\n }\n\n // Add step arguments (doc strings from docs, data tables)\n const args = this.buildStepArguments(step);\n if (args.length > 0) {\n jsonStep.arguments = args;\n }\n\n return jsonStep;\n }\n\n /**\n * Build embeddings from screenshot doc entries.\n */\n private buildScreenshotEmbeddings(step: StoryStep): IJsonEmbedding[] {\n if (!step.docs) {\n return [];\n }\n\n const embeddings: IJsonEmbedding[] = [];\n\n for (const doc of step.docs) {\n if (doc.kind !== \"screenshot\" || !doc.path.startsWith(\"data:\")) {\n continue;\n }\n\n // Parse data URI: data:image/png;base64,ABC123...\n const match = doc.path.match(/^data:([^;]+);base64,(.+)$/);\n if (match) {\n embeddings.push({\n data: match[2],\n mime_type: match[1],\n name: doc.alt,\n });\n }\n }\n\n return embeddings;\n }\n\n /**\n * Build step result.\n */\n private buildStepResult(result: StepResult | undefined): IJsonStepResult {\n if (!result) {\n return {\n status: \"undefined\",\n duration: 0,\n };\n }\n\n // Map canonical status to Cucumber status\n const statusMap: Record<string, IJsonStepResult[\"status\"]> = {\n passed: \"passed\",\n failed: \"failed\",\n skipped: \"skipped\",\n pending: \"pending\",\n };\n\n const stepResult: IJsonStepResult = {\n status: statusMap[result.status] ?? \"undefined\",\n // Duration in nanoseconds (Cucumber uses nanoseconds)\n duration: result.durationMs * 1_000_000,\n };\n\n if (result.errorMessage) {\n stepResult.error_message = result.errorMessage;\n }\n\n return stepResult;\n }\n\n /**\n * Build embeddings (attachments) for a step.\n *\n * Cucumber convention: attach to the failing step, or last step if no failure.\n */\n private buildEmbeddings(\n attachments: Attachment[],\n result: StepResult | undefined,\n isLastStep: boolean,\n hasFailedStep: boolean\n ): IJsonEmbedding[] {\n const isFailed = result?.status === \"failed\";\n\n // If this step failed, attach here\n if (isFailed) {\n // Continue to attach\n } else if (isLastStep && !hasFailedStep) {\n // No failed step in scenario, attach to last step\n } else {\n // Not a failed step, and either not last or there's a failed step elsewhere\n return [];\n }\n\n // Only include BASE64 attachments - IDENTITY attachments are URLs/paths, not embeddable data\n const base64Attachments = attachments.filter(\n (att) => att.contentEncoding === \"BASE64\"\n );\n\n if (base64Attachments.length === 0) {\n return [];\n }\n\n return base64Attachments.map((att) => ({\n data: att.body,\n mime_type: att.mediaType,\n name: att.name,\n }));\n }\n\n /**\n * Build step arguments from step docs.\n *\n * Converts doc entries to Cucumber step arguments (doc strings, data tables).\n */\n private buildStepArguments(step: StoryStep): Array<{ doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> }> {\n if (!step.docs || step.docs.length === 0) {\n return [];\n }\n\n const args: Array<{ doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> }> = [];\n\n for (const doc of step.docs) {\n const arg = this.docEntryToArgument(doc);\n if (arg) {\n args.push(arg);\n }\n }\n\n return args;\n }\n\n /**\n * Convert a doc entry to a Cucumber step argument.\n */\n private docEntryToArgument(doc: DocEntry): { doc_string?: { content: string; content_type?: string; line: number }; rows?: Array<{ cells: string[] }> } | null {\n switch (doc.kind) {\n case \"code\":\n return {\n doc_string: {\n content: doc.content,\n content_type: doc.lang,\n line: 0,\n },\n };\n\n case \"table\":\n return {\n rows: [\n { cells: doc.columns },\n ...doc.rows.map((row) => ({ cells: row })),\n ],\n };\n\n case \"note\":\n return {\n doc_string: {\n content: doc.text,\n content_type: \"text/plain\",\n line: 0,\n },\n };\n\n case \"mermaid\":\n return {\n doc_string: {\n content: doc.code,\n content_type: \"text/x-mermaid\",\n line: 0,\n },\n };\n\n case \"section\":\n return {\n doc_string: {\n content: `# ${doc.title}\\n\\n${doc.markdown}`,\n content_type: \"text/markdown\",\n line: 0,\n },\n };\n\n case \"link\":\n return {\n doc_string: {\n content: `[${doc.label}](${doc.url})`,\n content_type: \"text/markdown\",\n line: 0,\n },\n };\n\n case \"kv\": {\n const value = typeof doc.value === \"string\"\n ? doc.value\n : JSON.stringify(doc.value, null, 2);\n return {\n doc_string: {\n content: `${doc.label}: ${value}`,\n content_type: \"text/plain\",\n line: 0,\n },\n };\n }\n\n case \"tag\":\n return {\n doc_string: {\n content: doc.names.map((n) => `@${n}`).join(\" \"),\n content_type: \"text/plain\",\n line: 0,\n },\n };\n\n case \"custom\":\n return {\n doc_string: {\n content: JSON.stringify(doc.data, null, 2),\n content_type: \"application/json\",\n line: 0,\n },\n };\n\n case \"screenshot\":\n // Screenshots are handled as embeddings, not arguments\n return null;\n\n default:\n return null;\n }\n }\n}\n","/**\n * HTML Report Template.\n *\n * Generates the JavaScript for interactivity (theme toggle, search, collapse).\n */\n\n/** Theme-related JavaScript (only included when darkMode is enabled) */\nconst JS_THEME = `\n// Theme management\nfunction getSystemTheme() {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nfunction getEffectiveTheme() {\n const saved = localStorage.getItem('theme');\n if (saved === 'dark' || saved === 'light') return saved;\n return getSystemTheme();\n}\n\nfunction toggleTheme() {\n const current = getEffectiveTheme();\n const next = current === 'dark' ? 'light' : 'dark';\n localStorage.setItem('theme', next);\n applyTheme(next);\n}\n\nfunction applyTheme(theme) {\n document.documentElement.setAttribute('data-theme', theme);\n updateThemeIcon(theme);\n}\n\nfunction updateThemeIcon(theme) {\n const btn = document.querySelector('.theme-toggle');\n if (btn) {\n btn.textContent = theme === 'dark' ? '\\\\u2600\\\\ufe0f' : '\\\\ud83c\\\\udf19';\n btn.setAttribute('aria-label', theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');\n }\n}\n\nfunction initTheme() {\n const theme = getEffectiveTheme();\n applyTheme(theme);\n\n // Listen for system theme changes\n window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {\n if (!localStorage.getItem('theme')) {\n applyTheme(e.matches ? 'dark' : 'light');\n }\n });\n}\n`;\n\n/** Core JavaScript (always included) */\nconst JS_CORE = `\n// Search functionality\nfunction initSearch() {\n const input = document.querySelector('.search-input');\n if (!input) return;\n\n let debounceTimer;\n input.addEventListener('input', (e) => {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n filterScenarios(e.target.value.toLowerCase().trim());\n }, 150);\n });\n\n // Clear search on Escape\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n e.target.value = '';\n filterScenarios('');\n }\n });\n}\n\nfunction filterScenarios(query) {\n const features = document.querySelectorAll('.feature');\n\n features.forEach(feature => {\n const scenarios = feature.querySelectorAll('.scenario');\n let visibleCount = 0;\n\n scenarios.forEach(scenario => {\n const title = scenario.querySelector('.scenario-title')?.textContent?.toLowerCase() || '';\n const tags = Array.from(scenario.querySelectorAll('.tag')).map(t => t.textContent.toLowerCase());\n const steps = Array.from(scenario.querySelectorAll('.step-text')).map(s => s.textContent.toLowerCase());\n\n const matches = !query ||\n title.includes(query) ||\n tags.some(t => t.includes(query)) ||\n steps.some(s => s.includes(query));\n\n scenario.style.display = matches ? '' : 'none';\n if (matches) visibleCount++;\n });\n\n feature.style.display = visibleCount > 0 ? '' : 'none';\n });\n}\n\n// Collapse/expand functionality\nfunction toggleCollapse(header, container) {\n container?.classList.toggle('collapsed');\n const isCollapsed = container?.classList.contains('collapsed');\n header.setAttribute('aria-expanded', !isCollapsed);\n}\n\nfunction initCollapse() {\n document.querySelectorAll('.feature-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.feature'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.feature'));\n }\n });\n });\n\n document.querySelectorAll('.scenario-header').forEach(header => {\n header.addEventListener('click', () => {\n toggleCollapse(header, header.closest('.scenario'));\n });\n header.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse(header, header.closest('.scenario'));\n }\n });\n });\n}\n\nfunction expandAll() {\n document.querySelectorAll('.feature, .scenario').forEach(el => {\n el.classList.remove('collapsed');\n const header = el.querySelector('.feature-header, .scenario-header');\n header?.setAttribute('aria-expanded', 'true');\n });\n}\n\nfunction collapseAll() {\n document.querySelectorAll('.feature, .scenario').forEach(el => {\n el.classList.add('collapsed');\n const header = el.querySelector('.feature-header, .scenario-header');\n header?.setAttribute('aria-expanded', 'false');\n });\n}\n`;\n\n/** Options for HTML template generation */\nexport interface HtmlTemplateOptions {\n includeSearch?: boolean;\n includeDarkMode?: boolean;\n syntaxHighlighting?: boolean;\n mermaidEnabled?: boolean;\n markdownEnabled?: boolean;\n}\n\n/** JavaScript for markdown parsing (used as a function body string in the ESM module) */\nconst JS_MARKDOWN_FN = `\nfunction parseMarkdownSections(marked) {\n // Configure marked for safe output\n marked.setOptions({\n breaks: true,\n gfm: true\n });\n\n document.querySelectorAll('.doc-section-content[data-markdown]').forEach(el => {\n const encoded = el.getAttribute('data-markdown');\n if (!encoded) return;\n\n try {\n const markdown = decodeURIComponent(atob(encoded));\n // Use marked.parse and sanitize by escaping script tags\n let html = marked.parse(markdown);\n // Basic XSS prevention - remove script tags\n html = html.replace(/<script\\\\b[^<]*(?:(?!<\\\\/script>)<[^<]*)*<\\\\/script>/gi, '');\n el.innerHTML = html;\n el.removeAttribute('data-markdown');\n } catch (e) {\n console.warn('Failed to parse markdown:', e);\n }\n });\n}\n`;\n\n/** Generate the inline JavaScript for the report (non-CDN parts) */\nfunction generateScript(options: HtmlTemplateOptions): string {\n const initCalls: string[] = [];\n\n if (options.includeDarkMode) {\n initCalls.push('initTheme();');\n }\n initCalls.push('initSearch();');\n initCalls.push('initCollapse();');\n\n const initScript = `\n// Initialize on load\ndocument.addEventListener('DOMContentLoaded', () => {\n ${initCalls.join('\\n ')}\n});\n`;\n\n let script = options.includeDarkMode ? JS_THEME : '';\n script += JS_CORE;\n script += initScript;\n\n return script;\n}\n\n/** Generate ESM module script that imports CDN libraries and initializes them */\nfunction generateEsmScript(options: HtmlTemplateOptions): string {\n const imports: string[] = [];\n const initCalls: string[] = [];\n\n if (options.syntaxHighlighting) {\n imports.push('import hljs from \"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js\";');\n initCalls.push('hljs.highlightAll();');\n }\n\n if (options.mermaidEnabled) {\n imports.push('import mermaid from \"https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs\";');\n initCalls.push(\"mermaid.initialize({ startOnLoad: false, theme: 'neutral' });\");\n initCalls.push('await mermaid.run({ querySelector: \".mermaid\" });');\n }\n\n if (options.markdownEnabled) {\n imports.push('import { marked } from \"https://cdn.jsdelivr.net/npm/marked/lib/marked.esm.js\";');\n initCalls.push('parseMarkdownSections(marked);');\n }\n\n if (imports.length === 0) return '';\n\n let script = imports.join('\\n ');\n if (options.markdownEnabled) {\n script += '\\n' + JS_MARKDOWN_FN;\n }\n script += '\\n ' + initCalls.join('\\n ');\n\n return `\\n <script type=\"module\">\\n ${script}\\n </script>`;\n}\n\n/**\n * Generate the HTML template for the report.\n */\nexport function generateHtmlTemplate(\n title: string,\n styles: string,\n body: string,\n options: HtmlTemplateOptions = {}\n): string {\n const {\n includeSearch = true,\n includeDarkMode = true,\n syntaxHighlighting = true,\n mermaidEnabled = true,\n markdownEnabled = true,\n } = options;\n\n const script = generateScript(options);\n\n // Set initial theme to light; initTheme() will update based on system/localStorage\n const themeAttr = includeDarkMode ? ' data-theme=\"light\"' : '';\n\n // CDN stylesheet resources for optional features\n const cdnStyles: string[] = [];\n\n if (syntaxHighlighting) {\n cdnStyles.push('<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css\">');\n cdnStyles.push('<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css\" media=\"(prefers-color-scheme: dark)\">');\n }\n\n const cdnStylesHtml = cdnStyles.length > 0 ? '\\n ' + cdnStyles.join('\\n ') : '';\n const esmScriptHtml = generateEsmScript(options);\n\n return `<!DOCTYPE html>\n<html lang=\"en\"${themeAttr}>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"color-scheme\" content=\"light dark\">\n <title>${escapeHtml(title)}</title>${cdnStylesHtml}\n <style>${styles}</style>\n</head>\n<body>\n <div class=\"container\">\n <header class=\"header\">\n <h1>${escapeHtml(title)}</h1>\n <div class=\"header-actions\">\n ${includeSearch ? '<input type=\"text\" class=\"search-input\" placeholder=\"Search scenarios...\" aria-label=\"Search scenarios\">' : ''}\n ${includeDarkMode ? '<button type=\"button\" class=\"theme-toggle\" onclick=\"toggleTheme()\" aria-label=\"Toggle theme\"></button>' : ''}\n </div>\n </header>\n ${body}\n </div>\n <script>${script}</script>${esmScriptHtml}\n</body>\n</html>`;\n}\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","/**\n * HTML Report Styles.\n *\n * Modern, clean CSS inspired by shadcn/ui base theme with Cucumber branding.\n * Supports dark/light mode via CSS custom properties and prefers-color-scheme.\n */\n\nexport const CSS_STYLES = `\n/* ============================================================================\n Google Fonts Import - IBM Plex for refined typography\n ============================================================================ */\n@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');\n\n/* ============================================================================\n CSS Custom Properties - Light Mode (Default)\n Cucumber-branded shadcn/ui base theme\n ============================================================================ */\n:root {\n /* Typography */\n --font-sans: \"IBM Plex Sans\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n --font-mono: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace;\n\n /* Base colors (shadcn base with cucumber accent) */\n --background: hsl(0 0% 100%);\n --foreground: hsl(0 0% 9%);\n --card: hsl(0 0% 100%);\n --card-foreground: hsl(0 0% 9%);\n --popover: hsl(0 0% 100%);\n --popover-foreground: hsl(0 0% 9%);\n\n /* Cucumber green as primary */\n --primary: hsl(145 63% 42%);\n --primary-foreground: hsl(0 0% 100%);\n\n --secondary: hsl(0 0% 96.5%);\n --secondary-foreground: hsl(0 0% 9%);\n --muted: hsl(0 0% 96.5%);\n --muted-foreground: hsl(0 0% 45%);\n --accent: hsl(0 0% 96.5%);\n --accent-foreground: hsl(0 0% 9%);\n --destructive: hsl(0 84% 60%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 90%);\n --input: hsl(0 0% 90%);\n --ring: hsl(145 63% 42%);\n --radius: 0.5rem;\n\n /* Shadows - refined for depth */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.03);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.06);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.07), 0 2px 4px -2px rgb(0 0 0 / 0.05);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);\n\n /* Status colors - cucumber-harmonized */\n --success: hsl(145 63% 42%);\n --success-light: hsl(145 55% 96%);\n --success-border: hsl(145 55% 88%);\n --error: hsl(0 72% 51%);\n --error-light: hsl(0 86% 97%);\n --error-border: hsl(0 72% 92%);\n --warning: hsl(38 92% 50%);\n --warning-light: hsl(48 100% 96%);\n --warning-border: hsl(48 96% 88%);\n --pending: hsl(262 60% 55%);\n --pending-light: hsl(262 55% 97%);\n --pending-border: hsl(262 55% 90%);\n\n /* Cucumber-specific */\n --keyword-color: hsl(145 63% 32%);\n --tag-bg: hsl(145 55% 95%);\n --tag-color: hsl(145 63% 30%);\n --tag-border: hsl(145 55% 85%);\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: hsl(0 0% 98%);\n --accordion-content-bg: hsl(0 0% 98.5%);\n}\n\n/* ============================================================================\n Dark Mode - Cucumber branded\n ============================================================================ */\n[data-theme=\"dark\"] {\n --background: hsl(0 0% 6%);\n --foreground: hsl(0 0% 95%);\n --card: hsl(0 0% 9%);\n --card-foreground: hsl(0 0% 95%);\n --popover: hsl(0 0% 9%);\n --popover-foreground: hsl(0 0% 95%);\n\n /* Cucumber green stays vibrant in dark mode */\n --primary: hsl(145 63% 50%);\n --primary-foreground: hsl(0 0% 6%);\n\n --secondary: hsl(0 0% 13%);\n --secondary-foreground: hsl(0 0% 95%);\n --muted: hsl(0 0% 13%);\n --muted-foreground: hsl(0 0% 55%);\n --accent: hsl(0 0% 13%);\n --accent-foreground: hsl(0 0% 95%);\n --destructive: hsl(0 72% 55%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 16%);\n --input: hsl(0 0% 16%);\n --ring: hsl(145 63% 50%);\n\n /* Shadows (subtle for dark mode) */\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.35);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.35);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.45), 0 4px 6px -4px rgb(0 0 0 / 0.35);\n\n /* Status colors (dark mode) */\n --success: hsl(145 63% 55%);\n --success-light: hsl(145 35% 14%);\n --success-border: hsl(145 35% 22%);\n --error: hsl(0 72% 60%);\n --error-light: hsl(0 35% 14%);\n --error-border: hsl(0 35% 22%);\n --warning: hsl(38 92% 55%);\n --warning-light: hsl(38 35% 14%);\n --warning-border: hsl(38 35% 22%);\n --pending: hsl(262 60% 65%);\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n\n /* Cucumber-specific (dark) */\n --keyword-color: hsl(145 63% 60%);\n --tag-bg: hsl(145 35% 14%);\n --tag-color: hsl(145 63% 60%);\n --tag-border: hsl(145 35% 22%);\n\n /* Accordion/Collapsible styling */\n --accordion-header-hover: hsl(0 0% 11%);\n --accordion-content-bg: hsl(0 0% 7%);\n}\n\n/* Auto dark mode based on system preference */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --background: hsl(0 0% 6%);\n --foreground: hsl(0 0% 95%);\n --card: hsl(0 0% 9%);\n --card-foreground: hsl(0 0% 95%);\n --popover: hsl(0 0% 9%);\n --popover-foreground: hsl(0 0% 95%);\n --primary: hsl(145 63% 50%);\n --primary-foreground: hsl(0 0% 6%);\n --secondary: hsl(0 0% 13%);\n --secondary-foreground: hsl(0 0% 95%);\n --muted: hsl(0 0% 13%);\n --muted-foreground: hsl(0 0% 55%);\n --accent: hsl(0 0% 13%);\n --accent-foreground: hsl(0 0% 95%);\n --destructive: hsl(0 72% 55%);\n --destructive-foreground: hsl(0 0% 100%);\n --border: hsl(0 0% 16%);\n --input: hsl(0 0% 16%);\n --ring: hsl(145 63% 50%);\n --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / 0.3);\n --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.35);\n --shadow: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.35);\n --shadow-md: 0 10px 15px -3px rgb(0 0 0 / 0.45), 0 4px 6px -4px rgb(0 0 0 / 0.35);\n --success: hsl(145 63% 55%);\n --success-light: hsl(145 35% 14%);\n --success-border: hsl(145 35% 22%);\n --error: hsl(0 72% 60%);\n --error-light: hsl(0 35% 14%);\n --error-border: hsl(0 35% 22%);\n --warning: hsl(38 92% 55%);\n --warning-light: hsl(38 35% 14%);\n --warning-border: hsl(38 35% 22%);\n --pending: hsl(262 60% 65%);\n --pending-light: hsl(262 25% 14%);\n --pending-border: hsl(262 25% 22%);\n --keyword-color: hsl(145 63% 60%);\n --tag-bg: hsl(145 35% 14%);\n --tag-color: hsl(145 63% 60%);\n --tag-border: hsl(145 35% 22%);\n --accordion-header-hover: hsl(0 0% 11%);\n --accordion-content-bg: hsl(0 0% 7%);\n }\n}\n\n/* ============================================================================\n Base Styles\n ============================================================================ */\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: var(--font-sans);\n font-size: 14px;\n line-height: 1.6;\n color: var(--foreground);\n background-color: var(--background);\n min-height: 100vh;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ============================================================================\n Layout\n ============================================================================ */\n.container {\n max-width: 1100px;\n margin: 0 auto;\n padding: 1.25rem;\n}\n\n@media (min-width: 768px) {\n .container {\n padding: 2rem 2.5rem;\n }\n}\n\n/* ============================================================================\n Header - shadcn style\n ============================================================================ */\n.header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding-bottom: 1.25rem;\n margin-bottom: 1.5rem;\n border-bottom: 1px solid var(--border);\n}\n\n.header h1 {\n font-size: 1.375rem;\n font-weight: 600;\n letter-spacing: -0.02em;\n color: var(--foreground);\n}\n\n.header-actions {\n display: flex;\n gap: 0.625rem;\n align-items: center;\n}\n\n/* ============================================================================\n Theme Toggle - shadcn button style\n ============================================================================ */\n.theme-toggle {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 2.25rem;\n height: 2.25rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n cursor: pointer;\n color: var(--foreground);\n font-size: 1rem;\n transition: all 0.15s ease;\n}\n\n.theme-toggle:hover {\n background: var(--accent);\n border-color: var(--border);\n}\n\n.theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Search Input - shadcn input style\n ============================================================================ */\n.search-input {\n height: 2.25rem;\n padding: 0 0.875rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--background);\n color: var(--foreground);\n font-family: var(--font-sans);\n font-size: 0.875rem;\n width: 220px;\n transition: all 0.15s ease;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--ring);\n box-shadow: 0 0 0 3px hsl(145 63% 42% / 0.1);\n}\n\n.search-input::placeholder {\n color: var(--muted-foreground);\n}\n\n@media (min-width: 640px) {\n .search-input {\n width: 260px;\n }\n}\n\n/* ============================================================================\n Meta Info - clean card style\n ============================================================================ */\n.meta-info {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem 1.75rem;\n margin-bottom: 1.25rem;\n padding: 0.75rem 1rem;\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font-size: 0.8125rem;\n color: var(--muted-foreground);\n}\n\n.meta-info dt {\n font-weight: 500;\n color: var(--foreground);\n display: inline;\n}\n\n.meta-info dd {\n display: inline;\n margin: 0 0 0 0.375rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Summary Cards - tinted card style\n ============================================================================ */\n.summary {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 0.75rem;\n margin-bottom: 1.5rem;\n}\n\n@media (max-width: 640px) {\n .summary {\n grid-template-columns: repeat(2, 1fr);\n }\n}\n\n.summary-card {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n padding: 1rem 1.25rem;\n transition: all 0.15s ease;\n}\n\n.summary-card:hover {\n box-shadow: var(--shadow-sm);\n}\n\n.summary-card .label {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--muted-foreground);\n font-weight: 500;\n margin-bottom: 0.375rem;\n}\n\n.summary-card .value {\n font-size: 2rem;\n font-weight: 700;\n letter-spacing: -0.03em;\n line-height: 1.1;\n font-family: var(--font-sans);\n}\n\n/* Passed - green tint */\n.summary-card.passed {\n background: var(--success-light);\n border-color: var(--success-border);\n}\n.summary-card.passed .value { color: var(--success); }\n\n/* Failed - red tint */\n.summary-card.failed {\n background: var(--error-light);\n border-color: var(--error-border);\n}\n.summary-card.failed .value { color: var(--error); }\n\n/* Skipped - amber tint */\n.summary-card.skipped {\n background: var(--warning-light);\n border-color: var(--warning-border);\n}\n.summary-card.skipped .value { color: var(--warning); }\n\n/* Pending - purple tint */\n.summary-card.pending {\n background: var(--pending-light);\n border-color: var(--pending-border);\n}\n.summary-card.pending .value { color: var(--pending); }\n\n/* ============================================================================\n Feature Sections - shadcn accordion style\n ============================================================================ */\n.feature {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n margin-bottom: 0.625rem;\n overflow: hidden;\n}\n\n.feature-header {\n padding: 0.875rem 1rem;\n background: var(--card);\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n user-select: none;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.feature-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.feature-info {\n flex: 1;\n min-width: 0;\n}\n\n.feature-title {\n font-weight: 600;\n font-size: 0.9375rem;\n color: var(--foreground);\n letter-spacing: -0.01em;\n}\n\n.feature-path {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature-stats {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n font-size: 0.8125rem;\n font-weight: 500;\n flex-shrink: 0;\n}\n\n.feature-stats .stat {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.feature-stats .stat.passed { color: var(--success); }\n.feature-stats .stat.failed { color: var(--error); }\n.feature-stats .stat.skipped { color: var(--warning); }\n\n.feature-content {\n padding: 0.625rem;\n border-top: 1px solid var(--border);\n background: var(--accordion-content-bg);\n}\n\n.feature.collapsed .feature-content {\n display: none;\n}\n\n/* ============================================================================\n Scenarios - nested accordion style\n ============================================================================ */\n.scenario {\n background: var(--card);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n margin-bottom: 0.5rem;\n overflow: hidden;\n}\n\n.scenario:last-child {\n margin-bottom: 0;\n}\n\n.scenario-header {\n padding: 0.75rem 1rem;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n cursor: pointer;\n transition: background-color 0.15s ease;\n gap: 1rem;\n}\n\n.scenario-header:hover {\n background: var(--accordion-header-hover);\n}\n\n.scenario-info {\n flex: 1;\n min-width: 0;\n}\n\n.scenario-title {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 500;\n font-size: 0.875rem;\n color: var(--foreground);\n}\n\n.scenario-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.scenario-meta {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-top: 0.375rem;\n}\n\n.tag {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n.scenario-duration {\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.scenario-content {\n padding: 0.75rem 1rem 1rem;\n border-top: 1px solid var(--border);\n}\n\n.scenario.collapsed .scenario-content {\n display: none;\n}\n\n/* ============================================================================\n Status Icons - refined\n ============================================================================ */\n.status-icon {\n font-size: 0.875rem;\n line-height: 1;\n flex-shrink: 0;\n}\n\n.status-passed { color: var(--success); }\n.status-failed { color: var(--error); }\n.status-skipped { color: var(--warning); }\n.status-pending { color: var(--pending); }\n\n/* ============================================================================\n Steps - story-like flow\n ============================================================================ */\n.steps {\n margin-top: 0.25rem;\n padding: 0.25rem 0;\n}\n\n.step {\n display: flex;\n gap: 0.5rem;\n padding: 0.375rem 0;\n font-size: 0.8125rem;\n align-items: baseline;\n line-height: 1.5;\n}\n\n.step-status {\n flex-shrink: 0;\n width: 1rem;\n text-align: center;\n font-size: 0.75rem;\n}\n\n.step-keyword {\n font-weight: 600;\n color: var(--keyword-color);\n flex-shrink: 0;\n min-width: 52px;\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n/* Indent continuation keywords (And, But, *) to show they belong to previous step */\n.step.continuation {\n padding-left: 1.25rem;\n}\n\n.step.continuation .step-keyword {\n color: var(--muted-foreground);\n font-weight: 500;\n}\n\n.step-text {\n flex: 1;\n color: var(--foreground);\n}\n\n.step-duration {\n color: var(--muted-foreground);\n font-size: 0.6875rem;\n font-family: var(--font-mono);\n white-space: nowrap;\n opacity: 0.7;\n}\n\n/* ============================================================================\n Error Display - alert style\n ============================================================================ */\n.error-box {\n margin-top: 0.75rem;\n padding: 0.875rem 1rem;\n background: var(--error-light);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--error-border);\n border-left: 3px solid var(--error);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n white-space: pre-wrap;\n overflow-x: auto;\n color: var(--error);\n}\n\n/* ============================================================================\n Attachments - badge style\n ============================================================================ */\n.attachments {\n margin-top: 0.75rem;\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n}\n\n.attachment {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n font-size: 0.75rem;\n font-family: var(--font-mono);\n text-decoration: none;\n color: var(--muted-foreground);\n transition: all 0.15s ease;\n}\n\n.attachment:hover {\n background: var(--accent);\n color: var(--foreground);\n border-color: var(--ring);\n}\n\n.attachment-image {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.attachment-video {\n max-width: 100%;\n margin-top: 0.5rem;\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n/* ============================================================================\n Chevron Icon - smooth rotation\n ============================================================================ */\n.chevron {\n color: var(--muted-foreground);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.collapsed .chevron {\n transform: rotate(-90deg);\n}\n\n/* ============================================================================\n Scrollbars - subtle styling\n ============================================================================ */\n::-webkit-scrollbar {\n width: 6px;\n height: 6px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background: var(--border);\n border-radius: 3px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background: var(--muted-foreground);\n}\n\n/* ============================================================================\n Focus States - cucumber ring color\n ============================================================================ */\n*:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);\n}\n\n/* ============================================================================\n Selection - cucumber tinted\n ============================================================================ */\n::selection {\n background: hsl(145 63% 42% / 0.15);\n color: inherit;\n}\n\n/* ============================================================================\n Animations - smooth reveals\n ============================================================================ */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.feature {\n animation: fadeIn 0.2s ease-out;\n}\n\n.feature:nth-child(2) { animation-delay: 0.02s; }\n.feature:nth-child(3) { animation-delay: 0.04s; }\n.feature:nth-child(4) { animation-delay: 0.06s; }\n.feature:nth-child(5) { animation-delay: 0.08s; }\n\n/* ============================================================================\n Print Styles\n ============================================================================ */\n@media print {\n :root {\n --background: white;\n --foreground: black;\n --card: white;\n --border: #e5e5e5;\n --muted: #f5f5f5;\n --muted-foreground: #666;\n }\n\n body {\n font-size: 12px;\n }\n\n .container {\n max-width: 100%;\n padding: 0;\n }\n\n .header-actions {\n display: none;\n }\n\n .feature,\n .scenario {\n page-break-inside: avoid;\n box-shadow: none;\n animation: none;\n }\n\n .collapsed .feature-content,\n .collapsed .scenario-content {\n display: block;\n }\n}\n\n/* ============================================================================\n Documentation Entries - Containers\n ============================================================================ */\n.story-docs {\n margin-bottom: 0.75rem;\n padding: 0.75rem;\n background: var(--accordion-content-bg);\n border-radius: calc(var(--radius) - 2px);\n border: 1px solid var(--border);\n}\n\n.step-docs {\n margin-left: 1.5rem;\n margin-top: 0.25rem;\n margin-bottom: 0.5rem;\n padding: 0.5rem 0.75rem;\n background: var(--accordion-content-bg);\n border-left: 2px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n}\n\n/* ============================================================================\n Documentation Entries - Note\n ============================================================================ */\n.doc-note {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0.5rem;\n background: var(--muted);\n border-left: 3px solid var(--primary);\n border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;\n font-size: 0.8125rem;\n line-height: 1.5;\n color: var(--foreground);\n}\n\n.doc-note:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================================================\n Documentation Entries - Tags\n ============================================================================ */\n.doc-tag {\n display: flex;\n flex-wrap: wrap;\n gap: 0.375rem;\n margin-bottom: 0.5rem;\n}\n\n.doc-tag:last-child {\n margin-bottom: 0;\n}\n\n.doc-tag-item {\n font-size: 0.6875rem;\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n background: var(--tag-bg);\n color: var(--tag-color);\n border: 1px solid var(--tag-border);\n border-radius: 9999px;\n font-family: var(--font-mono);\n}\n\n/* ============================================================================\n Documentation Entries - Key-Value\n ============================================================================ */\n.doc-kv {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 0.375rem;\n font-size: 0.8125rem;\n align-items: baseline;\n}\n\n.doc-kv:last-child {\n margin-bottom: 0;\n}\n\n.doc-kv-label {\n font-weight: 600;\n color: var(--muted-foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n}\n\n.doc-kv-value {\n color: var(--foreground);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n white-space: pre-wrap;\n word-break: break-word;\n}\n\n/* ============================================================================\n Documentation Entries - Code\n ============================================================================ */\n.doc-code {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-code:last-child {\n margin-bottom: 0;\n}\n\n.doc-code-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n}\n\n.doc-code-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-code-lang {\n font-size: 0.625rem;\n font-weight: 500;\n padding: 0.125rem 0.375rem;\n background: var(--primary);\n color: var(--primary-foreground);\n border-radius: 9999px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n.doc-code-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-code-content code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Table\n ============================================================================ */\n.doc-table {\n margin-bottom: 0.5rem;\n}\n\n.doc-table:last-child {\n margin-bottom: 0;\n}\n\n.doc-table-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n margin-bottom: 0.375rem;\n}\n\n.doc-table table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.75rem;\n font-family: var(--font-mono);\n}\n\n.doc-table th,\n.doc-table td {\n padding: 0.5rem 0.75rem;\n text-align: left;\n border: 1px solid var(--border);\n}\n\n.doc-table th {\n background: var(--muted);\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-table td {\n background: var(--card);\n color: var(--foreground);\n}\n\n.doc-table tr:hover td {\n background: var(--accordion-header-hover);\n}\n\n/* ============================================================================\n Documentation Entries - Link\n ============================================================================ */\n.doc-link {\n margin-bottom: 0.375rem;\n}\n\n.doc-link:last-child {\n margin-bottom: 0;\n}\n\n.doc-link a {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n font-size: 0.8125rem;\n color: var(--primary);\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.doc-link a:hover {\n color: var(--keyword-color);\n text-decoration: underline;\n}\n\n.doc-link a::before {\n content: \"→\";\n font-size: 0.75rem;\n}\n\n/* ============================================================================\n Documentation Entries - Section\n ============================================================================ */\n.doc-section {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-section:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-title {\n padding: 0.5rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--foreground);\n}\n\n.doc-section-content {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-size: 0.8125rem;\n line-height: 1.6;\n white-space: pre-wrap;\n color: var(--foreground);\n}\n\n/* Parsed markdown content in sections */\n.doc-section-parsed .doc-section-content {\n white-space: normal;\n}\n\n.doc-section-parsed .doc-section-content h1,\n.doc-section-parsed .doc-section-content h2,\n.doc-section-parsed .doc-section-content h3,\n.doc-section-parsed .doc-section-content h4,\n.doc-section-parsed .doc-section-content h5,\n.doc-section-parsed .doc-section-content h6 {\n margin-top: 1em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.3;\n color: var(--foreground);\n}\n\n.doc-section-parsed .doc-section-content h1:first-child,\n.doc-section-parsed .doc-section-content h2:first-child,\n.doc-section-parsed .doc-section-content h3:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content h1 { font-size: 1.25rem; }\n.doc-section-parsed .doc-section-content h2 { font-size: 1.125rem; }\n.doc-section-parsed .doc-section-content h3 { font-size: 1rem; }\n.doc-section-parsed .doc-section-content h4 { font-size: 0.9375rem; }\n.doc-section-parsed .doc-section-content h5 { font-size: 0.875rem; }\n.doc-section-parsed .doc-section-content h6 { font-size: 0.8125rem; color: var(--muted-foreground); }\n\n.doc-section-parsed .doc-section-content p {\n margin: 0.5em 0;\n}\n\n.doc-section-parsed .doc-section-content p:first-child {\n margin-top: 0;\n}\n\n.doc-section-parsed .doc-section-content p:last-child {\n margin-bottom: 0;\n}\n\n.doc-section-parsed .doc-section-content ul,\n.doc-section-parsed .doc-section-content ol {\n margin: 0.5em 0;\n padding-left: 1.5em;\n}\n\n.doc-section-parsed .doc-section-content li {\n margin: 0.25em 0;\n}\n\n.doc-section-parsed .doc-section-content a {\n color: var(--primary);\n text-decoration: none;\n}\n\n.doc-section-parsed .doc-section-content a:hover {\n text-decoration: underline;\n}\n\n.doc-section-parsed .doc-section-content code {\n font-family: var(--font-mono);\n font-size: 0.85em;\n padding: 0.125em 0.375em;\n background: var(--muted);\n border-radius: 3px;\n}\n\n.doc-section-parsed .doc-section-content pre {\n margin: 0.75em 0;\n padding: 0.75em;\n background: var(--muted);\n border-radius: calc(var(--radius) - 2px);\n overflow-x: auto;\n}\n\n.doc-section-parsed .doc-section-content pre code {\n padding: 0;\n background: none;\n}\n\n.doc-section-parsed .doc-section-content blockquote {\n margin: 0.75em 0;\n padding: 0.5em 1em;\n border-left: 3px solid var(--primary);\n background: var(--muted);\n color: var(--muted-foreground);\n}\n\n.doc-section-parsed .doc-section-content blockquote p {\n margin: 0;\n}\n\n.doc-section-parsed .doc-section-content hr {\n margin: 1em 0;\n border: none;\n border-top: 1px solid var(--border);\n}\n\n.doc-section-parsed .doc-section-content table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n font-size: 0.8125rem;\n}\n\n.doc-section-parsed .doc-section-content th,\n.doc-section-parsed .doc-section-content td {\n padding: 0.5em 0.75em;\n border: 1px solid var(--border);\n text-align: left;\n}\n\n.doc-section-parsed .doc-section-content th {\n background: var(--muted);\n font-weight: 600;\n}\n\n.doc-section-parsed .doc-section-content img {\n max-width: 100%;\n height: auto;\n border-radius: calc(var(--radius) - 2px);\n}\n\n/* ============================================================================\n Documentation Entries - Mermaid (displayed as code)\n ============================================================================ */\n.doc-mermaid {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-mermaid:last-child {\n margin-bottom: 0;\n}\n\n.doc-mermaid-title {\n padding: 0.375rem 0.75rem;\n background: var(--muted);\n border-bottom: 1px solid var(--border);\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n\n.doc-mermaid-title::before {\n content: \"◇ \";\n color: var(--primary);\n}\n\n.doc-mermaid-code {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-mermaid-code code {\n font-family: inherit;\n background: none;\n}\n\n/* ============================================================================\n Documentation Entries - Screenshot\n ============================================================================ */\n.doc-screenshot {\n margin-bottom: 0.5rem;\n}\n\n.doc-screenshot:last-child {\n margin-bottom: 0;\n}\n\n.doc-screenshot-img {\n max-width: 100%;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n display: block;\n}\n\n.doc-screenshot-caption {\n margin-top: 0.375rem;\n font-size: 0.75rem;\n color: var(--muted-foreground);\n font-style: italic;\n}\n\n/* ============================================================================\n Documentation Entries - Custom\n ============================================================================ */\n.doc-custom {\n margin-bottom: 0.5rem;\n border: 1px solid var(--border);\n border-radius: calc(var(--radius) - 2px);\n overflow: hidden;\n}\n\n.doc-custom:last-child {\n margin-bottom: 0;\n}\n\n.doc-custom-type {\n padding: 0.375rem 0.75rem;\n background: var(--warning-light);\n border-bottom: 1px solid var(--warning-border);\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--warning);\n}\n\n.doc-custom-data {\n margin: 0;\n padding: 0.75rem;\n background: var(--card);\n font-family: var(--font-mono);\n font-size: 0.75rem;\n line-height: 1.6;\n overflow-x: auto;\n white-space: pre;\n}\n\n.doc-custom-data code {\n font-family: inherit;\n background: none;\n}\n`;\n","/**\n * Pure helper: map test status to display icon.\n * Used by scenario and steps renderers; inject via deps for testability.\n */\n\nimport type { TestStatus } from \"../../../types/test-result\";\n\nexport type GetStatusIcon = (status: TestStatus) => string;\n\nexport function getStatusIcon(status: TestStatus): string {\n switch (status) {\n case \"passed\":\n return \"✓\";\n case \"failed\":\n return \"✗\";\n case \"skipped\":\n return \"○\";\n case \"pending\":\n return \"◔\";\n default:\n return \"?\";\n }\n}\n","/**\n * Render meta info section (fn(args, deps)).\n */\n\nexport interface RenderMetaInfoArgs {\n startedAtMs: number;\n durationMs: number;\n packageVersion?: string;\n gitSha?: string;\n ciName?: string;\n}\n\nexport interface RenderMetaInfoDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderMetaInfo(\n args: RenderMetaInfoArgs,\n deps: RenderMetaInfoDeps,\n): string {\n const items: string[] = [];\n\n const startDate = new Date(args.startedAtMs);\n items.push(`<dt>Started:</dt><dd>${startDate.toISOString()}</dd>`);\n\n const duration = (args.durationMs / 1000).toFixed(2);\n items.push(`<dt>Duration:</dt><dd>${duration}s</dd>`);\n\n if (args.packageVersion) {\n items.push(`<dt>Version:</dt><dd>${deps.escapeHtml(args.packageVersion)}</dd>`);\n }\n\n if (args.gitSha) {\n const shortSha =\n args.gitSha.length > 7 ? args.gitSha.slice(0, 7) : args.gitSha;\n items.push(`<dt>Git:</dt><dd>${deps.escapeHtml(shortSha)}</dd>`);\n }\n\n if (args.ciName) {\n items.push(`<dt>CI:</dt><dd>${deps.escapeHtml(args.ciName)}</dd>`);\n }\n\n return `<dl class=\"meta-info\">${items.join(\"\")}</dl>`;\n}\n","/**\n * Render summary cards section (fn(args, deps)).\n * No deps: pure counts to HTML.\n */\n\nexport interface RenderSummaryArgs {\n total: number;\n passed: number;\n failed: number;\n skipped: number;\n}\n\nexport interface RenderSummaryDeps {\n // No dependencies; structure only\n}\n\nexport function renderSummary(\n args: RenderSummaryArgs,\n _deps: RenderSummaryDeps,\n): string {\n const { total, passed, failed, skipped } = args;\n return `\n<div class=\"summary\">\n <div class=\"summary-card\">\n <div class=\"label\">Total</div>\n <div class=\"value\">${total}</div>\n </div>\n <div class=\"summary-card passed\">\n <div class=\"label\">Passed</div>\n <div class=\"value\">${passed}</div>\n </div>\n <div class=\"summary-card failed\">\n <div class=\"label\">Failed</div>\n <div class=\"value\">${failed}</div>\n </div>\n <div class=\"summary-card skipped\">\n <div class=\"label\">Skipped</div>\n <div class=\"value\">${skipped}</div>\n </div>\n</div>`;\n}\n","/**\n * Render error box (fn(args, deps)).\n */\n\nexport interface RenderErrorBoxArgs {\n message: string;\n stack?: string;\n}\n\nexport interface RenderErrorBoxDeps {\n escapeHtml: (str: string) => string;\n}\n\nexport function renderErrorBox(\n args: RenderErrorBoxArgs,\n deps: RenderErrorBoxDeps,\n): string {\n const body =\n args.stack != null\n ? `${deps.escapeHtml(args.message)}\\n\\n${deps.escapeHtml(args.stack)}`\n : deps.escapeHtml(args.message);\n return `<div class=\"error-box\">${body}</div>`;\n}\n","/**\n * Render attachments section (fn(args, deps)).\n */\n\nimport type { Attachment } from \"../../../types/test-result\";\n\nexport interface RenderAttachmentsArgs {\n attachments: Attachment[];\n}\n\nexport interface RenderAttachmentsDeps {\n escapeHtml: (str: string) => string;\n embedScreenshots: boolean;\n}\n\nexport function renderAttachments(\n args: RenderAttachmentsArgs,\n deps: RenderAttachmentsDeps,\n): string {\n if (args.attachments.length === 0) {\n return \"\";\n }\n\n const items = args.attachments.map((att) => {\n const isImage = att.mediaType.startsWith(\"image/\");\n const isVideo = att.mediaType.startsWith(\"video/\");\n const isBase64 = att.contentEncoding === \"BASE64\";\n\n if (isImage && deps.embedScreenshots && isBase64) {\n return `\n<div class=\"attachment\">\n ${deps.escapeHtml(att.name)}\n <img class=\"attachment-image\" src=\"data:${att.mediaType};base64,${att.body}\" alt=\"${deps.escapeHtml(att.name)}\" />\n</div>`;\n }\n\n if (isVideo && deps.embedScreenshots) {\n const src = isBase64\n ? `data:${att.mediaType};base64,${att.body}`\n : att.body;\n return `\n<div class=\"attachment\">\n ${deps.escapeHtml(att.name)}\n <video class=\"attachment-video\" controls src=\"${deps.escapeHtml(src)}\"></video>\n</div>`;\n }\n\n const href = isBase64\n ? `data:${att.mediaType};base64,${att.body}`\n : att.body;\n\n return `<a class=\"attachment\" href=\"${deps.escapeHtml(href)}\">${deps.escapeHtml(att.name)}</a>`;\n });\n\n return `<div class=\"attachments\">${items.join(\"\")}</div>`;\n}\n","/**\n * Render doc entries (fn(args, deps)).\n * One function per doc kind + dispatcher renderDocEntry.\n */\n\nimport type { DocEntry } from \"../../../types/story\";\n\nexport interface DocEntryDeps {\n escapeHtml: (str: string) => string;\n syntaxHighlighting: boolean;\n markdownEnabled: boolean;\n mermaidEnabled: boolean;\n}\n\nexport function renderDocNote(\n entry: Extract<DocEntry, { kind: \"note\" }>,\n deps: DocEntryDeps,\n): string {\n return `<div class=\"doc-note\">${deps.escapeHtml(entry.text)}</div>`;\n}\n\nexport function renderDocTag(\n entry: Extract<DocEntry, { kind: \"tag\" }>,\n deps: DocEntryDeps,\n): string {\n const tags = entry.names\n .map((t) => `<span class=\"doc-tag-item\">${deps.escapeHtml(t)}</span>`)\n .join(\"\");\n return `<div class=\"doc-tag\">${tags}</div>`;\n}\n\nexport function renderDocKv(\n entry: Extract<DocEntry, { kind: \"kv\" }>,\n deps: DocEntryDeps,\n): string {\n const valueStr =\n typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value, null, 2);\n return `<div class=\"doc-kv\">\n <span class=\"doc-kv-label\">${deps.escapeHtml(entry.label)}:</span>\n <span class=\"doc-kv-value\">${deps.escapeHtml(valueStr)}</span>\n</div>`;\n}\n\nexport function renderDocCode(\n entry: Extract<DocEntry, { kind: \"code\" }>,\n deps: DocEntryDeps,\n): string {\n const langBadge = entry.lang\n ? `<span class=\"doc-code-lang\">${deps.escapeHtml(entry.lang)}</span>`\n : \"\";\n const langClass =\n deps.syntaxHighlighting && entry.lang\n ? ` class=\"language-${deps.escapeHtml(entry.lang)}\"`\n : \"\";\n return `<div class=\"doc-code\">\n <div class=\"doc-code-header\">\n <span class=\"doc-code-label\">${deps.escapeHtml(entry.label)}</span>\n ${langBadge}\n </div>\n <pre class=\"doc-code-content\"><code${langClass}>${deps.escapeHtml(entry.content)}</code></pre>\n</div>`;\n}\n\nexport function renderDocTable(\n entry: Extract<DocEntry, { kind: \"table\" }>,\n deps: DocEntryDeps,\n): string {\n const headers = entry.columns\n .map((c) => `<th>${deps.escapeHtml(c)}</th>`)\n .join(\"\");\n const rows = entry.rows\n .map((r) =>\n `<tr>${r.map((c) => `<td>${deps.escapeHtml(c)}</td>`).join(\"\")}</tr>`,\n )\n .join(\"\");\n return `<div class=\"doc-table\">\n <div class=\"doc-table-label\">${deps.escapeHtml(entry.label)}</div>\n <table>\n <thead><tr>${headers}</tr></thead>\n <tbody>${rows}</tbody>\n </table>\n</div>`;\n}\n\nexport function renderDocLink(\n entry: Extract<DocEntry, { kind: \"link\" }>,\n deps: DocEntryDeps,\n): string {\n return `<div class=\"doc-link\">\n <a href=\"${deps.escapeHtml(entry.url)}\" target=\"_blank\" rel=\"noopener noreferrer\">${deps.escapeHtml(entry.label)}</a>\n</div>`;\n}\n\nexport function renderDocSection(\n entry: Extract<DocEntry, { kind: \"section\" }>,\n deps: DocEntryDeps,\n): string {\n if (deps.markdownEnabled) {\n const encodedMarkdown = btoa(encodeURIComponent(entry.markdown));\n return `<div class=\"doc-section doc-section-parsed\">\n <div class=\"doc-section-title\">${deps.escapeHtml(entry.title)}</div>\n <div class=\"doc-section-content\" data-markdown=\"${encodedMarkdown}\"></div>\n</div>`;\n }\n return `<div class=\"doc-section\">\n <div class=\"doc-section-title\">${deps.escapeHtml(entry.title)}</div>\n <pre class=\"doc-section-content\">${deps.escapeHtml(entry.markdown)}</pre>\n</div>`;\n}\n\nexport function renderDocMermaid(\n entry: Extract<DocEntry, { kind: \"mermaid\" }>,\n deps: DocEntryDeps,\n): string {\n const title = entry.title\n ? `<div class=\"doc-mermaid-title\">${deps.escapeHtml(entry.title)}</div>`\n : \"\";\n\n if (deps.mermaidEnabled) {\n return `<div class=\"doc-mermaid doc-mermaid-live\">\n ${title}\n <pre class=\"mermaid\">${deps.escapeHtml(entry.code)}</pre>\n</div>`;\n }\n return `<div class=\"doc-mermaid\">\n ${title}\n <pre class=\"doc-mermaid-code\"><code>${deps.escapeHtml(entry.code)}</code></pre>\n</div>`;\n}\n\nexport function renderDocScreenshot(\n entry: Extract<DocEntry, { kind: \"screenshot\" }>,\n deps: DocEntryDeps,\n): string {\n const alt = entry.alt ?? \"Screenshot\";\n const src = entry.path;\n return `<div class=\"doc-screenshot\">\n <img src=\"${deps.escapeHtml(src)}\" alt=\"${deps.escapeHtml(alt)}\" class=\"doc-screenshot-img\" />\n ${entry.alt ? `<div class=\"doc-screenshot-caption\">${deps.escapeHtml(entry.alt)}</div>` : \"\"}\n</div>`;\n}\n\nexport function renderDocCustom(\n entry: Extract<DocEntry, { kind: \"custom\" }>,\n deps: DocEntryDeps,\n): string {\n const dataStr = JSON.stringify(entry.data, null, 2);\n return `<div class=\"doc-custom\">\n <div class=\"doc-custom-type\">${deps.escapeHtml(entry.type)}</div>\n <pre class=\"doc-custom-data\"><code>${deps.escapeHtml(dataStr)}</code></pre>\n</div>`;\n}\n\nexport function renderDocEntry(entry: DocEntry, deps: DocEntryDeps): string {\n switch (entry.kind) {\n case \"note\":\n return renderDocNote(entry, deps);\n case \"tag\":\n return renderDocTag(entry, deps);\n case \"kv\":\n return renderDocKv(entry, deps);\n case \"code\":\n return renderDocCode(entry, deps);\n case \"table\":\n return renderDocTable(entry, deps);\n case \"link\":\n return renderDocLink(entry, deps);\n case \"section\":\n return renderDocSection(entry, deps);\n case \"mermaid\":\n return renderDocMermaid(entry, deps);\n case \"screenshot\":\n return renderDocScreenshot(entry, deps);\n case \"custom\":\n return renderDocCustom(entry, deps);\n default:\n return \"\";\n }\n}\n","/**\n * Render steps list (fn(args, deps)).\n */\n\nimport type { DocEntry, StoryStep } from \"../../../types/story\";\nimport type { StepResult } from \"../../../types/test-result\";\n\nconst CONTINUATION_KEYWORDS = [\"And\", \"But\", \"*\"];\n\nexport interface RenderStepsArgs {\n steps: StoryStep[];\n stepResults: StepResult[];\n}\n\nexport interface RenderStepsDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: import(\"../../../types/test-result.js\").TestStatus) => string;\n renderDocs: (docs: DocEntry[] | undefined, containerClass: string) => string;\n}\n\nexport function renderStep(\n step: StoryStep,\n stepResult: StepResult | undefined,\n index: number,\n deps: RenderStepsDeps,\n): string {\n const statusIcon = stepResult ? deps.getStatusIcon(stepResult.status) : \"○\";\n const statusClass = stepResult ? `status-${stepResult.status}` : \"\";\n const duration =\n stepResult && stepResult.durationMs > 0\n ? `${stepResult.durationMs}ms`\n : \"\";\n\n const keywordTrimmed = step.keyword.trim();\n const isContinuation = CONTINUATION_KEYWORDS.includes(keywordTrimmed);\n const stepClass = isContinuation ? \"step continuation\" : \"step\";\n\n const stepDocs = deps.renderDocs(step.docs, \"step-docs\");\n\n return `<div class=\"${stepClass}\">\n <span class=\"step-status ${statusClass}\">${statusIcon}</span>\n <span class=\"step-keyword\">${deps.escapeHtml(step.keyword)}</span>\n <span class=\"step-text\">${deps.escapeHtml(step.text)}</span>\n <span class=\"step-duration\">${duration}</span>\n</div>${stepDocs}`;\n}\n\nexport function renderSteps(\n args: RenderStepsArgs,\n deps: RenderStepsDeps,\n): string {\n const stepsHtml = args.steps\n .map((step, index) => {\n const stepResult = args.stepResults.find((sr) => sr.index === index);\n return renderStep(step, stepResult, index, deps);\n })\n .join(\"\");\n return `<div class=\"steps\">${stepsHtml}</div>`;\n}\n","/**\n * Render a scenario element (fn(args, deps)).\n */\n\nimport type { DocEntry } from \"../../../types/story\";\nimport type { TestCaseResult } from \"../../../types/test-result\";\n\nexport interface RenderScenarioArgs {\n tc: TestCaseResult;\n}\n\nexport interface RenderScenarioDeps {\n escapeHtml: (str: string) => string;\n getStatusIcon: (status: import(\"../../../types/test-result.js\").TestStatus) => string;\n startCollapsed: boolean;\n renderSteps: (\n args: import(\"./steps.js\").RenderStepsArgs,\n deps: import(\"./steps.js\").RenderStepsDeps,\n ) => string;\n renderDocs: (docs: DocEntry[] | undefined, containerClass: string) => string;\n renderErrorBox: (\n args: import(\"./error-box.js\").RenderErrorBoxArgs,\n deps: import(\"./error-box.js\").RenderErrorBoxDeps,\n ) => string;\n renderAttachments: (\n args: import(\"./attachments.js\").RenderAttachmentsArgs,\n deps: import(\"./attachments.js\").RenderAttachmentsDeps,\n ) => string;\n embedScreenshots: boolean;\n}\n\nexport function renderScenario(\n args: RenderScenarioArgs,\n deps: RenderScenarioDeps,\n): string {\n const { tc } = args;\n const statusIcon = deps.getStatusIcon(tc.status);\n const statusClass = `status-${tc.status}`;\n const duration =\n tc.durationMs > 0 ? `${(tc.durationMs / 1000).toFixed(2)}s` : \"\";\n\n const tags = tc.tags\n .map((t) => `<span class=\"tag\">${deps.escapeHtml(t)}</span>`)\n .join(\"\");\n\n const storyDocs = deps.renderDocs(tc.story.docs, \"story-docs\");\n const steps = deps.renderSteps(\n { steps: tc.story.steps, stepResults: tc.stepResults },\n {\n escapeHtml: deps.escapeHtml,\n getStatusIcon: deps.getStatusIcon,\n renderDocs: deps.renderDocs,\n },\n );\n const error =\n tc.status === \"failed\" && tc.errorMessage\n ? deps.renderErrorBox(\n { message: tc.errorMessage, stack: tc.errorStack },\n { escapeHtml: deps.escapeHtml },\n )\n : \"\";\n const attachments = deps.renderAttachments(\n { attachments: tc.attachments },\n {\n escapeHtml: deps.escapeHtml,\n embedScreenshots: deps.embedScreenshots,\n },\n );\n\n const collapsedClass = deps.startCollapsed ? \" collapsed\" : \"\";\n const ariaExpanded = !deps.startCollapsed;\n\n return `\n<div class=\"scenario${collapsedClass}\">\n <div class=\"scenario-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"${ariaExpanded}\">\n <div class=\"scenario-info\">\n <div class=\"scenario-title\">\n <span class=\"status-icon ${statusClass}\">${statusIcon}</span>\n <span class=\"scenario-name\">${deps.escapeHtml(tc.story.scenario)}</span>\n </div>\n <div class=\"scenario-meta\">${tags}</div>\n </div>\n <span class=\"scenario-duration\">${duration}</span>\n </div>\n <div class=\"scenario-content\">\n ${storyDocs}\n ${steps}\n ${error}\n ${attachments}\n </div>\n</div>`;\n}\n","/**\n * Render a feature section (group of scenarios from same file) (fn(args, deps)).\n */\n\nimport type { TestCaseResult } from \"../../../types/test-result\";\n\nexport interface RenderFeatureArgs {\n file: string;\n testCases: TestCaseResult[];\n}\n\nexport interface RenderFeatureDeps {\n escapeHtml: (str: string) => string;\n startCollapsed: boolean;\n renderScenario: (\n args: import(\"./scenario.js\").RenderScenarioArgs,\n deps: import(\"./scenario.js\").RenderScenarioDeps,\n ) => string;\n scenarioDeps: import(\"./scenario.js\").RenderScenarioDeps;\n}\n\nexport function renderFeature(\n args: RenderFeatureArgs,\n deps: RenderFeatureDeps,\n): string {\n const { file, testCases } = args;\n const passed = testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n\n const suitePaths = testCases\n .map((tc) => tc.titlePath)\n .filter((p) => p.length > 0);\n const featureName =\n suitePaths.length > 0 && suitePaths[0].length > 0\n ? suitePaths[0][0]\n : file.split(\"/\").pop()?.replace(/\\.[^.]+$/, \"\") ?? file;\n\n const collapsedClass = deps.startCollapsed ? \" collapsed\" : \"\";\n const ariaExpanded = !deps.startCollapsed;\n\n const scenarios = testCases\n .map((tc) => deps.renderScenario({ tc }, deps.scenarioDeps))\n .join(\"\\n\");\n\n return `\n<div class=\"feature${collapsedClass}\">\n <div class=\"feature-header\" role=\"button\" tabindex=\"0\" aria-expanded=\"${ariaExpanded}\">\n <div class=\"feature-info\">\n <div class=\"feature-title\">${deps.escapeHtml(featureName)}</div>\n <div class=\"feature-path\">${deps.escapeHtml(file)}</div>\n </div>\n <div class=\"feature-stats\">\n <span class=\"stat passed\">✓ ${passed}</span>\n <span class=\"stat failed\">✗ ${failed}</span>\n <span class=\"stat skipped\">○ ${skipped}</span>\n <span class=\"chevron\">▼</span>\n </div>\n </div>\n <div class=\"feature-content\">\n ${scenarios}\n </div>\n</div>`;\n}\n","/**\n * Build report body from run (fn(args, deps)).\n * Composes meta, summary, and features; uses groupBy for feature grouping.\n */\n\nimport type { TestRunResult } from \"../../../types/test-result\";\n\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n\nexport interface BuildBodyArgs {\n run: TestRunResult;\n}\n\nexport interface BuildBodyDeps {\n renderMetaInfo: (\n args: import(\"./meta.js\").RenderMetaInfoArgs,\n deps: import(\"./meta.js\").RenderMetaInfoDeps,\n ) => string;\n renderSummary: (\n args: import(\"./summary.js\").RenderSummaryArgs,\n deps: import(\"./summary.js\").RenderSummaryDeps,\n ) => string;\n renderFeature: (\n args: import(\"./feature.js\").RenderFeatureArgs,\n deps: import(\"./feature.js\").RenderFeatureDeps,\n ) => string;\n metaDeps: import(\"./meta.js\").RenderMetaInfoDeps;\n summaryDeps: import(\"./summary.js\").RenderSummaryDeps;\n featureDeps: import(\"./feature.js\").RenderFeatureDeps;\n}\n\nexport function buildBody(args: BuildBodyArgs, deps: BuildBodyDeps): string {\n const { run } = args;\n const parts: string[] = [];\n\n parts.push(\n deps.renderMetaInfo(\n {\n startedAtMs: run.startedAtMs,\n durationMs: run.durationMs,\n packageVersion: run.packageVersion,\n gitSha: run.gitSha,\n ciName: run.ci?.name,\n },\n deps.metaDeps,\n ),\n );\n\n const total = run.testCases.length;\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\",\n ).length;\n parts.push(\n deps.renderSummary(\n { total, passed, failed, skipped },\n deps.summaryDeps,\n ),\n );\n\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n for (const [file, testCases] of byFile) {\n parts.push(\n deps.renderFeature({ file, testCases }, deps.featureDeps),\n );\n }\n\n return parts.join(\"\\n\");\n}\n","/**\n * HTML renderers and factory (fn(args, deps) pattern).\n * Exports all render functions and createHtmlFormatter.\n */\n\nimport type { DocEntry } from \"../../../types/story\";\nimport type { TestRunResult } from \"../../../types/test-result\";\nimport { escapeHtml, generateHtmlTemplate } from \"../template\";\nimport { CSS_STYLES } from \"../styles\";\nimport { getStatusIcon } from \"./status\";\nimport { renderMetaInfo } from \"./meta\";\nimport { renderSummary } from \"./summary\";\nimport { renderErrorBox } from \"./error-box\";\nimport { renderAttachments } from \"./attachments\";\nimport { renderDocEntry } from \"./doc-entries\";\nimport { renderSteps } from \"./steps\";\nimport { renderScenario } from \"./scenario\";\nimport { renderFeature } from \"./feature\";\nimport { buildBody } from \"./body\";\n\n/** Options for HTML formatting (subset used by createHtmlFormatter) */\nexport interface HtmlFormatterOptions {\n title?: string;\n darkMode?: boolean;\n searchable?: boolean;\n startCollapsed?: boolean;\n embedScreenshots?: boolean;\n syntaxHighlighting?: boolean;\n mermaidEnabled?: boolean;\n markdownEnabled?: boolean;\n}\n\nfunction normalizeOptions(options: HtmlFormatterOptions = {}) {\n return {\n title: options.title ?? \"Test Results\",\n darkMode: options.darkMode ?? true,\n searchable: options.searchable ?? true,\n startCollapsed: options.startCollapsed ?? false,\n embedScreenshots: options.embedScreenshots ?? true,\n syntaxHighlighting: options.syntaxHighlighting ?? true,\n mermaidEnabled: options.mermaidEnabled ?? true,\n markdownEnabled: options.markdownEnabled ?? true,\n };\n}\n\n/**\n * Factory: wire deps once, return { format(run) }.\n */\nexport function createHtmlFormatter(\n options: HtmlFormatterOptions = {},\n): { format(run: TestRunResult): string } {\n const opts = normalizeOptions(options);\n\n const docEntryDeps = {\n escapeHtml,\n syntaxHighlighting: opts.syntaxHighlighting,\n markdownEnabled: opts.markdownEnabled,\n mermaidEnabled: opts.mermaidEnabled,\n };\n\n const renderDocs = (\n docs: DocEntry[] | undefined,\n containerClass: string,\n ): string => {\n if (!docs || docs.length === 0) return \"\";\n const entries = docs.map((entry) => renderDocEntry(entry, docEntryDeps)).join(\"\");\n return `<div class=\"${containerClass}\">${entries}</div>`;\n };\n\n const stepsDeps = {\n escapeHtml,\n getStatusIcon,\n renderDocs,\n };\n\n const scenarioDeps = {\n escapeHtml,\n getStatusIcon,\n startCollapsed: opts.startCollapsed,\n renderSteps: (args: import(\"./steps.js\").RenderStepsArgs) =>\n renderSteps(args, stepsDeps),\n renderDocs,\n renderErrorBox: (\n args: import(\"./error-box.js\").RenderErrorBoxArgs,\n d: import(\"./error-box.js\").RenderErrorBoxDeps,\n ) => renderErrorBox(args, d),\n renderAttachments: (\n args: import(\"./attachments.js\").RenderAttachmentsArgs,\n d: import(\"./attachments.js\").RenderAttachmentsDeps,\n ) => renderAttachments(args, d),\n embedScreenshots: opts.embedScreenshots,\n };\n\n const featureDeps = {\n escapeHtml,\n startCollapsed: opts.startCollapsed,\n renderScenario: (args: import(\"./scenario.js\").RenderScenarioArgs) =>\n renderScenario(args, scenarioDeps),\n scenarioDeps,\n };\n\n const bodyDeps = {\n renderMetaInfo,\n renderSummary,\n renderFeature,\n metaDeps: { escapeHtml },\n summaryDeps: {},\n featureDeps,\n };\n\n return {\n format(run: TestRunResult): string {\n const body = buildBody({ run }, bodyDeps);\n return generateHtmlTemplate(\n opts.title,\n CSS_STYLES,\n body,\n {\n includeSearch: opts.searchable,\n includeDarkMode: opts.darkMode,\n syntaxHighlighting: opts.syntaxHighlighting,\n mermaidEnabled: opts.mermaidEnabled,\n markdownEnabled: opts.markdownEnabled,\n },\n );\n },\n };\n}\n\nexport { renderMetaInfo } from \"./meta\";\nexport { renderSummary } from \"./summary\";\nexport { renderErrorBox } from \"./error-box\";\nexport { renderAttachments } from \"./attachments\";\nexport {\n renderDocEntry,\n renderDocNote,\n renderDocTag,\n renderDocKv,\n renderDocCode,\n renderDocTable,\n renderDocLink,\n renderDocSection,\n renderDocMermaid,\n renderDocScreenshot,\n renderDocCustom,\n} from \"./doc-entries\";\nexport { renderSteps, renderStep } from \"./steps\";\nexport { renderScenario } from \"./scenario\";\nexport { renderFeature } from \"./feature\";\nexport { buildBody } from \"./body\";\nexport { getStatusIcon } from \"./status\";\nexport type { DocEntryDeps } from \"./doc-entries\";\nexport type { RenderMetaInfoArgs, RenderMetaInfoDeps } from \"./meta\";\nexport type { RenderSummaryArgs, RenderSummaryDeps } from \"./summary\";\nexport type { RenderErrorBoxArgs, RenderErrorBoxDeps } from \"./error-box\";\nexport type { RenderAttachmentsArgs, RenderAttachmentsDeps } from \"./attachments\";\nexport type { RenderStepsArgs, RenderStepsDeps } from \"./steps\";\nexport type { RenderScenarioArgs, RenderScenarioDeps } from \"./scenario\";\nexport type { RenderFeatureArgs, RenderFeatureDeps } from \"./feature\";\nexport type { BuildBodyArgs, BuildBodyDeps } from \"./body\";\n","/**\n * HTML Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into a standalone HTML report.\n * Implemented via createHtmlFormatter (fn(args, deps) pattern).\n */\n\nimport type { TestRunResult } from \"../../types/test-result\";\nimport { createHtmlFormatter } from \"./renderers/index\";\n\n/** Options for HTML formatting */\nexport interface HtmlOptions {\n /** Report title. Default: \"Test Results\" */\n title?: string;\n /** Include dark mode toggle. Default: true */\n darkMode?: boolean;\n /** Include search/filter functionality. Default: true */\n searchable?: boolean;\n /** Start scenarios collapsed. Default: false */\n startCollapsed?: boolean;\n /** Embed screenshots inline (base64). Default: true */\n embedScreenshots?: boolean;\n /** Enable syntax highlighting for code blocks (via highlight.js CDN). Default: true */\n syntaxHighlighting?: boolean;\n /** Enable live Mermaid diagram rendering (via Mermaid.js CDN). Default: true */\n mermaidEnabled?: boolean;\n /** Enable Markdown parsing for section doc entries (via marked.js CDN). Default: true */\n markdownEnabled?: boolean;\n}\n\n/**\n * HTML Formatter.\n *\n * Transforms TestRunResult into a standalone HTML report with:\n * - Dark/light mode toggle\n * - Search/filter functionality\n * - Collapsible features and scenarios\n * - Modern, accessible design\n *\n * Thin wrapper around createHtmlFormatter for backward compatibility.\n */\nexport class HtmlFormatter {\n private formatFn: (run: TestRunResult) => string;\n\n constructor(options: HtmlOptions = {}) {\n const wired = createHtmlFormatter(options);\n this.formatFn = wired.format.bind(wired);\n }\n\n /**\n * Format a test run into standalone HTML.\n *\n * @param run - Canonical test run result\n * @returns HTML string\n */\n format(run: TestRunResult): string {\n return this.formatFn(run);\n }\n}\n\nexport { createHtmlFormatter } from \"./renderers/index\";\nexport type { HtmlFormatterOptions } from \"./renderers/index\";\nexport { escapeHtml, generateHtmlTemplate } from \"./template\";\nexport { CSS_STYLES } from \"./styles\";\nexport {\n renderMetaInfo,\n renderSummary,\n renderErrorBox,\n renderAttachments,\n renderDocEntry,\n renderDocNote,\n renderDocTag,\n renderDocKv,\n renderDocCode,\n renderDocTable,\n renderDocLink,\n renderDocSection,\n renderDocMermaid,\n renderDocScreenshot,\n renderDocCustom,\n renderSteps,\n renderStep,\n renderScenario,\n renderFeature,\n buildBody,\n getStatusIcon,\n} from \"./renderers/index\";\n","/**\n * JUnit XML Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into JUnit XML format\n * for CI system integration.\n */\n\nimport type { DocEntry, StoryStep } from \"../types/story\";\nimport type { TestRunResult, TestCaseResult } from \"../types/test-result\";\n\n/** Options for JUnit XML formatting */\nexport interface JUnitOptions {\n /** Test suite name. Default: \"Test Suite\" */\n suiteName?: string;\n /** Include system-out/system-err. Default: true */\n includeOutput?: boolean;\n /** Pretty-print XML output. Default: true */\n pretty?: boolean;\n}\n\n/**\n * JUnit XML Formatter.\n *\n * Transforms TestRunResult into JUnit XML format for CI integrations.\n * Compatible with Jenkins, GitHub Actions, and other CI systems.\n */\nexport class JUnitFormatter {\n private options: Required<JUnitOptions>;\n\n constructor(options: JUnitOptions = {}) {\n this.options = {\n suiteName: options.suiteName ?? \"Test Suite\",\n includeOutput: options.includeOutput ?? true,\n pretty: options.pretty ?? true,\n };\n }\n\n /**\n * Format a test run into JUnit XML.\n *\n * @param run - Canonical test run result\n * @returns JUnit XML string\n */\n format(run: TestRunResult): string {\n const indent = this.options.pretty ? \" \" : \"\";\n const newline = this.options.pretty ? \"\\n\" : \"\";\n\n // Calculate totals\n const tests = run.testCases.length;\n const failures = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\"\n ).length;\n const errors = 0; // We don't distinguish errors from failures\n const time = (run.durationMs / 1000).toFixed(3);\n\n // Build XML\n const lines: string[] = [];\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n lines.push(\n `<testsuites name=\"${escapeXml(this.options.suiteName)}\" tests=\"${tests}\" failures=\"${failures}\" errors=\"${errors}\" skipped=\"${skipped}\" time=\"${time}\">`\n );\n\n // Group test cases by source file (one testsuite per file)\n const byFile = groupBy(run.testCases, (tc) => tc.sourceFile);\n\n for (const [file, testCases] of byFile) {\n lines.push(...this.buildTestSuite(file, testCases, indent, newline));\n }\n\n lines.push(\"</testsuites>\");\n\n return lines.join(newline);\n }\n\n /**\n * Build a testsuite element for a file.\n */\n private buildTestSuite(\n file: string,\n testCases: TestCaseResult[],\n indent: string,\n newline: string\n ): string[] {\n const lines: string[] = [];\n\n const tests = testCases.length;\n const failures = testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = testCases.filter(\n (tc) => tc.status === \"skipped\" || tc.status === \"pending\"\n ).length;\n const time = testCases\n .reduce((sum, tc) => sum + tc.durationMs, 0) / 1000;\n\n // Normalize path separators for consistent output across platforms\n const normalizedFile = file.replace(/\\\\/g, \"/\");\n\n lines.push(\n `${indent}<testsuite name=\"${escapeXml(normalizedFile)}\" tests=\"${tests}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\" time=\"${time.toFixed(3)}\">`\n );\n\n for (const tc of testCases) {\n lines.push(...this.buildTestCase(tc, indent + indent, newline));\n }\n\n lines.push(`${indent}</testsuite>`);\n\n return lines;\n }\n\n /**\n * Build a testcase element.\n */\n private buildTestCase(\n tc: TestCaseResult,\n indent: string,\n newline: string\n ): string[] {\n const lines: string[] = [];\n\n // Build classname from titlePath or sourceFile\n // Normalize path separators (Windows backslashes and Unix forward slashes)\n const classname = tc.titlePath.length > 0\n ? tc.titlePath.join(\".\")\n : tc.sourceFile\n .replace(/[\\\\/]+/g, \".\") // Replace path separators with dots\n .replace(/\\.[^.]+$/, \"\"); // Remove file extension\n\n const name = tc.story.scenario;\n const time = (tc.durationMs / 1000).toFixed(3);\n\n const hasFailure = tc.status === \"failed\";\n const hasSkipped = tc.status === \"skipped\" || tc.status === \"pending\";\n const hasOutput = this.options.includeOutput && tc.story.steps.length > 0;\n\n // Use full form if there's any content to include\n if (hasFailure || hasSkipped || hasOutput) {\n lines.push(\n `${indent}<testcase classname=\"${escapeXml(classname)}\" name=\"${escapeXml(name)}\" time=\"${time}\">`\n );\n\n if (hasFailure) {\n const message = tc.errorMessage\n ? escapeXml(tc.errorMessage.split(\"\\n\")[0])\n : \"Test failed\";\n lines.push(`${indent}${indent}<failure message=\"${message}\">`);\n if (tc.errorMessage) {\n lines.push(escapeXml(tc.errorMessage));\n }\n if (tc.errorStack) {\n lines.push(\"\");\n lines.push(escapeXml(tc.errorStack));\n }\n lines.push(`${indent}${indent}</failure>`);\n } else if (hasSkipped) {\n const message = tc.status === \"pending\" ? \"Test pending\" : \"Test skipped\";\n lines.push(`${indent}${indent}<skipped message=\"${message}\"/>`);\n }\n\n // Include system-out with step info and docs if requested\n if (hasOutput) {\n const output = this.buildSystemOut(tc);\n lines.push(`${indent}${indent}<system-out>${escapeXml(output)}</system-out>`);\n }\n\n lines.push(`${indent}</testcase>`);\n } else {\n // Self-closing tag for tests without content\n lines.push(\n `${indent}<testcase classname=\"${escapeXml(classname)}\" name=\"${escapeXml(name)}\" time=\"${time}\"/>`\n );\n }\n\n return lines;\n }\n\n /**\n * Build system-out content with steps and docs.\n */\n private buildSystemOut(tc: TestCaseResult): string {\n const outputLines: string[] = [];\n\n // Story-level docs\n if (tc.story.docs && tc.story.docs.length > 0) {\n for (const doc of tc.story.docs) {\n outputLines.push(this.renderDocEntry(doc));\n }\n outputLines.push(\"\");\n }\n\n // Steps with their docs\n for (const step of tc.story.steps) {\n outputLines.push(this.renderStep(step));\n }\n\n return outputLines.join(\"\\n\").trim();\n }\n\n /**\n * Render a step with its docs.\n */\n private renderStep(step: StoryStep): string {\n const lines: string[] = [];\n lines.push(`${step.keyword} ${step.text}`);\n\n // Step docs\n if (step.docs && step.docs.length > 0) {\n for (const doc of step.docs) {\n const rendered = this.renderDocEntry(doc, \" \");\n if (rendered) {\n lines.push(rendered);\n }\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Render a doc entry as plain text.\n */\n private renderDocEntry(entry: DocEntry, indent = \"\"): string {\n switch (entry.kind) {\n case \"note\":\n return `${indent}> ${entry.text}`;\n\n case \"tag\":\n return `${indent}Tags: ${entry.names.join(\", \")}`;\n\n case \"kv\": {\n const val = typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value);\n return `${indent}${entry.label}: ${val}`;\n }\n\n case \"code\": {\n const langLabel = entry.lang ? ` (${entry.lang})` : \"\";\n const header = entry.label ? `${indent}${entry.label}${langLabel}:\\n` : \"\";\n const codeLines = entry.content.split(\"\\n\").map((l) => `${indent} ${l}`).join(\"\\n\");\n return `${header}${codeLines}`;\n }\n\n case \"table\": {\n const lines: string[] = [];\n if (entry.label) {\n lines.push(`${indent}${entry.label}:`);\n }\n lines.push(`${indent}| ${entry.columns.join(\" | \")} |`);\n lines.push(`${indent}| ${entry.columns.map(() => \"---\").join(\" | \")} |`);\n for (const row of entry.rows) {\n lines.push(`${indent}| ${row.join(\" | \")} |`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"link\":\n return `${indent}${entry.label}: ${entry.url}`;\n\n case \"section\": {\n const lines: string[] = [];\n lines.push(`${indent}${entry.title}:`);\n for (const line of entry.markdown.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"mermaid\": {\n const lines: string[] = [];\n if (entry.title) {\n lines.push(`${indent}${entry.title}:`);\n }\n for (const line of entry.code.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n case \"screenshot\":\n return `${indent}Screenshot: ${entry.alt ?? entry.path}`;\n\n case \"custom\": {\n const dataStr = JSON.stringify(entry.data, null, 2);\n const lines: string[] = [];\n lines.push(`${indent}[${entry.type}]:`);\n for (const line of dataStr.split(\"\\n\")) {\n lines.push(`${indent} ${line}`);\n }\n return lines.join(\"\\n\");\n }\n\n default:\n return \"\";\n }\n }\n}\n\n/**\n * Escape special XML characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\n/**\n * Group array items by a key function.\n */\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n","/**\n * Markdown Formatter - Layer 3.\n *\n * Transforms canonical TestRunResult into Markdown documentation.\n * Compatible with existing markdown output from framework reporters.\n */\n\nimport type { StoryStep, DocEntry } from \"../types/story\";\nimport type { TestRunResult, TestCaseResult, TestStatus } from \"../types/test-result\";\nimport type { MarkdownRenderers } from \"../types/options\";\n\n/** Options for Markdown formatting */\nexport interface MarkdownOptions {\n /** Report title. Default: \"User Stories\" */\n title?: string;\n /** Include status icons on scenarios. Default: true */\n includeStatusIcons?: boolean;\n /** Include metadata table (date, version). Default: true */\n includeMetadata?: boolean;\n /** Include error details for failed scenarios. Default: true */\n includeErrors?: boolean;\n /** Scenario heading level. Default: 3 */\n scenarioHeadingLevel?: 2 | 3 | 4;\n /** Step rendering style. Default: \"bullets\" */\n stepStyle?: \"bullets\" | \"gherkin\";\n /** Group scenarios by. Default: \"file\" */\n groupBy?: \"file\" | \"suite\" | \"none\";\n /** Sort scenarios. Default: \"source\" */\n sortScenarios?: \"alpha\" | \"source\" | \"none\";\n /** Suite path separator. Default: \" - \" */\n suiteSeparator?: string;\n /** Include YAML front-matter for machine parsing. Default: false */\n includeFrontMatter?: boolean;\n /** Include summary table (counts, duration). Default: false */\n includeSummaryTable?: boolean;\n /** Base URL for source permalinks. E.g., \"https://github.com/user/repo/blob\" */\n permalinkBaseUrl?: string;\n /** URL template for ticket links. Use {ticket} as placeholder */\n ticketUrlTemplate?: string;\n /** Include source links when permalinkBaseUrl is set. Default: true */\n includeSourceLinks?: boolean;\n /** Custom renderers for doc entries */\n customRenderers?: MarkdownRenderers;\n}\n\n/** Resolved options with all defaults */\ntype ResolvedMarkdownOptions = {\n title: string;\n includeStatusIcons: boolean;\n includeMetadata: boolean;\n includeErrors: boolean;\n scenarioHeadingLevel: 2 | 3 | 4;\n stepStyle: \"bullets\" | \"gherkin\";\n groupBy: \"file\" | \"suite\" | \"none\";\n sortScenarios: \"alpha\" | \"source\" | \"none\";\n suiteSeparator: string;\n includeFrontMatter: boolean;\n includeSummaryTable: boolean;\n permalinkBaseUrl?: string;\n ticketUrlTemplate?: string;\n includeSourceLinks: boolean;\n customRenderers?: MarkdownRenderers;\n};\n\n/**\n * Markdown Formatter.\n *\n * Transforms TestRunResult into Markdown documentation that matches\n * the output format of existing framework reporters.\n */\nexport class MarkdownFormatter {\n private options: ResolvedMarkdownOptions;\n\n constructor(options: MarkdownOptions = {}) {\n this.options = {\n title: options.title ?? \"User Stories\",\n includeStatusIcons: options.includeStatusIcons ?? true,\n includeMetadata: options.includeMetadata ?? true,\n includeErrors: options.includeErrors ?? true,\n scenarioHeadingLevel: options.scenarioHeadingLevel ?? 3,\n stepStyle: options.stepStyle ?? \"bullets\",\n groupBy: options.groupBy ?? \"file\",\n sortScenarios: options.sortScenarios ?? \"source\",\n suiteSeparator: options.suiteSeparator ?? \" - \",\n includeFrontMatter: options.includeFrontMatter ?? false,\n includeSummaryTable: options.includeSummaryTable ?? false,\n permalinkBaseUrl: options.permalinkBaseUrl,\n ticketUrlTemplate: options.ticketUrlTemplate,\n includeSourceLinks: options.includeSourceLinks ?? true,\n customRenderers: options.customRenderers,\n };\n }\n\n /**\n * Format a test run into Markdown.\n *\n * @param run - Canonical test run result\n * @returns Markdown string\n */\n format(run: TestRunResult): string {\n const lines: string[] = [];\n\n // Front-matter\n if (this.options.includeFrontMatter) {\n this.renderFrontMatter(lines, run);\n }\n\n // Title\n lines.push(`# ${this.options.title}`);\n lines.push(\"\");\n\n // Metadata\n if (this.options.includeMetadata) {\n this.renderMetadata(lines, run);\n lines.push(\"\");\n }\n\n // Summary table\n if (this.options.includeSummaryTable) {\n this.renderSummaryTable(lines, run);\n lines.push(\"\");\n }\n\n // Render scenarios based on grouping\n switch (this.options.groupBy) {\n case \"none\":\n this.renderFlatList(lines, run.testCases);\n break;\n case \"suite\":\n this.renderBySuite(lines, run.testCases);\n break;\n case \"file\":\n default:\n this.renderByFile(lines, run.testCases);\n break;\n }\n\n // Custom footer\n if (this.options.customRenderers?.renderFooter) {\n const footer = this.options.customRenderers.renderFooter(run);\n if (footer) {\n lines.push(\"\");\n lines.push(footer);\n }\n }\n\n return lines.join(\"\\n\").trimEnd();\n }\n\n /**\n * Render YAML front-matter.\n */\n private renderFrontMatter(lines: string[], run: TestRunResult): void {\n const data: Record<string, unknown> = {\n title: this.options.title,\n generatedAt: new Date(run.startedAtMs).toISOString(),\n durationMs: run.durationMs,\n scenarios: run.testCases.length,\n passed: run.testCases.filter((tc) => tc.status === \"passed\").length,\n failed: run.testCases.filter((tc) => tc.status === \"failed\").length,\n skipped: run.testCases.filter((tc) => tc.status === \"skipped\").length,\n pending: run.testCases.filter((tc) => tc.status === \"pending\").length,\n };\n\n if (run.packageVersion) data.version = run.packageVersion;\n if (run.gitSha) data.gitSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;\n if (run.coverage) data.coverage = run.coverage;\n\n lines.push(\"---\");\n for (const [key, value] of Object.entries(data)) {\n if (value === undefined) continue;\n if (typeof value === \"object\") {\n lines.push(`${key}:`);\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n lines.push(` ${k}: ${v}`);\n }\n } else {\n lines.push(`${key}: ${value}`);\n }\n }\n lines.push(\"---\");\n lines.push(\"\");\n }\n\n /**\n * Render summary table.\n */\n private renderSummaryTable(lines: string[], run: TestRunResult): void {\n const totalScenarios = run.testCases.length;\n const totalSteps = run.testCases.reduce((acc, tc) => acc + tc.story.steps.length, 0);\n const passed = run.testCases.filter((tc) => tc.status === \"passed\").length;\n const failed = run.testCases.filter((tc) => tc.status === \"failed\").length;\n const skipped = run.testCases.filter((tc) => tc.status === \"skipped\").length;\n const pending = run.testCases.filter((tc) => tc.status === \"pending\").length;\n\n lines.push(\"| Scenarios | Steps | Passed | Failed | Skipped | Pending | Duration |\");\n lines.push(\"| ---: | ---: | ---: | ---: | ---: | ---: | ---: |\");\n lines.push(`| ${totalScenarios} | ${totalSteps} | ${passed} | ${failed} | ${skipped} | ${pending} | ${this.formatDuration(run.durationMs)} |`);\n\n // Coverage summary if available\n if (run.coverage) {\n lines.push(\"\");\n lines.push(\"| Coverage | % |\");\n lines.push(\"| --- | ---: |\");\n if (run.coverage.statementsPct !== undefined) {\n lines.push(`| Statements | ${run.coverage.statementsPct}% |`);\n }\n if (run.coverage.branchesPct !== undefined) {\n lines.push(`| Branches | ${run.coverage.branchesPct}% |`);\n }\n if (run.coverage.functionsPct !== undefined) {\n lines.push(`| Functions | ${run.coverage.functionsPct}% |`);\n }\n if (run.coverage.linesPct !== undefined) {\n lines.push(`| Lines | ${run.coverage.linesPct}% |`);\n }\n }\n }\n\n /**\n * Format duration in human-readable form.\n */\n private formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n return `${(ms / 1000).toFixed(2)}s`;\n }\n\n /**\n * Render metadata table.\n */\n private renderMetadata(lines: string[], run: TestRunResult): void {\n const rows: Array<[string, string]> = [];\n\n const startDate = new Date(run.startedAtMs);\n rows.push([\"Date\", startDate.toISOString()]);\n\n if (run.packageVersion) {\n rows.push([\"Version\", run.packageVersion]);\n }\n\n if (run.gitSha) {\n const shortSha = run.gitSha.length > 7 ? run.gitSha.slice(0, 7) : run.gitSha;\n rows.push([\"Git SHA\", shortSha]);\n }\n\n if (rows.length > 0) {\n lines.push(\"| Key | Value |\");\n lines.push(\"| --- | --- |\");\n for (const [key, value] of rows) {\n lines.push(`| ${key} | ${value} |`);\n }\n }\n }\n\n /**\n * Render scenarios grouped by file.\n */\n private renderByFile(lines: string[], testCases: TestCaseResult[]): void {\n const byFile = groupBy(testCases, (tc) => tc.sourceFile);\n\n for (const [file, fileTestCases] of byFile) {\n lines.push(`## ${file}`);\n lines.push(\"\");\n\n // Group by suite path within file\n this.renderSuiteGroups(lines, fileTestCases, 3);\n }\n }\n\n /**\n * Render scenarios grouped by suite path.\n */\n private renderBySuite(lines: string[], testCases: TestCaseResult[]): void {\n this.renderSuiteGroups(lines, testCases, 2);\n }\n\n /**\n * Render suite groups.\n */\n private renderSuiteGroups(\n lines: string[],\n testCases: TestCaseResult[],\n baseLevel: number\n ): void {\n const bySuite = groupBy(testCases, (tc) =>\n tc.titlePath.join(this.options.suiteSeparator)\n );\n\n // Sort suite groups\n const sortedSuites = this.sortSuiteGroups([...bySuite.entries()]);\n\n for (const [suitePath, suiteTestCases] of sortedSuites) {\n if (suitePath) {\n lines.push(`${\"#\".repeat(baseLevel)} ${suitePath}`);\n lines.push(\"\");\n }\n\n const sorted = this.sortScenarios(suiteTestCases);\n for (const tc of sorted) {\n this.renderScenario(lines, tc);\n }\n }\n }\n\n /**\n * Render flat list of scenarios.\n */\n private renderFlatList(lines: string[], testCases: TestCaseResult[]): void {\n const sorted = this.sortScenarios(testCases);\n for (const tc of sorted) {\n this.renderScenario(lines, tc);\n }\n }\n\n /**\n * Render a single scenario.\n */\n private renderScenario(lines: string[], tc: TestCaseResult): void {\n // Check for custom scenario header renderer\n if (this.options.customRenderers?.renderScenarioHeader) {\n const custom = this.options.customRenderers.renderScenarioHeader(tc);\n if (custom !== null) {\n lines.push(custom);\n lines.push(\"\");\n // Still render steps and docs after custom header\n this.renderScenarioBody(lines, tc);\n return;\n }\n }\n\n const headingPrefix = \"#\".repeat(this.options.scenarioHeadingLevel);\n\n // Status icon\n let icon = \"\";\n if (this.options.includeStatusIcons) {\n icon = this.getStatusIcon(tc.status) + \" \";\n }\n\n // Scenario heading\n lines.push(`${headingPrefix} ${icon}${tc.story.scenario}`);\n\n // Source link\n if (this.options.includeSourceLinks && this.options.permalinkBaseUrl && tc.sourceFile !== \"unknown\") {\n const permalink = this.buildPermalink(tc);\n lines.push(`Source: [${tc.sourceFile}](${permalink})`);\n }\n\n // Tags and tickets\n const meta: string[] = [];\n if (tc.tags.length > 0) {\n meta.push(`Tags: ${tc.tags.map((t) => `\\`${t}\\``).join(\", \")}`);\n }\n if (tc.story.tickets && tc.story.tickets.length > 0) {\n const ticketTemplate = this.options.ticketUrlTemplate;\n if (ticketTemplate) {\n const ticketLinks = tc.story.tickets.map(\n (t) => `[${t}](${ticketTemplate.replace(\"{ticket}\", t)})`\n );\n meta.push(`Tickets: ${ticketLinks.join(\", \")}`);\n } else {\n meta.push(`Tickets: ${tc.story.tickets.map((t) => `\\`${t}\\``).join(\", \")}`);\n }\n }\n if (meta.length > 0) {\n lines.push(meta.join(\" | \"));\n }\n\n lines.push(\"\");\n\n this.renderScenarioBody(lines, tc);\n }\n\n /**\n * Render scenario body (docs, steps, errors).\n */\n private renderScenarioBody(lines: string[], tc: TestCaseResult): void {\n // Story-level docs\n if (tc.story.docs && tc.story.docs.length > 0) {\n for (const doc of tc.story.docs) {\n this.renderDocEntry(lines, doc);\n }\n }\n\n // Steps\n for (const step of tc.story.steps) {\n this.renderStep(lines, step);\n }\n\n // Error\n if (tc.status === \"failed\" && tc.errorMessage && this.options.includeErrors) {\n lines.push(\"**Failure**\");\n lines.push(\"\");\n lines.push(\"```text\");\n lines.push(tc.errorMessage);\n if (tc.errorStack) {\n lines.push(\"\");\n lines.push(tc.errorStack);\n }\n lines.push(\"```\");\n lines.push(\"\");\n }\n\n lines.push(\"\");\n }\n\n /**\n * Build permalink URL for a test case.\n */\n private buildPermalink(tc: TestCaseResult): string {\n const base = this.options.permalinkBaseUrl!.replace(/\\/$/, \"\");\n const file = tc.sourceFile;\n const line = tc.sourceLine > 0 ? `#L${tc.sourceLine}` : \"\";\n return `${base}/${file}${line}`;\n }\n\n /**\n * Render a step.\n */\n private renderStep(lines: string[], step: StoryStep): void {\n // Check for custom step renderer\n if (this.options.customRenderers?.renderStep) {\n const custom = this.options.customRenderers.renderStep(step);\n if (custom !== null) {\n lines.push(custom);\n // Still render step docs\n if (step.docs && step.docs.length > 0) {\n const indent = this.options.stepStyle === \"gherkin\" ? \"\" : \" \";\n for (const doc of step.docs) {\n this.renderDocEntry(lines, doc, indent);\n }\n }\n return;\n }\n }\n\n // Mode indicator\n let modeIndicator = \"\";\n if (step.mode === \"skip\") {\n modeIndicator = \" _(skipped)_\";\n } else if (step.mode === \"todo\") {\n modeIndicator = \" _(todo)_\";\n } else if (step.mode === \"fails\") {\n modeIndicator = \" _(expected to fail)_\";\n }\n\n if (this.options.stepStyle === \"gherkin\") {\n lines.push(`**${step.keyword}** ${step.text}${modeIndicator}`);\n } else {\n lines.push(`- **${step.keyword}** ${step.text}${modeIndicator}`);\n }\n\n // Render step docs\n if (step.docs && step.docs.length > 0) {\n const indent = this.options.stepStyle === \"gherkin\" ? \"\" : \" \";\n for (const doc of step.docs) {\n this.renderDocEntry(lines, doc, indent);\n }\n }\n }\n\n /**\n * Render a documentation entry.\n */\n private renderDocEntry(lines: string[], entry: DocEntry, indent = \"\"): void {\n // Check for custom doc entry renderer\n if (this.options.customRenderers?.renderDocEntry) {\n const custom = this.options.customRenderers.renderDocEntry(entry);\n if (custom !== null) {\n lines.push(`${indent}${custom}`);\n return;\n }\n }\n\n switch (entry.kind) {\n case \"note\":\n lines.push(`${indent}> ${entry.text}`);\n break;\n\n case \"tag\":\n lines.push(`${indent}${entry.names.map((n) => `\\`${n}\\``).join(\" \")}`);\n break;\n\n case \"kv\": {\n const val = typeof entry.value === \"string\"\n ? entry.value\n : JSON.stringify(entry.value);\n lines.push(`${indent}- **${entry.label}:** ${val}`);\n break;\n }\n\n case \"code\":\n if (entry.label) {\n lines.push(`${indent}**${entry.label}**`);\n lines.push(`${indent}`);\n }\n lines.push(`${indent}\\`\\`\\`${entry.lang ?? \"\"}`);\n for (const line of (entry.content ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push(`${indent}`);\n break;\n\n case \"table\":\n if (entry.label) {\n lines.push(`${indent}**${entry.label}**`);\n lines.push(`${indent}`);\n }\n lines.push(`${indent}| ${entry.columns.join(\" | \")} |`);\n lines.push(`${indent}| ${entry.columns.map(() => \"---\").join(\" | \")} |`);\n for (const row of entry.rows) {\n lines.push(`${indent}| ${row.join(\" | \")} |`);\n }\n lines.push(`${indent}`);\n break;\n\n case \"link\":\n lines.push(`${indent}[${entry.label}](${entry.url})`);\n break;\n\n case \"section\":\n lines.push(`${indent}**${entry.title}**`);\n lines.push(`${indent}`);\n for (const line of (entry.markdown ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}`);\n break;\n\n case \"mermaid\":\n if (entry.title) {\n lines.push(`${indent}**${entry.title}**`);\n }\n lines.push(`${indent}\\`\\`\\`mermaid`);\n for (const line of (entry.code ?? \"\").split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n break;\n\n case \"screenshot\":\n lines.push(`${indent}`);\n break;\n\n case \"custom\":\n lines.push(`${indent}**[${entry.type}]**`);\n lines.push(`${indent}`);\n lines.push(`${indent}\\`\\`\\`json`);\n for (const line of JSON.stringify(entry.data ?? null, null, 2).split(\"\\n\")) {\n lines.push(`${indent}${line}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push(`${indent}`);\n break;\n }\n }\n\n /**\n * Get status icon for a status.\n */\n private getStatusIcon(status: TestStatus): string {\n switch (status) {\n case \"passed\":\n return \"✅\";\n case \"failed\":\n return \"❌\";\n case \"skipped\":\n return \"⏩\";\n case \"pending\":\n return \"📝\";\n default:\n return \"⚠️\";\n }\n }\n\n /**\n * Sort scenarios based on options.\n */\n private sortScenarios(testCases: TestCaseResult[]): TestCaseResult[] {\n if (this.options.sortScenarios === \"alpha\") {\n return [...testCases].sort((a, b) =>\n a.story.scenario.localeCompare(b.story.scenario)\n );\n }\n if (this.options.sortScenarios === \"source\") {\n return [...testCases].sort(\n (a, b) => (a.story.sourceOrder ?? 0) - (b.story.sourceOrder ?? 0)\n );\n }\n return testCases;\n }\n\n /**\n * Sort suite groups.\n */\n private sortSuiteGroups(\n entries: [string, TestCaseResult[]][]\n ): [string, TestCaseResult[]][] {\n if (this.options.sortScenarios === \"alpha\") {\n return entries.sort(([a], [b]) => a.localeCompare(b));\n }\n if (this.options.sortScenarios === \"source\") {\n return entries.sort(([, a], [, b]) => {\n const minA = Math.min(...a.map((s) => s.story.sourceOrder ?? Infinity));\n const minB = Math.min(...b.map((s) => s.story.sourceOrder ?? Infinity));\n return minA - minB;\n });\n }\n return entries;\n }\n}\n\n/**\n * Group array items by a key function.\n */\nfunction groupBy<T, K>(items: T[], keyFn: (item: T) => K): Map<K, T[]> {\n const map = new Map<K, T[]>();\n for (const item of items) {\n const key = keyFn(item);\n const existing = map.get(key);\n if (existing) {\n existing.push(item);\n } else {\n map.set(key, [item]);\n }\n }\n return map;\n}\n","/**\n * Synthesize .feature file text and a line map from TestCaseResult[].\n *\n * Used to produce Source and GherkinDocument messages when real .feature\n * files don't exist (all test cases come from e.g. Jest/Vitest/Playwright).\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\n\n/** Map from scenario name → { scenarioLine, stepLines: Map<stepIndex, line> } */\nexport interface LineMap {\n featureLine: number;\n scenarios: Map<\n string,\n { scenarioLine: number; stepLines: Map<number, number> }\n >;\n /** Tag lines for each scenario, keyed by scenario name */\n scenarioTagLines: Map<string, number>;\n /** Feature-level tag line (if any) */\n featureTagLine?: number;\n}\n\nexport interface SynthesizedFeature {\n /** The synthesized .feature text */\n text: string;\n /** Line map for building the GherkinDocument AST */\n lineMap: LineMap;\n /** Extracted feature name */\n featureName: string;\n /** Feature-level tags (union of all scenario tags) */\n featureTags: string[];\n}\n\n/**\n * Extract a feature name from grouped test cases.\n *\n * Uses the first element of the first titlePath, or derives from the URI.\n */\nexport function extractFeatureName(\n testCases: TestCaseResult[],\n uri: string\n): string {\n for (const tc of testCases) {\n if (tc.titlePath.length > 0) {\n return tc.titlePath[0];\n }\n }\n // Fallback: derive from filename\n const basename = uri.replace(/^.*[\\\\/]/, \"\").replace(/\\.[^.]+$/, \"\");\n return basename\n .replace(/[-_]+/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Synthesize a .feature file from a group of test cases belonging to the same source file.\n */\nexport function synthesizeFeature(\n uri: string,\n testCases: TestCaseResult[]\n): SynthesizedFeature {\n const featureName = extractFeatureName(testCases, uri);\n\n // Collect feature-level tags (union of all scenario tags)\n const featureTagSet = new Set<string>();\n for (const tc of testCases) {\n for (const tag of tc.tags) {\n featureTagSet.add(tag);\n }\n }\n const featureTags = [...featureTagSet].sort();\n\n const lines: string[] = [];\n const lineMap: LineMap = {\n featureLine: 0,\n scenarios: new Map(),\n scenarioTagLines: new Map(),\n };\n\n let currentLine = 1;\n\n // Feature-level tags\n if (featureTags.length > 0) {\n lines.push(featureTags.map((t) => `@${t}`).join(\" \"));\n lineMap.featureTagLine = currentLine;\n currentLine++;\n }\n\n // Feature declaration\n lines.push(`Feature: ${featureName}`);\n lineMap.featureLine = currentLine;\n currentLine++;\n\n // Blank line after feature\n lines.push(\"\");\n currentLine++;\n\n for (const tc of testCases) {\n const scenario = tc.story.scenario;\n\n // Scenario tags\n if (tc.tags.length > 0) {\n lines.push(` ${tc.tags.map((t) => `@${t}`).join(\" \")}`);\n lineMap.scenarioTagLines.set(scenario, currentLine);\n currentLine++;\n }\n\n // Scenario declaration\n const scenarioLine = currentLine;\n lines.push(` Scenario: ${scenario}`);\n currentLine++;\n\n // Steps\n const stepLines = new Map<number, number>();\n for (let i = 0; i < tc.story.steps.length; i++) {\n const step = tc.story.steps[i];\n stepLines.set(i, currentLine);\n lines.push(` ${step.keyword} ${step.text}`);\n currentLine++;\n }\n\n lineMap.scenarios.set(scenario, { scenarioLine, stepLines });\n\n // Blank line between scenarios\n lines.push(\"\");\n currentLine++;\n }\n\n return {\n text: lines.join(\"\\n\"),\n lineMap,\n featureName,\n featureTags,\n };\n}\n","/**\n * Utility functions for Cucumber Messages NDJSON format.\n */\n\nimport { createHash } from \"node:crypto\";\nimport type {\n Timestamp,\n Duration,\n KeywordType,\n PickleStepType,\n TestStepResultStatus,\n} from \"../types/cucumber-messages\";\nimport type { TestStatus } from \"../types/test-result\";\nimport type { StepKeyword } from \"../types/story\";\n\n/**\n * Convert epoch milliseconds to protobuf Timestamp.\n */\nexport function msToTimestamp(ms: number): Timestamp {\n const seconds = Math.floor(ms / 1000);\n const nanos = Math.round((ms % 1000) * 1_000_000);\n return { seconds, nanos };\n}\n\n/**\n * Convert milliseconds to protobuf Duration.\n */\nexport function msToDuration(ms: number): Duration {\n const seconds = Math.floor(ms / 1000);\n const nanos = Math.round((ms % 1000) * 1_000_000);\n return { seconds, nanos };\n}\n\n/**\n * Map a StepKeyword to a Cucumber KeywordType.\n *\n * And/But/* inherit from the previous non-conjunction keyword type.\n */\nexport function keywordToKeywordType(keyword: StepKeyword): KeywordType {\n switch (keyword) {\n case \"Given\":\n return \"Context\";\n case \"When\":\n return \"Action\";\n case \"Then\":\n return \"Outcome\";\n case \"And\":\n case \"But\":\n return \"Conjunction\";\n default:\n return \"Unknown\";\n }\n}\n\n/**\n * Resolve the effective PickleStepType for a sequence of steps,\n * inheriting the previous non-conjunction type for And/But.\n *\n * Returns an array of resolved types, one per step.\n */\nexport function resolvePickleStepTypes(\n keywords: StepKeyword[]\n): PickleStepType[] {\n let lastNonConjunction: PickleStepType = \"Unknown\";\n return keywords.map((kw) => {\n const kt = keywordToKeywordType(kw);\n if (kt === \"Conjunction\") {\n return lastNonConjunction;\n }\n const resolved = keywordTypeToPickleStepType(kt);\n lastNonConjunction = resolved;\n return resolved;\n });\n}\n\n/**\n * Convert a non-conjunction KeywordType to PickleStepType.\n */\nfunction keywordTypeToPickleStepType(kt: KeywordType): PickleStepType {\n switch (kt) {\n case \"Context\":\n return \"Context\";\n case \"Action\":\n return \"Action\";\n case \"Outcome\":\n return \"Outcome\";\n default:\n return \"Unknown\";\n }\n}\n\n/**\n * Map TestStatus to Cucumber TestStepResultStatus.\n */\nexport function statusToCucumberStatus(\n status: TestStatus\n): TestStepResultStatus {\n switch (status) {\n case \"passed\":\n return \"PASSED\";\n case \"failed\":\n return \"FAILED\";\n case \"skipped\":\n return \"SKIPPED\";\n case \"pending\":\n return \"PENDING\";\n default:\n return \"UNKNOWN\";\n }\n}\n\n/**\n * Generate a deterministic ID using SHA-1.\n *\n * @param kind - Namespace to prevent collisions between entity types (e.g., \"pickle\", \"testCase\")\n * @param salt - Optional salt from options (idSalt)\n * @param parts - Strings to hash\n * @returns 36-character hex string (UUID-length without dashes)\n */\nexport function deterministicId(\n kind: string,\n salt: string,\n ...parts: string[]\n): string {\n const input = [salt, kind, ...parts].join(\"::\");\n return createHash(\"sha1\").update(input).digest(\"hex\").slice(0, 36);\n}\n","/**\n * Build a GherkinDocument envelope from synthesized feature data.\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\nimport type { DocEntry, StepKeyword, StoryStep } from \"../../types/story\";\nimport type {\n GherkinDocument,\n Feature,\n FeatureChild,\n Scenario,\n Step,\n Tag,\n Envelope,\n DocString,\n DataTable,\n TableRow,\n TableCell,\n} from \"../../types/cucumber-messages\";\nimport type { SynthesizedFeature } from \"./synthesize-feature\";\nimport { deterministicId, keywordToKeywordType } from \"../../utils/cucumber-messages\";\n\n/**\n * Build GherkinDocument + Source envelopes for a group of test cases from one file.\n */\nexport function buildGherkinDocumentEnvelopes(\n uri: string,\n testCases: TestCaseResult[],\n synthesized: SynthesizedFeature,\n salt: string\n): { sourceEnvelope: Envelope; gherkinDocumentEnvelope: Envelope } {\n const { lineMap, featureName, featureTags, text } = synthesized;\n\n // Build feature-level tags\n const featureTagNodes: Tag[] = featureTags.map((tag, i) => ({\n location: {\n line: lineMap.featureTagLine ?? 1,\n column: undefined,\n },\n name: `@${tag}`,\n id: deterministicId(\"featureTag\", salt, uri, tag),\n }));\n\n // Build children (scenarios)\n const children: FeatureChild[] = [];\n\n for (const tc of testCases) {\n const scenarioName = tc.story.scenario;\n const scenarioInfo = lineMap.scenarios.get(scenarioName);\n if (!scenarioInfo) continue;\n\n const scenarioId = deterministicId(\"scenario\", salt, uri, scenarioName);\n\n // Build scenario tags\n const scenarioTags: Tag[] = tc.tags.map((tag) => ({\n location: {\n line: lineMap.scenarioTagLines.get(scenarioName) ?? scenarioInfo.scenarioLine,\n },\n name: `@${tag}`,\n id: deterministicId(\"scenarioTag\", salt, uri, scenarioName, tag),\n }));\n\n // Build steps with keyword type tracking for And/But inheritance\n let lastNonConjunctionType: \"Context\" | \"Action\" | \"Outcome\" = \"Context\";\n const steps: Step[] = tc.story.steps.map((step, i) => {\n const keyword = step.keyword as StepKeyword;\n let kwType = keywordToKeywordType(keyword);\n\n if (kwType === \"Conjunction\") {\n kwType = lastNonConjunctionType;\n } else if (kwType === \"Context\" || kwType === \"Action\" || kwType === \"Outcome\") {\n lastNonConjunctionType = kwType;\n }\n\n const stepLine = scenarioInfo.stepLines.get(i) ?? 0;\n const astStep: Step = {\n location: { line: stepLine },\n keyword: `${keyword} `,\n keywordType: keywordToKeywordType(keyword),\n text: step.text,\n id: deterministicId(\"astStep\", salt, uri, scenarioName, String(i)),\n };\n\n // Convert doc entries to DocString/DataTable\n const { docString, dataTable } = buildStepArguments(step, stepLine);\n if (docString) astStep.docString = docString;\n if (dataTable) astStep.dataTable = dataTable;\n\n return astStep;\n });\n\n const scenario: Scenario = {\n location: { line: scenarioInfo.scenarioLine },\n tags: scenarioTags,\n keyword: \"Scenario\",\n name: scenarioName,\n description: \"\",\n steps,\n id: scenarioId,\n };\n\n children.push({ scenario });\n }\n\n const feature: Feature = {\n location: { line: lineMap.featureLine },\n tags: featureTagNodes,\n language: \"en\",\n keyword: \"Feature\",\n name: featureName,\n description: \"\",\n children,\n };\n\n const gherkinDocument: GherkinDocument = { uri, feature };\n\n return {\n sourceEnvelope: {\n source: {\n uri,\n data: text,\n mediaType: \"text/x.cucumber.gherkin+plain\",\n },\n },\n gherkinDocumentEnvelope: { gherkinDocument },\n };\n}\n\n/**\n * Convert step doc entries to DocString or DataTable for the GherkinDocument AST.\n *\n * Priority: first \"table\" doc → DataTable, else first \"code\"/\"note\"/\"section\"/\"mermaid\" → DocString.\n * Screenshots are handled as attachments, not arguments.\n */\nfunction buildStepArguments(\n step: StoryStep,\n stepLine: number\n): { docString?: DocString; dataTable?: DataTable } {\n if (!step.docs || step.docs.length === 0) return {};\n\n // Look for table first (takes priority)\n const tableDocs = step.docs.filter((d): d is Extract<DocEntry, { kind: \"table\" }> => d.kind === \"table\");\n if (tableDocs.length > 0) {\n const table = tableDocs[0];\n return { dataTable: buildDataTable(table, stepLine + 1) };\n }\n\n // Look for doc-string-like entries\n for (const doc of step.docs) {\n const ds = docEntryToDocString(doc, stepLine + 1);\n if (ds) return { docString: ds };\n }\n\n return {};\n}\n\nfunction docEntryToDocString(doc: DocEntry, line: number): DocString | undefined {\n switch (doc.kind) {\n case \"code\":\n return {\n location: { line },\n mediaType: doc.lang,\n content: doc.content,\n delimiter: '\"\"\"',\n };\n case \"note\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: doc.text,\n delimiter: '\"\"\"',\n };\n case \"section\":\n return {\n location: { line },\n mediaType: \"text/markdown\",\n content: doc.markdown,\n delimiter: '\"\"\"',\n };\n case \"mermaid\":\n return {\n location: { line },\n mediaType: \"text/x-mermaid\",\n content: doc.code,\n delimiter: '\"\"\"',\n };\n case \"kv\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: `${doc.label}: ${typeof doc.value === \"string\" ? doc.value : JSON.stringify(doc.value)}`,\n delimiter: '\"\"\"',\n };\n case \"link\":\n return {\n location: { line },\n mediaType: \"text/markdown\",\n content: `[${doc.label}](${doc.url})`,\n delimiter: '\"\"\"',\n };\n case \"custom\":\n return {\n location: { line },\n mediaType: \"application/json\",\n content: JSON.stringify(doc.data, null, 2),\n delimiter: '\"\"\"',\n };\n case \"tag\":\n return {\n location: { line },\n mediaType: \"text/plain\",\n content: doc.names.map((n) => `@${n}`).join(\" \"),\n delimiter: '\"\"\"',\n };\n // screenshot and other kinds are not converted to doc strings\n default:\n return undefined;\n }\n}\n\nfunction buildDataTable(\n table: Extract<DocEntry, { kind: \"table\" }>,\n line: number\n): DataTable {\n const rows: TableRow[] = [];\n\n // Header row\n rows.push({\n location: { line },\n cells: table.columns.map((col) => ({\n location: { line },\n value: col,\n })),\n id: \"\",\n });\n\n // Data rows\n for (let r = 0; r < table.rows.length; r++) {\n const rowLine = line + 1 + r;\n rows.push({\n location: { line: rowLine },\n cells: table.rows[r].map((cell) => ({\n location: { line: rowLine },\n value: cell,\n })),\n id: \"\",\n });\n }\n\n return {\n location: { line },\n rows,\n };\n}\n","/**\n * Build Pickle envelopes from test cases and GherkinDocument data.\n *\n * Each TestCaseResult becomes one Pickle (the compiled, runnable scenario).\n */\n\nimport type { TestCaseResult } from \"../../types/test-result\";\nimport type { DocEntry, StepKeyword, StoryStep } from \"../../types/story\";\nimport type {\n Pickle,\n PickleStep,\n PickleTag,\n PickleStepArgument,\n PickleDocString,\n PickleTable,\n PickleTableRow,\n Envelope,\n} from \"../../types/cucumber-messages\";\nimport {\n deterministicId,\n resolvePickleStepTypes,\n} from \"../../utils/cucumber-messages\";\n\n/**\n * Build Pickle envelopes for a group of test cases from one file.\n */\nexport function buildPickleEnvelopes(\n uri: string,\n testCases: TestCaseResult[],\n salt: string\n): Envelope[] {\n const envelopes: Envelope[] = [];\n\n for (const tc of testCases) {\n const scenarioName = tc.story.scenario;\n const pickleId = deterministicId(\"pickle\", salt, uri, scenarioName);\n const scenarioAstId = deterministicId(\"scenario\", salt, uri, scenarioName);\n\n // Resolve step types with And/But inheritance\n const keywords = tc.story.steps.map((s) => s.keyword as StepKeyword);\n const resolvedTypes = resolvePickleStepTypes(keywords);\n\n const pickleSteps: PickleStep[] = tc.story.steps.map((step, i) => {\n const ps: PickleStep = {\n astNodeIds: [\n deterministicId(\"astStep\", salt, uri, scenarioName, String(i)),\n ],\n id: deterministicId(\"pickleStep\", salt, uri, scenarioName, String(i)),\n type: resolvedTypes[i],\n text: step.text,\n };\n\n const argument = buildPickleStepArgument(step);\n if (argument) ps.argument = argument;\n\n return ps;\n });\n\n const pickleTags: PickleTag[] = tc.tags.map((tag) => ({\n name: `@${tag}`,\n astNodeId: deterministicId(\"scenarioTag\", salt, uri, scenarioName, tag),\n }));\n\n const pickle: Pickle = {\n id: pickleId,\n uri,\n name: scenarioName,\n language: \"en\",\n steps: pickleSteps,\n tags: pickleTags,\n astNodeIds: [scenarioAstId],\n };\n\n envelopes.push({ pickle });\n }\n\n return envelopes;\n}\n\n/**\n * Build a PickleStepArgument from step docs.\n * Priority: first table → PickleTable, else first doc-string-like → PickleDocString.\n */\nfunction buildPickleStepArgument(step: StoryStep): PickleStepArgument | undefined {\n if (!step.docs || step.docs.length === 0) return undefined;\n\n // Table takes priority\n const tableDocs = step.docs.filter(\n (d): d is Extract<DocEntry, { kind: \"table\" }> => d.kind === \"table\"\n );\n if (tableDocs.length > 0) {\n return { dataTable: buildPickleTable(tableDocs[0]) };\n }\n\n // Doc-string-like entries\n for (const doc of step.docs) {\n const ds = docEntryToPickleDocString(doc);\n if (ds) return { docString: ds };\n }\n\n return undefined;\n}\n\nfunction docEntryToPickleDocString(doc: DocEntry): PickleDocString | undefined {\n switch (doc.kind) {\n case \"code\":\n return { mediaType: doc.lang, content: doc.content };\n case \"note\":\n return { mediaType: \"text/plain\", content: doc.text };\n case \"section\":\n return { mediaType: \"text/markdown\", content: doc.markdown };\n case \"mermaid\":\n return { mediaType: \"text/x-mermaid\", content: doc.code };\n case \"kv\":\n return {\n mediaType: \"text/plain\",\n content: `${doc.label}: ${typeof doc.value === \"string\" ? doc.value : JSON.stringify(doc.value)}`,\n };\n case \"link\":\n return { mediaType: \"text/markdown\", content: `[${doc.label}](${doc.url})` };\n case \"custom\":\n return { mediaType: \"application/json\", content: JSON.stringify(doc.data, null, 2) };\n case \"tag\":\n return { mediaType: \"text/plain\", content: doc.names.map((n) => `@${n}`).join(\" \") };\n default:\n return undefined;\n }\n}\n\nfunction buildPickleTable(\n table: Extract<DocEntry, { kind: \"table\" }>\n): PickleTable {\n const rows: PickleTableRow[] = [];\n\n // Header row\n rows.push({\n cells: table.columns.map((col) => ({ value: col })),\n });\n\n // Data rows\n for (const row of table.rows) {\n rows.push({\n cells: row.map((cell) => ({ value: cell })),\n });\n }\n\n return { rows };\n}\n","/**\n * Build execution envelopes: TestCase, TestCaseStarted/Finished,\n * TestStepStarted/Finished, and Attachment messages.\n *\n * Attachment placement:\n * - Step-level doc screenshots → Attachment tied to that specific step\n * - Test-case level attachments → Attachment tied to failed step or last step\n * - All Attachment envelopes appear after the relevant TestStepFinished\n */\n\nimport type { TestCaseResult, TestRunResult } from \"../../types/test-result\";\nimport type { StoryStep } from \"../../types/story\";\nimport type {\n Envelope,\n TestCase,\n TestStep,\n TestCaseStarted,\n TestStepStarted,\n TestStepFinished,\n TestCaseFinished,\n TestRunStarted,\n TestRunFinished,\n CucumberAttachment,\n AttachmentContentEncoding,\n} from \"../../types/cucumber-messages\";\nimport {\n deterministicId,\n msToTimestamp,\n msToDuration,\n statusToCucumberStatus,\n} from \"../../utils/cucumber-messages\";\n\n/**\n * Build TestRunStarted envelope.\n */\nexport function buildTestRunStarted(run: TestRunResult): Envelope {\n return {\n testRunStarted: {\n timestamp: msToTimestamp(run.startedAtMs),\n },\n };\n}\n\n/**\n * Build TestRunFinished envelope.\n */\nexport function buildTestRunFinished(run: TestRunResult): Envelope {\n const allPassed = run.testCases.every((tc) => tc.status === \"passed\");\n return {\n testRunFinished: {\n timestamp: msToTimestamp(run.finishedAtMs),\n success: allPassed,\n },\n };\n}\n\n/**\n * Build all execution envelopes for a single test case:\n * TestCase, then for each attempt:\n * TestCaseStarted, [TestStepStarted + TestStepFinished + Attachment*]*, TestCaseFinished\n *\n * When tc.attempts exists with multiple entries, prior attempts are emitted\n * with willBeRetried=true, and the final attempt with willBeRetried=false.\n */\nexport function buildTestCaseExecutionEnvelopes(\n uri: string,\n tc: TestCaseResult,\n salt: string\n): Envelope[] {\n const envelopes: Envelope[] = [];\n const scenarioName = tc.story.scenario;\n\n const pickleId = deterministicId(\"pickle\", salt, uri, scenarioName);\n const testCaseId = deterministicId(\"testCase\", salt, uri, scenarioName);\n\n // Build test steps (shared across all attempts — the TestCase is the same)\n const testSteps: TestStep[] = tc.story.steps.map((_step, i) => ({\n id: deterministicId(\"testStep\", salt, uri, scenarioName, String(i)),\n pickleStepId: deterministicId(\n \"pickleStep\",\n salt,\n uri,\n scenarioName,\n String(i)\n ),\n stepDefinitionIds: [],\n }));\n\n // TestCase envelope (emitted once, before all attempts)\n const testCase: TestCase = {\n id: testCaseId,\n pickleId,\n testSteps,\n };\n envelopes.push({ testCase });\n\n // If there are explicit attempts, emit each one\n if (tc.attempts && tc.attempts.length > 1) {\n for (let a = 0; a < tc.attempts.length; a++) {\n const attempt = tc.attempts[a];\n const isLastAttempt = a === tc.attempts.length - 1;\n\n const attemptEnvelopes = buildAttemptEnvelopes({\n testCaseId,\n testSteps,\n tc,\n uri,\n scenarioName,\n salt,\n attemptNumber: attempt.attempt,\n attemptStatus: attempt.status,\n attemptDurationMs: attempt.durationMs,\n attemptErrorMessage: attempt.errorMessage,\n willBeRetried: !isLastAttempt,\n // Only emit attachments and doc screenshots on the final attempt\n emitAttachments: isLastAttempt,\n });\n envelopes.push(...attemptEnvelopes);\n }\n } else {\n // Single attempt (normal case)\n const attemptEnvelopes = buildAttemptEnvelopes({\n testCaseId,\n testSteps,\n tc,\n uri,\n scenarioName,\n salt,\n attemptNumber: tc.retry,\n attemptStatus: undefined, // Use tc.stepResults\n attemptDurationMs: undefined,\n attemptErrorMessage: undefined,\n willBeRetried: false,\n emitAttachments: true,\n });\n envelopes.push(...attemptEnvelopes);\n }\n\n return envelopes;\n}\n\ninterface AttemptParams {\n testCaseId: string;\n testSteps: TestStep[];\n tc: TestCaseResult;\n uri: string;\n scenarioName: string;\n salt: string;\n attemptNumber: number;\n attemptStatus: import(\"../../types/test-result.js\").TestStatus | undefined;\n attemptDurationMs: number | undefined;\n attemptErrorMessage: string | undefined;\n willBeRetried: boolean;\n emitAttachments: boolean;\n}\n\n/**\n * Build envelopes for a single attempt:\n * TestCaseStarted, [TestStepStarted, TestStepFinished, Attachment*]*, TestCaseFinished\n */\nfunction buildAttemptEnvelopes(params: AttemptParams): Envelope[] {\n const {\n testCaseId, testSteps, tc, uri, scenarioName, salt,\n attemptNumber, attemptStatus, attemptDurationMs, attemptErrorMessage,\n willBeRetried, emitAttachments,\n } = params;\n\n const envelopes: Envelope[] = [];\n const testCaseStartedId = deterministicId(\n \"testCaseStarted\",\n salt,\n uri,\n scenarioName,\n String(attemptNumber)\n );\n\n // TestCaseStarted\n envelopes.push({\n testCaseStarted: {\n id: testCaseStartedId,\n testCaseId,\n timestamp: msToTimestamp(0),\n attempt: attemptNumber,\n },\n });\n\n // Determine which step receives test-case-level attachments\n const tcAttachmentStepIndex = emitAttachments\n ? findTestCaseAttachmentStepIndex(tc)\n : -1;\n\n // Step execution envelopes\n let cumulativeMs = 0;\n\n for (let i = 0; i < testSteps.length; i++) {\n const testStep = testSteps[i];\n const storyStep = tc.story.steps[i];\n\n // Determine step status for this attempt\n let stepStatus: import(\"../../types/test-result.js\").TestStatus;\n let stepDurationMs: number;\n let stepErrorMessage: string | undefined;\n\n if (attemptStatus !== undefined) {\n // For prior attempts, derive step statuses from attempt-level status\n // (we don't have per-step data for prior attempts)\n stepStatus = attemptStatus === \"failed\" && i === tc.story.steps.length - 1\n ? \"failed\"\n : attemptStatus === \"failed\" && i < tc.story.steps.length - 1\n ? \"passed\"\n : attemptStatus;\n stepDurationMs = attemptDurationMs !== undefined\n ? attemptDurationMs / tc.story.steps.length\n : 0;\n stepErrorMessage = stepStatus === \"failed\" ? attemptErrorMessage : undefined;\n } else {\n // Final/only attempt: use actual step results\n const stepResult = tc.stepResults[i];\n stepStatus = stepResult?.status ?? \"passed\";\n stepDurationMs = stepResult?.durationMs ?? 0;\n // Include stack trace from test case if this is the failed step\n stepErrorMessage = stepResult?.errorMessage;\n if (stepStatus === \"failed\" && tc.errorStack && stepErrorMessage) {\n stepErrorMessage = stepErrorMessage + \"\\n\" + tc.errorStack;\n } else if (stepStatus === \"failed\" && tc.errorStack && !stepErrorMessage) {\n stepErrorMessage = tc.errorStack;\n }\n }\n\n // TestStepStarted\n envelopes.push({\n testStepStarted: {\n testCaseStartedId,\n testStepId: testStep.id,\n timestamp: msToTimestamp(cumulativeMs),\n },\n });\n\n // TestStepFinished\n cumulativeMs += stepDurationMs;\n envelopes.push({\n testStepFinished: {\n testCaseStartedId,\n testStepId: testStep.id,\n testStepResult: {\n duration: msToDuration(stepDurationMs),\n status: statusToCucumberStatus(stepStatus),\n message: stepErrorMessage,\n },\n timestamp: msToTimestamp(cumulativeMs),\n },\n });\n\n // Per-step doc screenshots (only on final attempt)\n if (emitAttachments) {\n const docAttachments = extractDocAttachments(storyStep);\n for (const att of docAttachments) {\n envelopes.push({\n attachment: {\n testCaseStartedId,\n testStepId: testStep.id,\n body: att.body,\n mediaType: att.mediaType,\n contentEncoding: att.contentEncoding,\n },\n });\n }\n }\n\n // Test-case level attachments (only on final attempt, on failed/last step)\n if (i === tcAttachmentStepIndex) {\n for (const att of tc.attachments) {\n envelopes.push({\n attachment: {\n testCaseStartedId,\n testStepId: testStep.id,\n body: att.body,\n mediaType: att.mediaType,\n contentEncoding: att.contentEncoding,\n },\n });\n }\n }\n }\n\n // TestCaseFinished\n envelopes.push({\n testCaseFinished: {\n testCaseStartedId,\n timestamp: msToTimestamp(cumulativeMs),\n willBeRetried,\n },\n });\n\n return envelopes;\n}\n\n/**\n * Extract attachments from a step's doc entries.\n *\n * Handles:\n * - screenshot docs with data: URIs → BASE64 attachment\n * - screenshot docs with other paths → IDENTITY attachment\n */\nfunction extractDocAttachments(\n step: StoryStep\n): Array<{ body: string; mediaType: string; contentEncoding: AttachmentContentEncoding }> {\n if (!step.docs) return [];\n\n const attachments: Array<{\n body: string;\n mediaType: string;\n contentEncoding: AttachmentContentEncoding;\n }> = [];\n\n for (const doc of step.docs) {\n if (doc.kind !== \"screenshot\") continue;\n\n // Parse data URI: data:image/png;base64,ABC123...\n const match = doc.path.match(/^data:([^;]+);base64,(.+)$/);\n if (match) {\n attachments.push({\n body: match[2],\n mediaType: match[1],\n contentEncoding: \"BASE64\",\n });\n } else {\n // Non-data-URI screenshot (file path or URL) → IDENTITY\n attachments.push({\n body: doc.path,\n mediaType: guessMediaType(doc.path),\n contentEncoding: \"IDENTITY\",\n });\n }\n }\n\n return attachments;\n}\n\n/**\n * Guess media type from a file path or URL.\n */\nfunction guessMediaType(path: string): string {\n const lower = path.toLowerCase();\n if (lower.endsWith(\".png\")) return \"image/png\";\n if (lower.endsWith(\".jpg\") || lower.endsWith(\".jpeg\")) return \"image/jpeg\";\n if (lower.endsWith(\".gif\")) return \"image/gif\";\n if (lower.endsWith(\".webp\")) return \"image/webp\";\n if (lower.endsWith(\".svg\")) return \"image/svg+xml\";\n return \"image/png\"; // Default assumption for screenshots\n}\n\n/**\n * Determine which step index should receive test-case-level attachments.\n * Attach to the failed step if one exists, otherwise the last step.\n * Returns -1 if there are no attachments.\n */\nfunction findTestCaseAttachmentStepIndex(tc: TestCaseResult): number {\n if (tc.attachments.length === 0) return -1;\n\n const failedIndex = tc.stepResults.findIndex((sr) => sr.status === \"failed\");\n if (failedIndex >= 0) return failedIndex;\n\n return tc.stepResults.length - 1;\n}\n","/**\n * CucumberMessagesFormatter — produces NDJSON compatible with @cucumber/html-formatter.\n *\n * Message stream order:\n * Meta → Source* → GherkinDocument* → Pickle* → TestRunStarted →\n * [TestCase, TestCaseStarted, TestStep*, TestCaseFinished]* → TestRunFinished\n */\n\nimport type { TestRunResult, TestCaseResult } from \"../../types/test-result\";\nimport type { Envelope, Meta } from \"../../types/cucumber-messages\";\nimport { synthesizeFeature } from \"./synthesize-feature\";\nimport { buildGherkinDocumentEnvelopes } from \"./build-gherkin-document\";\nimport { buildPickleEnvelopes } from \"./build-pickles\";\nimport {\n buildTestRunStarted,\n buildTestRunFinished,\n buildTestCaseExecutionEnvelopes,\n} from \"./build-execution\";\n\nexport interface CucumberMessagesOptions {\n /** Strategy for deriving Source.uri. Default: \"sourceFile\" */\n uriStrategy?: \"sourceFile\" | \"virtual\";\n /** Whether to emit Source/GherkinDocument for synthesized features. Default: true */\n includeSynthetics?: boolean;\n /** Salt for deterministic IDs. Default: \"\" */\n idSalt?: string;\n /** Tool metadata for Meta envelope */\n meta?: { toolName?: string; toolVersion?: string };\n}\n\nexport class CucumberMessagesFormatter {\n private options: Required<\n Pick<CucumberMessagesOptions, \"uriStrategy\" | \"includeSynthetics\" | \"idSalt\">\n > & { meta?: CucumberMessagesOptions[\"meta\"] };\n\n constructor(options: CucumberMessagesOptions = {}) {\n this.options = {\n uriStrategy: options.uriStrategy ?? \"sourceFile\",\n includeSynthetics: options.includeSynthetics ?? true,\n idSalt: options.idSalt ?? \"\",\n meta: options.meta,\n };\n }\n\n /**\n * Format a TestRunResult into an array of Envelope objects.\n */\n format(run: TestRunResult): Envelope[] {\n const envelopes: Envelope[] = [];\n const salt = this.options.idSalt;\n\n // 1. Meta envelope\n envelopes.push(this.buildMetaEnvelope(run));\n\n // Group test cases by source file\n const grouped = this.groupBySourceFile(run.testCases);\n\n // 2. Source + GherkinDocument + Pickle envelopes (definition phase)\n const allPickleEnvelopes: Envelope[] = [];\n\n for (const [uri, testCases] of grouped) {\n const synthesized = synthesizeFeature(uri, testCases);\n\n if (this.options.includeSynthetics) {\n const { sourceEnvelope, gherkinDocumentEnvelope } =\n buildGherkinDocumentEnvelopes(uri, testCases, synthesized, salt);\n envelopes.push(sourceEnvelope);\n envelopes.push(gherkinDocumentEnvelope);\n }\n\n const pickles = buildPickleEnvelopes(uri, testCases, salt);\n allPickleEnvelopes.push(...pickles);\n }\n\n // All pickles after all source/gherkin documents\n envelopes.push(...allPickleEnvelopes);\n\n // 3. TestRunStarted\n envelopes.push(buildTestRunStarted(run));\n\n // 4. Execution envelopes per test case\n for (const [uri, testCases] of grouped) {\n for (const tc of testCases) {\n const executionEnvelopes = buildTestCaseExecutionEnvelopes(\n uri,\n tc,\n salt\n );\n envelopes.push(...executionEnvelopes);\n }\n }\n\n // 5. TestRunFinished (always last)\n envelopes.push(buildTestRunFinished(run));\n\n return envelopes;\n }\n\n /**\n * Format as NDJSON string (one JSON line per envelope).\n */\n formatToString(run: TestRunResult): string {\n const envelopes = this.format(run);\n return envelopes.map((e) => JSON.stringify(e)).join(\"\\n\") + \"\\n\";\n }\n\n /**\n * Build the Meta envelope.\n */\n private buildMetaEnvelope(run: TestRunResult): Envelope {\n const meta: Meta = {\n protocolVersion: \"25.0.1\",\n implementation: {\n name: this.options.meta?.toolName ?? \"executable-stories\",\n version:\n this.options.meta?.toolVersion ?? run.packageVersion ?? \"0.0.0\",\n },\n runtime: {\n name: \"node.js\",\n version: process.version,\n },\n os: {\n name: process.platform,\n },\n cpu: {\n name: process.arch,\n },\n };\n\n return { meta };\n }\n\n /**\n * Group test cases by source file, preserving order.\n */\n private groupBySourceFile(\n testCases: TestCaseResult[]\n ): Map<string, TestCaseResult[]> {\n const grouped = new Map<string, TestCaseResult[]>();\n for (const tc of testCases) {\n const uri = tc.sourceFile;\n const existing = grouped.get(uri);\n if (existing) {\n existing.push(tc);\n } else {\n grouped.set(uri, [tc]);\n }\n }\n return grouped;\n }\n}\n","/**\n * CucumberHtmlFormatter — produces the official Cucumber HTML report.\n *\n * Thin wrapper: reuses CucumberMessagesFormatter to generate NDJSON envelopes,\n * then pipes them through @cucumber/html-formatter's CucumberHtmlStream.\n */\n\nimport { Readable, Writable } from \"node:stream\";\nimport { CucumberHtmlStream } from \"@cucumber/html-formatter\";\nimport { CucumberMessagesFormatter } from \"./cucumber-messages/index\";\nimport type { CucumberMessagesOptions } from \"./cucumber-messages/index\";\nimport type { TestRunResult } from \"../types/test-result\";\n\nexport interface CucumberHtmlOptions {\n /** Options forwarded to the underlying CucumberMessagesFormatter */\n messages?: CucumberMessagesOptions;\n}\n\nexport class CucumberHtmlFormatter {\n private messagesFormatter: CucumberMessagesFormatter;\n\n constructor(options: CucumberHtmlOptions = {}) {\n this.messagesFormatter = new CucumberMessagesFormatter(options.messages);\n }\n\n /**\n * Format a TestRunResult into official Cucumber HTML.\n *\n * Returns a Promise because CucumberHtmlStream is a Node.js Transform stream.\n */\n async format(run: TestRunResult): Promise<string> {\n return this.formatToString(run);\n }\n\n /**\n * Format a TestRunResult into official Cucumber HTML string.\n */\n async formatToString(run: TestRunResult): Promise<string> {\n // 1. Generate NDJSON envelopes as plain objects\n const envelopes = this.messagesFormatter.format(run);\n\n // 2. Create the Cucumber HTML stream\n const htmlStream = new CucumberHtmlStream();\n\n // 3. Collect output chunks\n const chunks: Buffer[] = [];\n const collector = new Writable({\n write(chunk, _encoding, callback) {\n chunks.push(Buffer.from(chunk));\n callback();\n },\n });\n\n // 4. Pipe HTML stream output to collector\n htmlStream.pipe(collector);\n\n // 5. Write each envelope through the stream\n for (const envelope of envelopes) {\n const accepted = htmlStream.write(envelope);\n if (!accepted) {\n await new Promise<void>((resolve) => htmlStream.once(\"drain\", resolve));\n }\n }\n\n // 6. End the stream and wait for completion\n await new Promise<void>((resolve, reject) => {\n collector.on(\"finish\", resolve);\n collector.on(\"error\", reject);\n htmlStream.end();\n });\n\n return Buffer.concat(chunks).toString(\"utf8\");\n }\n}\n","/**\n * Jest Adapter - Layer 1.\n *\n * Transforms Jest test results and story reports into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"../../types/raw\";\n\n/** Jest test result shape (subset of what Jest provides) */\nexport interface JestTestResult {\n fullName: string;\n status: \"passed\" | \"failed\" | \"pending\" | \"todo\";\n duration?: number;\n failureMessages?: string[];\n}\n\n/** Jest file result shape */\nexport interface JestFileResult {\n testFilePath: string;\n testResults: JestTestResult[];\n}\n\n/** Jest aggregated result shape */\nexport interface JestAggregatedResult {\n testResults: JestFileResult[];\n startTime?: number;\n}\n\n/** Story file report written by story.init() */\nexport interface StoryFileReport {\n testFilePath: string;\n scenarios: StoryMeta[];\n}\n\n/** Options for Jest adapter */\nexport interface JestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n}\n\n/**\n * Map Jest status to RawStatus.\n */\nfunction mapJestStatus(status: JestTestResult[\"status\"]): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"pending\":\n return \"pending\";\n case \"todo\":\n return \"todo\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Jest results and story reports to RawRun.\n *\n * @param jestResults - Jest aggregated results\n * @param storyReports - Story reports from story.init()\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptJestRun(\n jestResults: JestAggregatedResult,\n storyReports: StoryFileReport[],\n options: JestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n\n // Build map of Jest results by file for lookup\n const fileResultsMap = new Map<string, JestFileResult>();\n for (const fileResult of jestResults.testResults) {\n fileResultsMap.set(fileResult.testFilePath, fileResult);\n }\n\n // Process each story report\n for (const report of storyReports) {\n const fileResult = fileResultsMap.get(report.testFilePath);\n\n for (const meta of report.scenarios) {\n if (!meta?.scenario) continue;\n\n // Find matching Jest test result\n const matchingTest = findMatchingJestTest(fileResult, meta);\n\n testCases.push({\n externalId: matchingTest?.fullName,\n title: meta.scenario,\n titlePath: meta.suitePath\n ? [...meta.suitePath, meta.scenario]\n : [meta.scenario],\n story: meta,\n sourceFile: report.testFilePath,\n sourceLine: undefined, // Jest doesn't provide line numbers\n status: matchingTest ? mapJestStatus(matchingTest.status) : \"unknown\",\n durationMs: matchingTest?.duration,\n error: matchingTest?.failureMessages?.length\n ? { message: matchingTest.failureMessages.join(\"\\n\") }\n : undefined,\n stepEvents: undefined, // Jest doesn't provide step-level results\n attachments: undefined, // Jest doesn't capture attachments\n meta: { jestStatus: matchingTest?.status },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: jestResults.startTime,\n finishedAtMs: Date.now(),\n projectRoot: options.projectRoot ?? process.cwd(),\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Find matching Jest test result for a story.\n *\n * Matches by constructing fullName from suitePath and scenario.\n */\nfunction findMatchingJestTest(\n fileResult: JestFileResult | undefined,\n meta: StoryMeta\n): JestTestResult | undefined {\n if (!fileResult) return undefined;\n\n // Construct expected fullName\n const expectedFullName = meta.suitePath\n ? [...meta.suitePath, meta.scenario].join(\" > \")\n : meta.scenario;\n\n return fileResult.testResults.find((test) => test.fullName === expectedFullName);\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Vitest Adapter - Layer 1.\n *\n * Transforms Vitest test results into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus } from \"../../types/raw\";\n\n/** Vitest test state */\nexport type VitestState = \"passed\" | \"failed\" | \"skipped\" | \"pending\";\n\n/** Vitest serialized error */\nexport interface VitestSerializedError {\n message?: string;\n stack?: string;\n diff?: string;\n}\n\n/** Vitest test result shape */\nexport interface VitestTestResult {\n state?: VitestState;\n duration?: number;\n errors?: VitestSerializedError[];\n}\n\n/** Vitest test case shape (minimal) */\nexport interface VitestTestCase {\n name: string;\n meta: () => Record<string, unknown>;\n result: () => VitestTestResult | undefined;\n}\n\n/** Vitest test module shape (minimal) */\nexport interface VitestTestModule {\n moduleId?: string;\n relativeModuleId?: string;\n children?: {\n allTests: () => Iterable<VitestTestCase>;\n };\n}\n\n/** Options for Vitest adapter */\nexport interface VitestAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n}\n\n/**\n * Map Vitest state to RawStatus.\n */\nfunction mapVitestStatus(state?: VitestState): RawStatus {\n switch (state) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"pending\":\n return \"pending\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Vitest test modules to RawRun.\n *\n * @param testModules - Vitest test modules\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptVitestRun(\n testModules: ReadonlyArray<VitestTestModule>,\n options: VitestAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const mod of testModules) {\n const collection = mod.children;\n if (!collection) continue;\n\n // Get module path\n const moduleId = mod.moduleId ?? mod.relativeModuleId ?? \"\";\n const sourcePath = moduleId.startsWith(\"/\")\n ? moduleId\n : `${projectRoot}/${moduleId}`;\n\n for (const test of collection.allTests()) {\n const meta = test.meta();\n const story = meta?.[\"story\"] as StoryMeta | undefined;\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n const result = test.result();\n const state = result?.state;\n const errors = state === \"failed\" && result ? result.errors : undefined;\n\n testCases.push({\n externalId: test.name,\n title: story.scenario,\n titlePath: story.suitePath\n ? [...story.suitePath, story.scenario]\n : [story.scenario],\n story,\n sourceFile: sourcePath,\n sourceLine: undefined, // Vitest doesn't provide line numbers easily\n status: mapVitestStatus(state),\n durationMs: result?.duration,\n error: errors?.length\n ? {\n message: formatVitestError(errors[0]),\n stack: errors[0].stack,\n }\n : undefined,\n stepEvents: undefined, // Vitest doesn't provide step-level results\n attachments: undefined, // Vitest doesn't capture attachments\n meta: { vitestState: state },\n retry: 0,\n retries: 0,\n projectName: undefined,\n });\n }\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Format Vitest error message.\n */\nfunction formatVitestError(error: VitestSerializedError): string {\n const parts: string[] = [];\n\n if (error.message) {\n parts.push(error.message);\n }\n\n if (error.diff) {\n parts.push(\"\", error.diff);\n }\n\n return parts.join(\"\\n\") || \"Unknown error\";\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Playwright Adapter - Layer 1.\n *\n * Transforms Playwright test results into RawRun.\n */\n\nimport type { StoryMeta } from \"../../types/story\";\nimport type { RawRun, RawTestCase, RawStatus, RawAttachment } from \"../../types/raw\";\n\n/** Playwright test status */\nexport type PlaywrightStatus =\n | \"passed\"\n | \"failed\"\n | \"skipped\"\n | \"timedOut\"\n | \"interrupted\";\n\n/** Playwright test error */\nexport interface PlaywrightError {\n message?: string;\n stack?: string;\n}\n\n/** Playwright attachment */\nexport interface PlaywrightAttachment {\n name: string;\n contentType: string;\n path?: string;\n body?: Buffer;\n}\n\n/** Playwright test result */\nexport interface PlaywrightTestResult {\n status: PlaywrightStatus;\n duration: number;\n errors: PlaywrightError[];\n attachments: PlaywrightAttachment[];\n retry: number;\n}\n\n/** Playwright test case annotation */\nexport interface PlaywrightAnnotation {\n type: string;\n description?: string;\n}\n\n/** Playwright test location */\nexport interface PlaywrightLocation {\n file: string;\n line: number;\n column: number;\n}\n\n/** Playwright test case shape (minimal) */\nexport interface PlaywrightTestCase {\n title: string;\n titlePath: () => string[];\n annotations: PlaywrightAnnotation[];\n location: PlaywrightLocation;\n retries: number;\n}\n\n/** Options for Playwright adapter */\nexport interface PlaywrightAdapterOptions {\n /** Project root directory */\n projectRoot?: string;\n /** Package version */\n packageVersion?: string;\n /** Git SHA */\n gitSha?: string;\n /** Start time (epoch ms) */\n startedAtMs?: number;\n /** Playwright project name */\n projectName?: string;\n}\n\n/**\n * Map Playwright status to RawStatus.\n */\nfunction mapPlaywrightStatus(status: PlaywrightStatus): RawStatus {\n switch (status) {\n case \"passed\":\n return \"pass\";\n case \"failed\":\n return \"fail\";\n case \"skipped\":\n return \"skip\";\n case \"timedOut\":\n return \"timeout\";\n case \"interrupted\":\n return \"interrupted\";\n default:\n return \"unknown\";\n }\n}\n\n/**\n * Adapt Playwright test results to RawRun.\n *\n * @param testResults - Array of [testCase, result] tuples\n * @param options - Adapter options\n * @returns RawRun for ACL processing\n */\nexport function adaptPlaywrightRun(\n testResults: Array<[PlaywrightTestCase, PlaywrightTestResult]>,\n options: PlaywrightAdapterOptions = {}\n): RawRun {\n const testCases: RawTestCase[] = [];\n const projectRoot = options.projectRoot ?? process.cwd();\n\n for (const [test, result] of testResults) {\n // Find story-meta annotation\n const storyAnnotation = test.annotations.find((a) => a.type === \"story-meta\");\n if (!storyAnnotation?.description) continue;\n\n let story: StoryMeta;\n try {\n story = JSON.parse(storyAnnotation.description);\n } catch {\n continue; // Skip if annotation is not valid JSON\n }\n\n if (!story?.scenario || !Array.isArray(story.steps)) continue;\n\n // Convert attachments\n const attachments = convertAttachments(result.attachments);\n\n // Get error info\n const error = result.errors?.length\n ? {\n message: result.errors[0].message,\n stack: result.errors[0].stack,\n }\n : undefined;\n\n testCases.push({\n externalId: test.titlePath().join(\" > \"),\n title: story.scenario,\n titlePath: test.titlePath(),\n story,\n sourceFile: test.location.file,\n sourceLine: test.location.line,\n status: mapPlaywrightStatus(result.status),\n durationMs: result.duration,\n error,\n stepEvents: undefined, // Playwright could provide step info, but we don't capture it yet\n attachments,\n meta: {\n playwrightStatus: result.status,\n column: test.location.column,\n },\n retry: result.retry,\n retries: test.retries,\n projectName: options.projectName,\n });\n }\n\n return {\n testCases,\n startedAtMs: options.startedAtMs,\n finishedAtMs: Date.now(),\n projectRoot,\n packageVersion: options.packageVersion,\n gitSha: options.gitSha,\n ci: detectCI(),\n };\n}\n\n/**\n * Convert Playwright attachments to raw attachments.\n */\nfunction convertAttachments(\n attachments: PlaywrightAttachment[]\n): RawAttachment[] {\n return attachments.map((att) => ({\n name: att.name,\n mediaType: att.contentType,\n path: att.path,\n body: att.body ? att.body.toString(\"base64\") : undefined,\n encoding: att.body ? \"BASE64\" as const : undefined,\n byteLength: att.body?.length,\n }));\n}\n\n/**\n * Detect CI environment.\n */\nfunction detectCI() {\n if (process.env.GITHUB_ACTIONS === \"true\") {\n return {\n name: \"GitHub Actions\",\n url: process.env.GITHUB_SERVER_URL && process.env.GITHUB_REPOSITORY\n ? `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`\n : undefined,\n buildNumber: process.env.GITHUB_RUN_NUMBER,\n };\n }\n\n if (process.env.JENKINS_URL) {\n return {\n name: \"Jenkins\",\n url: process.env.BUILD_URL,\n buildNumber: process.env.BUILD_NUMBER,\n };\n }\n\n if (process.env.CI) {\n return {\n name: \"CI\",\n url: undefined,\n buildNumber: undefined,\n };\n }\n\n return undefined;\n}\n","/**\n * Story types — the shared vocabulary for all framework adapters.\n *\n * These types were previously in executable-stories-core.\n * They now live in formatters so every adapter can import them\n * from the same place that defines RawRun (the output contract).\n */\n\n// ============================================================================\n// Step Keywords\n// ============================================================================\n\n/** BDD step keywords for scenario documentation */\nexport type StepKeyword = \"Given\" | \"When\" | \"Then\" | \"And\" | \"But\";\n\n/** Step execution mode for docs rendering */\nexport type StepMode = \"normal\" | \"skip\" | \"only\" | \"todo\" | \"fails\" | \"concurrent\";\n\n// ============================================================================\n// Doc Entry Types\n// ============================================================================\n\n/** Phase tracks when the doc entry was added */\nexport type DocPhase = \"static\" | \"runtime\";\n\n/** Union type for all documentation entry kinds */\nexport type DocEntry =\n | { kind: \"note\"; text: string; phase: DocPhase }\n | { kind: \"tag\"; names: string[]; phase: DocPhase }\n | { kind: \"kv\"; label: string; value: unknown; phase: DocPhase }\n | { kind: \"code\"; label: string; content: string; lang?: string; phase: DocPhase }\n | { kind: \"table\"; label: string; columns: string[]; rows: string[][]; phase: DocPhase }\n | { kind: \"link\"; label: string; url: string; phase: DocPhase }\n | { kind: \"section\"; title: string; markdown: string; phase: DocPhase }\n | { kind: \"mermaid\"; code: string; title?: string; phase: DocPhase }\n | { kind: \"screenshot\"; path: string; alt?: string; phase: DocPhase }\n | { kind: \"custom\"; type: string; data: unknown; phase: DocPhase };\n\n// ============================================================================\n// Story Step\n// ============================================================================\n\n/**\n * A single step in a scenario with its documentation entries.\n */\nexport interface StoryStep {\n /** Stable internal ID (auto-generated at creation, e.g., \"step-0\") */\n id?: string;\n /** The BDD keyword (Given, When, Then, And, But) */\n keyword: StepKeyword;\n /** The step description text */\n text: string;\n /** Step execution mode for docs rendering */\n mode?: StepMode;\n /** Rich documentation entries attached to this step */\n docs?: DocEntry[];\n /** Opt-in step duration in milliseconds */\n durationMs?: number;\n /** Whether this step wrapped a function body (step.fn / step.step) vs a text marker */\n wrapped?: boolean;\n}\n\n// ============================================================================\n// Story Metadata\n// ============================================================================\n\n/**\n * Metadata for a complete scenario, attached to test metadata.\n * Used by reporters to generate documentation.\n */\nexport interface StoryMeta {\n /** The scenario title (from test name) */\n scenario: string;\n /** All steps in this scenario */\n steps: StoryStep[];\n /** Tags for filtering and categorization */\n tags?: string[];\n /** Ticket/issue references (normalized to array) */\n tickets?: string[];\n /** User-defined metadata */\n meta?: Record<string, unknown>;\n /** Parent describe/suite names for hierarchical grouping */\n suitePath?: string[];\n /** Story-level docs (before any steps) */\n docs?: DocEntry[];\n /** Order in which story.init() was called (for source ordering) */\n sourceOrder?: number;\n}\n\n/** Key used to store StoryMeta in test metadata */\nexport const STORY_META_KEY = \"story\";\n","/**\n * Validation helpers for canonical TestRunResult.\n *\n * Used in tests to verify ACL output meets all invariants.\n */\n\nimport type { TestRunResult, TestCaseResult } from \"../../types/test-result\";\n\n/** Validation result */\nexport interface ValidationResult {\n /** Whether the run is valid */\n valid: boolean;\n /** List of validation errors */\n errors: string[];\n}\n\n/**\n * Validate a canonical TestRunResult.\n *\n * Checks:\n * - All required fields are present\n * - stepResults length matches story.steps length\n * - stepResults indexes are valid and unique\n * - Durations are non-negative\n * - Timestamps are valid\n *\n * @param run - The TestRunResult to validate\n * @returns Validation result with errors if any\n */\nexport function validateCanonicalRun(run: TestRunResult): ValidationResult {\n const errors: string[] = [];\n\n // Validate run-level fields\n if (!run.runId) {\n errors.push(\"Run missing runId\");\n }\n\n if (!run.projectRoot) {\n errors.push(\"Run missing projectRoot\");\n }\n\n if (typeof run.startedAtMs !== \"number\" || run.startedAtMs < 0) {\n errors.push(`Invalid startedAtMs: ${run.startedAtMs}`);\n }\n\n if (typeof run.finishedAtMs !== \"number\" || run.finishedAtMs < 0) {\n errors.push(`Invalid finishedAtMs: ${run.finishedAtMs}`);\n }\n\n if (run.finishedAtMs < run.startedAtMs) {\n errors.push(`finishedAtMs (${run.finishedAtMs}) < startedAtMs (${run.startedAtMs})`);\n }\n\n if (typeof run.durationMs !== \"number\" || run.durationMs < 0) {\n errors.push(`Invalid durationMs: ${run.durationMs}`);\n }\n\n if (!Array.isArray(run.testCases)) {\n errors.push(\"testCases is not an array\");\n } else {\n // Validate each test case\n for (let i = 0; i < run.testCases.length; i++) {\n const tcErrors = validateTestCase(run.testCases[i], i);\n errors.push(...tcErrors);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Validate a single test case.\n */\nfunction validateTestCase(tc: TestCaseResult, index: number): string[] {\n const errors: string[] = [];\n const prefix = `TestCase[${index}]`;\n\n // Required fields\n if (!tc.id) {\n errors.push(`${prefix}: missing id`);\n }\n\n if (!tc.story) {\n errors.push(`${prefix}: missing story`);\n return errors; // Can't validate further without story\n }\n\n if (!tc.sourceFile) {\n errors.push(`${prefix}: missing sourceFile`);\n }\n\n if (typeof tc.sourceLine !== \"number\" || tc.sourceLine < 1) {\n errors.push(`${prefix}: invalid sourceLine ${tc.sourceLine}`);\n }\n\n // Status must be valid enum value\n const validStatuses = [\"passed\", \"failed\", \"skipped\", \"pending\"];\n if (!validStatuses.includes(tc.status)) {\n errors.push(`${prefix}: invalid status \"${tc.status}\"`);\n }\n\n // Duration must be non-negative\n if (typeof tc.durationMs !== \"number\" || tc.durationMs < 0) {\n errors.push(`${prefix}: invalid durationMs ${tc.durationMs}`);\n }\n\n // Retry fields\n if (typeof tc.retry !== \"number\" || tc.retry < 0) {\n errors.push(`${prefix}: invalid retry ${tc.retry}`);\n }\n\n if (typeof tc.retries !== \"number\" || tc.retries < 0) {\n errors.push(`${prefix}: invalid retries ${tc.retries}`);\n }\n\n // Arrays must be arrays\n if (!Array.isArray(tc.attachments)) {\n errors.push(`${prefix}: attachments is not an array`);\n }\n\n if (!Array.isArray(tc.titlePath)) {\n errors.push(`${prefix}: titlePath is not an array`);\n }\n\n if (!Array.isArray(tc.tags)) {\n errors.push(`${prefix}: tags is not an array`);\n }\n\n // Validate stepResults\n if (!Array.isArray(tc.stepResults)) {\n errors.push(`${prefix}: stepResults is not an array`);\n } else {\n const stepsCount = tc.story.steps?.length ?? 0;\n\n // stepResults length should match story.steps length\n if (tc.stepResults.length !== stepsCount) {\n errors.push(\n `${prefix}: stepResults.length (${tc.stepResults.length}) !== story.steps.length (${stepsCount})`\n );\n }\n\n // Validate each step result\n const seenIndexes = new Set<number>();\n for (const sr of tc.stepResults) {\n // Index must be valid\n if (typeof sr.index !== \"number\" || sr.index < 0 || sr.index >= stepsCount) {\n errors.push(`${prefix}: invalid stepResult index ${sr.index}`);\n }\n\n // Index must be unique\n if (seenIndexes.has(sr.index)) {\n errors.push(`${prefix}: duplicate stepResult index ${sr.index}`);\n }\n seenIndexes.add(sr.index);\n\n // Status must be valid\n if (!validStatuses.includes(sr.status)) {\n errors.push(`${prefix}: invalid stepResult status \"${sr.status}\" at index ${sr.index}`);\n }\n\n // Duration must be non-negative\n if (typeof sr.durationMs !== \"number\" || sr.durationMs < 0) {\n errors.push(`${prefix}: invalid stepResult durationMs ${sr.durationMs} at index ${sr.index}`);\n }\n }\n }\n\n return errors;\n}\n\n/**\n * Assert that a run is valid, throwing if not.\n *\n * Useful in tests.\n *\n * @param run - The TestRunResult to validate\n * @throws Error if validation fails\n */\nexport function assertValidRun(run: TestRunResult): void {\n const result = validateCanonicalRun(run);\n if (!result.valid) {\n throw new Error(`Invalid TestRunResult:\\n${result.errors.join(\"\\n\")}`);\n }\n}\n","/**\n * NDJSON-to-TestRunResult parser.\n *\n * Parses Cucumber Messages NDJSON (one JSON envelope per line) back into\n * a TestRunResult suitable for rendering by HtmlFormatter or other formatters.\n *\n * This is the NDJSON compat path: it produces a minimal but sufficient\n * TestRunResult. Fields not present in the NDJSON stream are given\n * sensible defaults.\n */\n\nimport type { StoryMeta, StoryStep, StepKeyword, DocEntry, DocPhase } from \"../types/story\";\nimport type {\n TestRunResult,\n TestCaseResult,\n TestCaseAttempt,\n StepResult,\n TestStatus,\n Attachment,\n} from \"../types/test-result\";\nimport type {\n Envelope,\n Pickle,\n PickleStep,\n GherkinDocument,\n Source,\n TestCase,\n TestCaseStarted,\n TestCaseFinished,\n TestStepFinished,\n TestStepResultStatus,\n CucumberAttachment,\n Timestamp,\n} from \"../types/cucumber-messages\";\n\n// ============================================================================\n// Internal index types\n// ============================================================================\n\ninterface PickleIndex {\n pickle: Pickle;\n uri: string;\n}\n\ninterface StepDefinition {\n keyword: StepKeyword;\n text: string;\n}\n\ninterface TestCaseIndex {\n testCase: TestCase;\n pickleId: string;\n}\n\ninterface TestCaseStartedIndex {\n testCaseStarted: TestCaseStarted;\n testCaseId: string;\n}\n\ninterface TestCaseFinishedIndex {\n testCaseStartedId: string;\n willBeRetried: boolean;\n}\n\ninterface StepResultAccumulator {\n testStepId: string;\n status: TestStatus;\n durationMs: number;\n errorMessage?: string;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Parse an NDJSON string into a TestRunResult.\n *\n * @param ndjson - NDJSON string (one JSON envelope per line)\n * @returns TestRunResult reconstructed from the envelopes\n */\nexport function parseNdjson(ndjson: string): TestRunResult {\n const lines = ndjson.trim().split(\"\\n\").filter(Boolean);\n const envelopes: Envelope[] = lines.map((line) => JSON.parse(line));\n return parseEnvelopes(envelopes);\n}\n\n/**\n * Parse an array of Envelope objects into a TestRunResult.\n *\n * @param envelopes - Array of Cucumber Messages envelopes\n * @returns TestRunResult reconstructed from the envelopes\n */\nexport function parseEnvelopes(envelopes: Envelope[]): TestRunResult {\n // Indexes for cross-referencing\n const sources = new Map<string, Source>(); // uri → Source\n const gherkinDocs = new Map<string, GherkinDocument>(); // uri → GherkinDocument\n const pickles = new Map<string, PickleIndex>(); // pickleId → { pickle, uri }\n const testCases = new Map<string, TestCaseIndex>(); // testCaseId → { testCase, pickleId }\n const testCaseStarteds = new Map<string, TestCaseStartedIndex>(); // testCaseStartedId → { ... }\n const testCaseFinisheds = new Map<string, TestCaseFinishedIndex>(); // testCaseStartedId → { ... }\n const testCaseIdToStartedIds = new Map<string, string[]>(); // testCaseId → testCaseStartedId[]\n const stepResults = new Map<string, StepResultAccumulator[]>(); // testCaseStartedId → step results\n const attachments = new Map<string, CucumberAttachment[]>(); // testCaseStartedId → attachments\n\n let startedAtMs = 0;\n let finishedAtMs = 0;\n let success = true;\n let toolName = \"unknown\";\n let toolVersion = \"0.0.0\";\n\n // Single-pass index building\n for (const envelope of envelopes) {\n if (\"meta\" in envelope) {\n toolName = envelope.meta.implementation.name;\n toolVersion = envelope.meta.implementation.version;\n }\n\n if (\"source\" in envelope) {\n sources.set(envelope.source.uri, envelope.source);\n }\n\n if (\"gherkinDocument\" in envelope) {\n const doc = envelope.gherkinDocument;\n gherkinDocs.set(doc.uri, doc);\n }\n\n if (\"pickle\" in envelope) {\n const p = envelope.pickle;\n pickles.set(p.id, { pickle: p, uri: p.uri });\n }\n\n if (\"testCase\" in envelope) {\n const tc = envelope.testCase;\n testCases.set(tc.id, { testCase: tc, pickleId: tc.pickleId });\n }\n\n if (\"testCaseStarted\" in envelope) {\n const tcs = envelope.testCaseStarted;\n testCaseStarteds.set(tcs.id, {\n testCaseStarted: tcs,\n testCaseId: tcs.testCaseId,\n });\n stepResults.set(tcs.id, []);\n attachments.set(tcs.id, []);\n // Group by testCaseId for retry detection\n const existing = testCaseIdToStartedIds.get(tcs.testCaseId) ?? [];\n existing.push(tcs.id);\n testCaseIdToStartedIds.set(tcs.testCaseId, existing);\n }\n\n if (\"testCaseFinished\" in envelope) {\n const tcf = envelope.testCaseFinished;\n testCaseFinisheds.set(tcf.testCaseStartedId, {\n testCaseStartedId: tcf.testCaseStartedId,\n willBeRetried: tcf.willBeRetried,\n });\n }\n\n if (\"testStepFinished\" in envelope) {\n const tsf = envelope.testStepFinished;\n const results = stepResults.get(tsf.testCaseStartedId);\n if (results) {\n results.push({\n testStepId: tsf.testStepId,\n status: cucumberStatusToTestStatus(tsf.testStepResult.status),\n durationMs: durationToMs(tsf.testStepResult.duration),\n errorMessage: tsf.testStepResult.message,\n });\n }\n }\n\n if (\"attachment\" in envelope) {\n const att = envelope.attachment;\n const list = attachments.get(att.testCaseStartedId);\n if (list) {\n list.push(att);\n }\n }\n\n if (\"testRunStarted\" in envelope) {\n startedAtMs = timestampToMs(envelope.testRunStarted.timestamp);\n }\n\n if (\"testRunFinished\" in envelope) {\n finishedAtMs = timestampToMs(envelope.testRunFinished.timestamp);\n success = envelope.testRunFinished.success;\n }\n }\n\n // Reconstruct TestCaseResults (one per TestCase, grouping retries)\n const testCaseResults: TestCaseResult[] = [];\n\n for (const [testCaseId, tcIndex] of testCases) {\n const pickleIndex = pickles.get(tcIndex.pickleId);\n if (!pickleIndex) continue;\n\n const pickle = pickleIndex.pickle;\n const uri = pickleIndex.uri;\n\n // Get all started IDs for this test case (sorted by attempt number)\n const startedIds = testCaseIdToStartedIds.get(testCaseId) ?? [];\n if (startedIds.length === 0) continue;\n\n // Sort by attempt number\n const sortedStarteds = startedIds\n .map((id) => testCaseStarteds.get(id)!)\n .filter(Boolean)\n .sort((a, b) => a.testCaseStarted.attempt - b.testCaseStarted.attempt);\n\n // The final attempt is the last one (or the one with willBeRetried=false)\n const finalStarted = sortedStarteds[sortedStarteds.length - 1];\n const finalStartedId = finalStarted.testCaseStarted.id;\n\n // Build step-to-result mapping via testStepId\n const testStepIdToIndex = new Map<string, number>();\n for (let i = 0; i < tcIndex.testCase.testSteps.length; i++) {\n testStepIdToIndex.set(tcIndex.testCase.testSteps[i].id, i);\n }\n\n // Reconstruct StorySteps from pickle steps + gherkin doc\n const storySteps = reconstructStorySteps(pickle, uri, gherkinDocs);\n\n // Use final attempt's step results\n const tcStepResults = stepResults.get(finalStartedId) ?? [];\n const tcAttachments = attachments.get(finalStartedId) ?? [];\n\n // Reconstruct StepResults in order\n const orderedStepResults: StepResult[] = storySteps.map((_, i) => ({\n index: i,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n\n for (const sr of tcStepResults) {\n const stepIndex = testStepIdToIndex.get(sr.testStepId);\n if (stepIndex !== undefined && stepIndex < orderedStepResults.length) {\n orderedStepResults[stepIndex] = {\n index: stepIndex,\n status: sr.status,\n durationMs: sr.durationMs,\n errorMessage: sr.errorMessage,\n };\n }\n }\n\n // Derive overall status from step results\n const overallStatus = deriveOverallStatus(orderedStepResults);\n const totalDurationMs = orderedStepResults.reduce(\n (sum, sr) => sum + sr.durationMs,\n 0\n );\n\n // Reconstruct tags\n const tags = pickle.tags.map((t) => t.name.replace(/^@/, \"\"));\n\n // Reconstruct attachments (from final attempt)\n const resolvedAttachments: Attachment[] = tcAttachments.map((att) => ({\n name: att.mediaType,\n mediaType: att.mediaType,\n body: att.body,\n contentEncoding: att.contentEncoding,\n }));\n\n // Extract titlePath from feature name + scenario name\n const featureName = extractFeatureName(uri, gherkinDocs);\n const titlePath = featureName\n ? [featureName, pickle.name]\n : [pickle.name];\n\n // Build StoryMeta\n const story: StoryMeta = {\n scenario: pickle.name,\n steps: storySteps,\n tags: tags.length > 0 ? tags : undefined,\n };\n\n // Build error info\n const failedStep = orderedStepResults.find((sr) => sr.status === \"failed\");\n\n // Build attempts array if there are retries\n let attempts: TestCaseAttempt[] | undefined;\n if (sortedStarteds.length > 1) {\n attempts = sortedStarteds.map((started) => {\n const sId = started.testCaseStarted.id;\n const sStepResults = stepResults.get(sId) ?? [];\n const attemptStatus = deriveOverallStatus(\n buildOrderedStepResults(sStepResults, testStepIdToIndex, storySteps.length)\n );\n const attemptDurationMs = sStepResults.reduce(\n (sum, sr) => sum + sr.durationMs, 0\n );\n const attemptFailedStep = sStepResults.find((sr) => sr.status === \"failed\");\n return {\n attempt: started.testCaseStarted.attempt,\n status: attemptStatus,\n durationMs: attemptDurationMs,\n errorMessage: attemptFailedStep?.errorMessage,\n };\n });\n }\n\n const testCaseResult: TestCaseResult = {\n id: tcIndex.testCase.id,\n story,\n sourceFile: uri,\n sourceLine: 1,\n status: overallStatus,\n durationMs: totalDurationMs,\n errorMessage: failedStep?.errorMessage,\n attachments: resolvedAttachments,\n stepResults: orderedStepResults,\n titlePath,\n retry: finalStarted.testCaseStarted.attempt,\n retries: sortedStarteds.length > 1 ? sortedStarteds.length - 1 : 0,\n tags,\n attempts,\n };\n\n testCaseResults.push(testCaseResult);\n }\n\n const durationMs =\n finishedAtMs > 0 && startedAtMs > 0\n ? finishedAtMs - startedAtMs\n : testCaseResults.reduce((sum, tc) => sum + tc.durationMs, 0);\n\n return {\n testCases: testCaseResults,\n startedAtMs,\n finishedAtMs,\n durationMs,\n projectRoot: \"\",\n runId: \"\",\n packageVersion: toolVersion !== \"0.0.0\" ? toolVersion : undefined,\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction cucumberStatusToTestStatus(status: TestStepResultStatus): TestStatus {\n switch (status) {\n case \"PASSED\":\n return \"passed\";\n case \"FAILED\":\n return \"failed\";\n case \"SKIPPED\":\n return \"skipped\";\n case \"PENDING\":\n case \"UNDEFINED\":\n return \"pending\";\n default:\n return \"skipped\";\n }\n}\n\nfunction timestampToMs(ts: Timestamp): number {\n return ts.seconds * 1000 + Math.round(ts.nanos / 1_000_000);\n}\n\nfunction durationToMs(d: { seconds: number; nanos: number }): number {\n return d.seconds * 1000 + Math.round(d.nanos / 1_000_000);\n}\n\n/**\n * Reconstruct StorySteps from pickle steps, using GherkinDocument for keywords.\n */\nfunction reconstructStorySteps(\n pickle: Pickle,\n uri: string,\n gherkinDocs: Map<string, GherkinDocument>\n): StoryStep[] {\n const doc = gherkinDocs.get(uri);\n\n // Build AST step ID → keyword map from GherkinDocument\n const astStepKeywords = new Map<string, string>();\n if (doc) {\n for (const child of doc.feature.children) {\n const scenario = child.scenario ?? child.background;\n if (scenario) {\n for (const step of scenario.steps) {\n astStepKeywords.set(step.id, step.keyword.trim());\n }\n }\n }\n }\n\n return pickle.steps.map((ps) => {\n // Look up the keyword from the AST step\n let keyword: StepKeyword = \"Given\";\n if (ps.astNodeIds.length > 0) {\n const astKeyword = astStepKeywords.get(ps.astNodeIds[0]);\n if (astKeyword && isStepKeyword(astKeyword)) {\n keyword = astKeyword;\n }\n }\n\n // Fallback: derive keyword from pickle step type\n if (!ps.astNodeIds.length || !astStepKeywords.has(ps.astNodeIds[0])) {\n keyword = pickleStepTypeToKeyword(ps.type);\n }\n\n const step: StoryStep = {\n keyword,\n text: ps.text,\n };\n\n // Reconstruct docs from pickle step argument\n const docs = pickleStepArgumentToDocs(ps);\n if (docs.length > 0) {\n step.docs = docs;\n }\n\n return step;\n });\n}\n\nfunction isStepKeyword(s: string): s is StepKeyword {\n return [\"Given\", \"When\", \"Then\", \"And\", \"But\"].includes(s);\n}\n\nfunction pickleStepTypeToKeyword(\n type: string\n): StepKeyword {\n switch (type) {\n case \"Context\":\n return \"Given\";\n case \"Action\":\n return \"When\";\n case \"Outcome\":\n return \"Then\";\n default:\n return \"Given\";\n }\n}\n\nfunction extractFeatureName(\n uri: string,\n gherkinDocs: Map<string, GherkinDocument>\n): string | undefined {\n const doc = gherkinDocs.get(uri);\n if (doc) {\n return doc.feature.name;\n }\n return undefined;\n}\n\nfunction deriveOverallStatus(stepResults: StepResult[]): TestStatus {\n if (stepResults.some((sr) => sr.status === \"failed\")) return \"failed\";\n if (stepResults.every((sr) => sr.status === \"skipped\")) return \"skipped\";\n if (stepResults.some((sr) => sr.status === \"pending\")) return \"pending\";\n if (stepResults.every((sr) => sr.status === \"passed\")) return \"passed\";\n return \"passed\";\n}\n\n/**\n * Build ordered step results from accumulated results for a specific attempt.\n */\nfunction buildOrderedStepResults(\n accumulators: StepResultAccumulator[],\n testStepIdToIndex: Map<string, number>,\n stepCount: number\n): StepResult[] {\n const results: StepResult[] = Array.from({ length: stepCount }, (_, i) => ({\n index: i,\n status: \"passed\" as TestStatus,\n durationMs: 0,\n }));\n\n for (const sr of accumulators) {\n const stepIndex = testStepIdToIndex.get(sr.testStepId);\n if (stepIndex !== undefined && stepIndex < results.length) {\n results[stepIndex] = {\n index: stepIndex,\n status: sr.status,\n durationMs: sr.durationMs,\n errorMessage: sr.errorMessage,\n };\n }\n }\n\n return results;\n}\n\n/**\n * Convert a pickle step's argument (DocString/DataTable) back to DocEntry[].\n */\nfunction pickleStepArgumentToDocs(ps: PickleStep): DocEntry[] {\n if (!ps.argument) return [];\n const docs: DocEntry[] = [];\n const phase: DocPhase = \"static\";\n\n if (ps.argument.dataTable) {\n const table = ps.argument.dataTable;\n if (table.rows.length > 0) {\n const columns = table.rows[0].cells.map((c) => c.value);\n const rows = table.rows.slice(1).map((r) => r.cells.map((c) => c.value));\n docs.push({\n kind: \"table\",\n label: \"\",\n columns,\n rows,\n phase,\n });\n }\n }\n\n if (ps.argument.docString) {\n const ds = ps.argument.docString;\n const mediaType = ds.mediaType ?? \"text/plain\";\n\n if (mediaType === \"text/plain\") {\n docs.push({\n kind: \"note\",\n text: ds.content,\n phase,\n });\n } else if (mediaType === \"text/markdown\") {\n docs.push({\n kind: \"section\",\n title: \"\",\n markdown: ds.content,\n phase,\n });\n } else if (mediaType === \"text/x-mermaid\") {\n docs.push({\n kind: \"mermaid\",\n code: ds.content,\n phase,\n });\n } else if (mediaType === \"application/json\") {\n try {\n docs.push({\n kind: \"custom\",\n type: \"json\",\n data: JSON.parse(ds.content),\n phase,\n });\n } catch {\n docs.push({\n kind: \"code\",\n label: \"\",\n content: ds.content,\n lang: \"json\",\n phase,\n });\n }\n } else {\n // Default: treat as code block with mediaType as lang\n docs.push({\n kind: \"code\",\n label: \"\",\n content: ds.content,\n lang: mediaType,\n phase,\n });\n }\n }\n\n return docs;\n}\n","/**\n * Git information utilities.\n *\n * Read git SHA and other info from the local repository.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * Read the current git SHA.\n *\n * Checks environment variables first (for CI), then reads from .git directory.\n *\n * @param cwd - Working directory to start search from\n * @returns Git SHA or undefined if not in a git repo\n */\nexport function readGitSha(cwd: string = process.cwd()): string | undefined {\n // Check CI environment variables first\n const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;\n if (envSha) return envSha;\n\n // Find .git directory\n const gitDir = findGitDir(cwd);\n if (!gitDir) return undefined;\n\n try {\n const headPath = path.join(gitDir, \"HEAD\");\n const head = fs.readFileSync(headPath, \"utf8\").trim();\n\n // If HEAD is a direct SHA (detached HEAD state)\n if (!head.startsWith(\"ref:\")) {\n return head;\n }\n\n // HEAD points to a ref, resolve it\n const refPath = head.replace(\"ref:\", \"\").trim();\n const refFile = path.join(gitDir, refPath);\n\n if (fs.existsSync(refFile)) {\n return fs.readFileSync(refFile, \"utf8\").trim();\n }\n\n // Check packed-refs\n const packedRefs = path.join(gitDir, \"packed-refs\");\n if (fs.existsSync(packedRefs)) {\n const content = fs.readFileSync(packedRefs, \"utf8\");\n for (const line of content.split(\"\\n\")) {\n if (!line || line.startsWith(\"#\") || line.startsWith(\"^\")) continue;\n const [sha, ref] = line.split(\" \");\n if (ref === refPath) return sha;\n }\n }\n\n return undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Find the .git directory starting from a given directory.\n *\n * @param start - Directory to start search from\n * @returns Path to .git directory or undefined if not found\n */\nexport function findGitDir(start: string): string | undefined {\n let current = start;\n while (true) {\n const candidate = path.join(current, \".git\");\n if (fs.existsSync(candidate)) {\n // Handle git worktrees where .git is a file\n const stat = fs.statSync(candidate);\n if (stat.isFile()) {\n const content = fs.readFileSync(candidate, \"utf8\").trim();\n const match = content.match(/^gitdir: (.+)$/);\n if (match) {\n return path.resolve(current, match[1]);\n }\n }\n return candidate;\n }\n const parent = path.dirname(current);\n if (parent === current) return undefined;\n current = parent;\n }\n}\n\n/**\n * Read the current branch name.\n *\n * @param cwd - Working directory\n * @returns Branch name or undefined\n */\nexport function readBranchName(cwd: string = process.cwd()): string | undefined {\n // Check CI environment variables first\n const envBranch = process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_REF_NAME;\n if (envBranch) return envBranch;\n\n const gitDir = findGitDir(cwd);\n if (!gitDir) return undefined;\n\n try {\n const headPath = path.join(gitDir, \"HEAD\");\n const head = fs.readFileSync(headPath, \"utf8\").trim();\n\n if (head.startsWith(\"ref:\")) {\n const refPath = head.replace(\"ref:\", \"\").trim();\n // Extract branch name from refs/heads/branch-name\n const match = refPath.match(/^refs\\/heads\\/(.+)$/);\n return match ? match[1] : refPath;\n }\n\n // Detached HEAD\n return undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Duration formatting utilities.\n */\n\n/**\n * Format milliseconds as human-readable duration.\n *\n * @param ms - Duration in milliseconds\n * @returns Formatted string (e.g., \"123 ms\", \"1.5 s\", \"2m 30s\")\n */\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`;\n }\n\n if (ms < 60000) {\n return `${(ms / 1000).toFixed(2)} s`;\n }\n\n const minutes = Math.floor(ms / 60000);\n const seconds = Math.floor((ms % 60000) / 1000);\n\n if (minutes < 60) {\n return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;\n }\n\n const hours = Math.floor(minutes / 60);\n const remainingMinutes = minutes % 60;\n return `${hours}h ${remainingMinutes}m`;\n}\n\n/**\n * Convert milliseconds to nanoseconds.\n *\n * Used for Cucumber JSON format which expects nanoseconds.\n *\n * @param ms - Duration in milliseconds\n * @returns Duration in nanoseconds\n */\nexport function msToNanoseconds(ms: number): number {\n return Math.round(ms * 1_000_000);\n}\n\n/**\n * Convert nanoseconds to milliseconds.\n *\n * @param ns - Duration in nanoseconds\n * @returns Duration in milliseconds\n */\nexport function nanosecondsToMs(ns: number): number {\n return ns / 1_000_000;\n}\n","/**\n * Metadata utilities for reading package information.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/** Cache for package versions by root directory */\nconst versionCache = new Map<string, string | undefined>();\n\n/**\n * Read the package version from the closest package.json.\n *\n * Results are cached by root directory.\n *\n * @param root - Directory to start searching from\n * @returns Package version string or undefined if not found\n */\nexport function readPackageVersion(root: string): string | undefined {\n if (versionCache.has(root)) {\n return versionCache.get(root);\n }\n\n const version = findPackageVersion(root);\n versionCache.set(root, version);\n return version;\n}\n\n/**\n * Find package.json version by walking up the directory tree.\n */\nfunction findPackageVersion(startDir: string): string | undefined {\n let current = path.resolve(startDir);\n\n while (true) {\n const pkgPath = path.join(current, \"package.json\");\n try {\n if (fs.existsSync(pkgPath)) {\n const raw = fs.readFileSync(pkgPath, \"utf8\");\n const parsed = JSON.parse(raw) as { version?: string };\n return parsed.version;\n }\n } catch {\n // Continue searching\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n // Reached root\n return undefined;\n }\n current = parent;\n }\n}\n\n/**\n * Clear the package version cache.\n * Useful for testing.\n */\nexport function clearVersionCache(): void {\n versionCache.clear();\n}\n","/**\n * CI environment auto-detection utility.\n *\n * Detects known CI providers from environment variables.\n * Precedence: GitHub Actions > CircleCI > Jenkins > Travis > GitLab CI > generic CI\n */\n\nimport type { RawCIInfo } from \"../types/raw\";\n\n/**\n * Detect CI environment from process.env.\n * Returns undefined when not running in CI.\n */\nexport function detectCI(\n env: Record<string, string | undefined> = process.env\n): RawCIInfo | undefined {\n // GitHub Actions\n if (env.GITHUB_ACTIONS === \"true\") {\n const url =\n env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : undefined;\n return {\n name: \"github\",\n buildNumber: env.GITHUB_RUN_NUMBER,\n url,\n };\n }\n\n // CircleCI\n if (env.CIRCLECI === \"true\") {\n return {\n name: \"circleci\",\n buildNumber: env.CIRCLE_BUILD_NUM,\n url: env.CIRCLE_BUILD_URL,\n };\n }\n\n // Jenkins\n if (env.JENKINS_URL !== undefined) {\n return {\n name: \"jenkins\",\n buildNumber: env.BUILD_NUMBER,\n url: env.BUILD_URL,\n };\n }\n\n // Travis CI\n if (env.TRAVIS === \"true\") {\n return {\n name: \"travis\",\n buildNumber: env.TRAVIS_BUILD_NUMBER,\n url: env.TRAVIS_BUILD_WEB_URL,\n };\n }\n\n // GitLab CI\n if (env.GITLAB_CI === \"true\") {\n return {\n name: \"gitlab\",\n buildNumber: env.CI_PIPELINE_IID,\n url: env.CI_PIPELINE_URL,\n };\n }\n\n // Generic CI (fallback)\n if (env.CI === \"true\") {\n return {\n name: \"ci\",\n };\n }\n\n return undefined;\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["path","groupBy","groupBy","basename","createHash","path","resolve","detectCI","detectCI","extractFeatureName","fs","path","fs","path","detectCI"]}
|