executable-stories-formatters 0.10.0 → 0.11.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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CIInfo, T as TestRunResult, a as TestCaseResult, S as StoryStep, D as DocEntry, b as TestStatus$1, N as NormalizedTicket, A as Attachment, c as DocPhase, O as OtelSpan, d as StepResult, e as CIProvider, R as RawStatus, f as RawAttachment, g as RawRun, h as RawCIInfo, i as adaptJestRun, j as adaptPlaywrightRun, k as adaptVitestRun } from './index-CbWFyoTx.cjs';
2
- export { l as CIInfo, m as CoverageSummary, J as JestAdapterOptions, n as JestAggregatedResult, o as JestFileResult, p as JestTestResult, q as OtelAttributeValue, P as PlaywrightAdapterOptions, r as PlaywrightAnnotation, s as PlaywrightAttachment, t as PlaywrightError, u as PlaywrightLocation, v as PlaywrightStatus, w as PlaywrightTestCase, x as PlaywrightTestResult, y as RawStepEvent, z as RawTestCase, B as STORY_META_KEY, E as StepKeyword, F as StepMode, G as StoryFileReport, H as StoryMeta, I as TestCaseAttempt, K as TestCaseEvidence, V as VitestAdapterOptions, L as VitestSerializedError, M as VitestState, Q as VitestTestCase, U as VitestTestModule, W as VitestTestResult, X as toCIInfo, Y as toRawCIInfo } from './index-CbWFyoTx.cjs';
1
+ import { C as CIInfo, T as TestRunResult, a as TestCaseResult, S as StoryStep, D as DocEntry, b as TestStatus$1, N as NormalizedTicket, A as Attachment, c as DocPhase, O as OtelSpan, d as StepResult, e as CIProvider, R as RawStatus, f as RawAttachment, g as RawRun, h as RawCIInfo, i as adaptJestRun, j as adaptPlaywrightRun, k as adaptVitestRun } from './index-DF16Xl5i.cjs';
2
+ export { l as CIInfo, m as CoverageSummary, J as JestAdapterOptions, n as JestAggregatedResult, o as JestFileResult, p as JestTestResult, q as OtelAttributeValue, P as PlaywrightAdapterOptions, r as PlaywrightAnnotation, s as PlaywrightAttachment, t as PlaywrightError, u as PlaywrightLocation, v as PlaywrightStatus, w as PlaywrightTestCase, x as PlaywrightTestResult, y as RawStepEvent, z as RawTestCase, B as STORY_META_KEY, E as StepKeyword, F as StepMode, G as StoryFileReport, H as StoryMeta, I as TestCaseAttempt, K as TestCaseEvidence, V as VitestAdapterOptions, L as VitestSerializedError, M as VitestState, Q as VitestTestCase, U as VitestTestModule, W as VitestTestResult, X as toCIInfo, Y as toRawCIInfo } from './index-DF16Xl5i.cjs';
3
3
 
4
4
  /**
5
5
  * Notification types for webhook integrations (Slack, Teams).
@@ -155,8 +155,14 @@ type OutputFormat = "astro" | "behavior-manifest-json" | "confluence" | "cucumbe
155
155
  type SortTestCasesMode = "id" | "source" | "none";
156
156
  /** Output mode for report routing */
157
157
  type OutputMode = "aggregated" | "colocated";
158
- /** Colocated output style */
159
- type ColocatedStyle = "mirrored" | "adjacent";
158
+ /**
159
+ * Colocated output style:
160
+ * - `mirrored` — preserve the source directory tree under outputDir (default)
161
+ * - `adjacent` — write next to each source file (ignores outputDir)
162
+ * - `flat` — one page per file directly under outputDir, named by its clean
163
+ * stem (e.g. `convert-currency.md`); best for a browsable docs nav
164
+ */
165
+ type ColocatedStyle = "mirrored" | "adjacent" | "flat";
160
166
  /** Output rule for routing reports based on source file patterns */
161
167
  interface OutputRule {
162
168
  /** Glob pattern to match sourceFile (uses micromatch, forward slashes) */
@@ -706,7 +712,7 @@ interface ReportCoverageSummary {
706
712
  functionsPct?: number;
707
713
  statementsPct?: number;
708
714
  }
709
- type ReportDocEntry = ReportDocNote | ReportDocTag | ReportDocKv | ReportDocCode | ReportDocTable | ReportDocLink | ReportDocSection | ReportDocMermaid | ReportDocScreenshot | ReportDocCustom;
715
+ type ReportDocEntry = ReportDocNote | ReportDocTag | ReportDocKv | ReportDocCode | ReportDocTable | ReportDocLink | ReportDocSection | ReportDocMermaid | ReportDocScreenshot | ReportDocVideo | ReportDocCustom;
710
716
  interface ReportDocNote {
711
717
  kind: "note";
712
718
  text: string;
@@ -770,6 +776,14 @@ interface ReportDocScreenshot {
770
776
  phase: DocPhase;
771
777
  children?: ReportDocEntry[];
772
778
  }
779
+ interface ReportDocVideo {
780
+ kind: "video";
781
+ path: string;
782
+ caption?: string;
783
+ poster?: string;
784
+ phase: DocPhase;
785
+ children?: ReportDocEntry[];
786
+ }
773
787
  interface ReportDocCustom {
774
788
  kind: "custom";
775
789
  type: string;
@@ -1987,13 +2001,27 @@ interface StarlightBadge {
1987
2001
  }
1988
2002
  interface AstroFormatterOptions {
1989
2003
  assetsBaseUrl?: string;
2004
+ /**
2005
+ * Title each page by its own suite/file rather than the configured title.
2006
+ * Set by colocated mode (one page per file) so the docs nav reads with
2007
+ * distinct, meaningful labels.
2008
+ */
2009
+ perFileTitle?: boolean;
1990
2010
  markdown?: Omit<MarkdownOptions, "includeFrontMatter" | "includeSummaryTable" | "includeMetadata" | "stepStyle">;
1991
2011
  }
1992
2012
  declare class AstroFormatter {
1993
2013
  private markdownFormatter;
1994
2014
  private title;
2015
+ private perFileTitle;
1995
2016
  constructor(options?: AstroFormatterOptions);
1996
2017
  format(run: TestRunResult): string;
2018
+ /**
2019
+ * Title for the page. A per-file page (one source file — i.e. colocated mode)
2020
+ * is titled by its suite/describe name, falling back to a humanized filename,
2021
+ * so the docs nav reads "Convert Currency" not "User Stories" six times over.
2022
+ * Multi-file (aggregated) pages keep the configured title.
2023
+ */
2024
+ private deriveTitle;
1997
2025
  private buildFrontmatter;
1998
2026
  static computeBadge(testCases: Pick<TestCaseResult, "status">[]): StarlightBadge;
1999
2027
  }
@@ -3038,6 +3066,14 @@ interface GenerateCompareResult {
3038
3066
  files: string[];
3039
3067
  diff: RunDiffResult;
3040
3068
  }
3069
+ /**
3070
+ * Join an output name with a format extension, collapsing a stutter when the
3071
+ * chosen name already carries the format's tag. With the default name "index",
3072
+ * `story-report-json` writes `index.story-report.json`; but if the caller names
3073
+ * the file `story-report`, this yields `story-report.json`, not
3074
+ * `story-report.story-report.json`.
3075
+ */
3076
+ declare function joinNameAndExt(name: string, ext: string): string;
3041
3077
  /**
3042
3078
  * High-level report generator that combines multiple formatters.
3043
3079
  *
@@ -3107,4 +3143,4 @@ declare function normalizeVitestResults(testModules: Parameters<typeof adaptVite
3107
3143
  */
3108
3144
  declare function normalizePlaywrightResults(testResults: Parameters<typeof adaptPlaywrightRun>[0], adapterOptions?: Parameters<typeof adaptPlaywrightRun>[1], canonicalizeOptions?: CanonicalizeOptions): TestRunResult;
3109
3145
 
3110
- export { type AstroAssetResult, AstroFormatter, type AstroFormatterOptions as AstroFormatterOpts, Attachment, type BehaviorDebuggerIssue, type BehaviorDiff, type BehaviorDiffEntry, type BehaviorManifest, BehaviorManifestJsonFormatter, type BehaviorManifestJsonOptions, type BehaviorSourceFile, type BehaviorTag, type BundleOptions, type BundleResult, CIProvider, type CanonicalizeOptions, type ChangeType, type ChangedFile, type ChangedFileReview, type ColocatedStyle, type CompareFormat, type CompareFormatterOptions, type ConfluenceAuth, ConfluenceFormatter, type ConfluenceFormatterOptions as ConfluenceFormatterOpts, type CopyMarkdownAssetsOptions, CucumberHtmlFormatter, type CucumberHtmlOptions, CucumberJsonFormatter, type CucumberJsonOptions, CucumberMessagesFormatter, type CucumberMessagesOptions, DocEntry, DocPhase, ES_THEME_TOKENS_CSS, ES_THEME_TOKEN_VALUES, type EvidenceStrength, type ExecutableStoriesConfig, type FetchFn, type FileChangeKind, type FlakinessLevel, type Formatter, type FormatterOptions, type GenerateArgs, type GenerateCompareResult, type GenerateDeps, type GenerateResult, type GenericWebhookNotifierOptions, type HistoryEntry, type HistoryStore, HtmlFormatter, type HtmlOptions, type HtmlTheme, type HtmlThemeName, type IJsonDataTable, type IJsonDocString, type IJsonEmbedding, type IJsonFeature, type IJsonScenario, type IJsonStep, type IJsonStepArgument, type IJsonStepResult, type IJsonTableRow, type IJsonTag, JUnitFormatter, type JUnitOptions, type JiraAuth, type JiraPublishMode, type ListScenariosArgs, type ListScenariosDeps, type Logger, MIN_FLAKINESS_SAMPLES, MIN_METRIC_SAMPLES, MIN_PERF_SAMPLES, MarkdownFormatter, type MarkdownFormatterOptions, type MarkdownOptions, type MarkdownRenderers, NormalizedTicket, type NotificationSummary, type NotifyCondition, OtelSpan, type OtelTraceContext, type OutputConfig, type OutputFormat, type OutputMode, type OutputRule, type PerformanceTrend, type PublishConfluenceArgs, type PublishConfluenceDeps, type PublishConfluenceResult, type PublishJiraArgs, type PublishJiraDeps, type PublishJiraResult, RawAttachment, RawCIInfo, RawRun, RawStatus, type ReportAttachment, type ReportCIInfo, type ReportCoverageSummary, type ReportDocCode, type ReportDocCustom, type ReportDocEntry, type ReportDocKv, type ReportDocLink, type ReportDocMermaid, type ReportDocNote, type ReportDocScreenshot, type ReportDocSection, type ReportDocTable, type ReportDocTag, type ReportFeature, ReportGenerator, type ReportScenario, type ReportStep, type ReportSummary, type ReportTicket, type ResolvedFormatterOptions, type ReviewAudience, type ReviewBand, type ReviewClaim, type ReviewContext, ReviewHtmlFormatter, type ReviewHtmlOptions, ReviewMarkdownFormatter, type ReviewMarkdownOptions, type ReviewResult, type ReviewSummary, RunDiffHtmlFormatter, type RunDiffHtmlOptions, RunDiffMarkdownFormatter, type RunDiffMarkdownOptions, type RunDiffResult, type RunDiffSummary, STORY_REPORT_SCHEMA_MAJOR, STORY_REPORT_SCHEMA_VERSION, type ScenarioChangeFlags, type ScenarioChangeKind, type ScenarioDiff, type ScenarioIndex, type ScenarioIndexFilters, type ScenarioIndexItem, ScenarioIndexJsonFormatter, type ScenarioIndexJsonOptions, type ScenarioIndexStep, type ScenarioSnapshot, type SortTestCasesMode, type StabilityGrade, type StarlightBadge, StepResult, type StoryReport, StoryReportJsonFormatter, type StoryReportJsonOptions, type StoryReportSchemaVersion, StoryStep, TestCaseResult, type TestHistory, type TestMetrics, TestRunResult, TestStatus$1 as TestStatus, CIInfo as TypedCIInfo, type ValidationResult, type WatchDeps, type WatchHandle, type WatchOptions, type WebhookPayload, type WebhookSignerHmac, type WriteFile, adaptJestRun, adaptPlaywrightRun, adaptVitestRun, assertValidRun, buildReview, bundleAssets, calculateFlakiness, calculateStability, canonicalizeRun, classifyStatusChange, clearVersionCache, computeTestMetrics, copyMarkdownAssets, createPrCommentSummary, createReportGenerator, deriveAudience, deriveChangeType, deriveStepResults, detectCI, detectPerformanceTrend, diffRuns, diffStoryReports, findGitDir, formatDuration, generateRunComparison, generateRunId, generateTestCaseId, getAvailableThemes, getCssOnlyThemes, gradeEvidence, hasSufficientHistory, isReviewableSource, isTestFile, listScenarios, loadHistory, mergeStepResults, msToNanoseconds, nanosecondsToMs, normalizeJestResults, normalizePlaywrightResults, normalizeStatus, normalizeVitestResults, parseEnvelopes, parseNdjson, publishConfluencePage, publishJiraIssue, readBranchName, readGitSha, readPackageVersion, regenerateArtifacts, resolveAttachment, resolveAttachments, resolveTheme, resolveTraceUrl, rewriteAssetPaths, saveHistory, scenariosCoveringPaths, sendNotifications, sendSlackNotification, sendTeamsNotification, sendWebhookNotification, signBody, slugify, startWatch, stripAnsi, toBehaviorManifest, toScenarioIndex, toStoryReport, tryGetActiveOtelContext, updateHistory, validateCanonicalRun };
3146
+ export { type AstroAssetResult, AstroFormatter, type AstroFormatterOptions as AstroFormatterOpts, Attachment, type BehaviorDebuggerIssue, type BehaviorDiff, type BehaviorDiffEntry, type BehaviorManifest, BehaviorManifestJsonFormatter, type BehaviorManifestJsonOptions, type BehaviorSourceFile, type BehaviorTag, type BundleOptions, type BundleResult, CIProvider, type CanonicalizeOptions, type ChangeType, type ChangedFile, type ChangedFileReview, type ColocatedStyle, type CompareFormat, type CompareFormatterOptions, type ConfluenceAuth, ConfluenceFormatter, type ConfluenceFormatterOptions as ConfluenceFormatterOpts, type CopyMarkdownAssetsOptions, CucumberHtmlFormatter, type CucumberHtmlOptions, CucumberJsonFormatter, type CucumberJsonOptions, CucumberMessagesFormatter, type CucumberMessagesOptions, DocEntry, DocPhase, ES_THEME_TOKENS_CSS, ES_THEME_TOKEN_VALUES, type EvidenceStrength, type ExecutableStoriesConfig, type FetchFn, type FileChangeKind, type FlakinessLevel, type Formatter, type FormatterOptions, type GenerateArgs, type GenerateCompareResult, type GenerateDeps, type GenerateResult, type GenericWebhookNotifierOptions, type HistoryEntry, type HistoryStore, HtmlFormatter, type HtmlOptions, type HtmlTheme, type HtmlThemeName, type IJsonDataTable, type IJsonDocString, type IJsonEmbedding, type IJsonFeature, type IJsonScenario, type IJsonStep, type IJsonStepArgument, type IJsonStepResult, type IJsonTableRow, type IJsonTag, JUnitFormatter, type JUnitOptions, type JiraAuth, type JiraPublishMode, type ListScenariosArgs, type ListScenariosDeps, type Logger, MIN_FLAKINESS_SAMPLES, MIN_METRIC_SAMPLES, MIN_PERF_SAMPLES, MarkdownFormatter, type MarkdownFormatterOptions, type MarkdownOptions, type MarkdownRenderers, NormalizedTicket, type NotificationSummary, type NotifyCondition, OtelSpan, type OtelTraceContext, type OutputConfig, type OutputFormat, type OutputMode, type OutputRule, type PerformanceTrend, type PublishConfluenceArgs, type PublishConfluenceDeps, type PublishConfluenceResult, type PublishJiraArgs, type PublishJiraDeps, type PublishJiraResult, RawAttachment, RawCIInfo, RawRun, RawStatus, type ReportAttachment, type ReportCIInfo, type ReportCoverageSummary, type ReportDocCode, type ReportDocCustom, type ReportDocEntry, type ReportDocKv, type ReportDocLink, type ReportDocMermaid, type ReportDocNote, type ReportDocScreenshot, type ReportDocSection, type ReportDocTable, type ReportDocTag, type ReportFeature, ReportGenerator, type ReportScenario, type ReportStep, type ReportSummary, type ReportTicket, type ResolvedFormatterOptions, type ReviewAudience, type ReviewBand, type ReviewClaim, type ReviewContext, ReviewHtmlFormatter, type ReviewHtmlOptions, ReviewMarkdownFormatter, type ReviewMarkdownOptions, type ReviewResult, type ReviewSummary, RunDiffHtmlFormatter, type RunDiffHtmlOptions, RunDiffMarkdownFormatter, type RunDiffMarkdownOptions, type RunDiffResult, type RunDiffSummary, STORY_REPORT_SCHEMA_MAJOR, STORY_REPORT_SCHEMA_VERSION, type ScenarioChangeFlags, type ScenarioChangeKind, type ScenarioDiff, type ScenarioIndex, type ScenarioIndexFilters, type ScenarioIndexItem, ScenarioIndexJsonFormatter, type ScenarioIndexJsonOptions, type ScenarioIndexStep, type ScenarioSnapshot, type SortTestCasesMode, type StabilityGrade, type StarlightBadge, StepResult, type StoryReport, StoryReportJsonFormatter, type StoryReportJsonOptions, type StoryReportSchemaVersion, StoryStep, TestCaseResult, type TestHistory, type TestMetrics, TestRunResult, TestStatus$1 as TestStatus, CIInfo as TypedCIInfo, type ValidationResult, type WatchDeps, type WatchHandle, type WatchOptions, type WebhookPayload, type WebhookSignerHmac, type WriteFile, adaptJestRun, adaptPlaywrightRun, adaptVitestRun, assertValidRun, buildReview, bundleAssets, calculateFlakiness, calculateStability, canonicalizeRun, classifyStatusChange, clearVersionCache, computeTestMetrics, copyMarkdownAssets, createPrCommentSummary, createReportGenerator, deriveAudience, deriveChangeType, deriveStepResults, detectCI, detectPerformanceTrend, diffRuns, diffStoryReports, findGitDir, formatDuration, generateRunComparison, generateRunId, generateTestCaseId, getAvailableThemes, getCssOnlyThemes, gradeEvidence, hasSufficientHistory, isReviewableSource, isTestFile, joinNameAndExt, listScenarios, loadHistory, mergeStepResults, msToNanoseconds, nanosecondsToMs, normalizeJestResults, normalizePlaywrightResults, normalizeStatus, normalizeVitestResults, parseEnvelopes, parseNdjson, publishConfluencePage, publishJiraIssue, readBranchName, readGitSha, readPackageVersion, regenerateArtifacts, resolveAttachment, resolveAttachments, resolveTheme, resolveTraceUrl, rewriteAssetPaths, saveHistory, scenariosCoveringPaths, sendNotifications, sendSlackNotification, sendTeamsNotification, sendWebhookNotification, signBody, slugify, startWatch, stripAnsi, toBehaviorManifest, toScenarioIndex, toStoryReport, tryGetActiveOtelContext, updateHistory, validateCanonicalRun };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CIInfo, T as TestRunResult, a as TestCaseResult, S as StoryStep, D as DocEntry, b as TestStatus$1, N as NormalizedTicket, A as Attachment, c as DocPhase, O as OtelSpan, d as StepResult, e as CIProvider, R as RawStatus, f as RawAttachment, g as RawRun, h as RawCIInfo, i as adaptJestRun, j as adaptPlaywrightRun, k as adaptVitestRun } from './index-CbWFyoTx.js';
2
- export { l as CIInfo, m as CoverageSummary, J as JestAdapterOptions, n as JestAggregatedResult, o as JestFileResult, p as JestTestResult, q as OtelAttributeValue, P as PlaywrightAdapterOptions, r as PlaywrightAnnotation, s as PlaywrightAttachment, t as PlaywrightError, u as PlaywrightLocation, v as PlaywrightStatus, w as PlaywrightTestCase, x as PlaywrightTestResult, y as RawStepEvent, z as RawTestCase, B as STORY_META_KEY, E as StepKeyword, F as StepMode, G as StoryFileReport, H as StoryMeta, I as TestCaseAttempt, K as TestCaseEvidence, V as VitestAdapterOptions, L as VitestSerializedError, M as VitestState, Q as VitestTestCase, U as VitestTestModule, W as VitestTestResult, X as toCIInfo, Y as toRawCIInfo } from './index-CbWFyoTx.js';
1
+ import { C as CIInfo, T as TestRunResult, a as TestCaseResult, S as StoryStep, D as DocEntry, b as TestStatus$1, N as NormalizedTicket, A as Attachment, c as DocPhase, O as OtelSpan, d as StepResult, e as CIProvider, R as RawStatus, f as RawAttachment, g as RawRun, h as RawCIInfo, i as adaptJestRun, j as adaptPlaywrightRun, k as adaptVitestRun } from './index-DF16Xl5i.js';
2
+ export { l as CIInfo, m as CoverageSummary, J as JestAdapterOptions, n as JestAggregatedResult, o as JestFileResult, p as JestTestResult, q as OtelAttributeValue, P as PlaywrightAdapterOptions, r as PlaywrightAnnotation, s as PlaywrightAttachment, t as PlaywrightError, u as PlaywrightLocation, v as PlaywrightStatus, w as PlaywrightTestCase, x as PlaywrightTestResult, y as RawStepEvent, z as RawTestCase, B as STORY_META_KEY, E as StepKeyword, F as StepMode, G as StoryFileReport, H as StoryMeta, I as TestCaseAttempt, K as TestCaseEvidence, V as VitestAdapterOptions, L as VitestSerializedError, M as VitestState, Q as VitestTestCase, U as VitestTestModule, W as VitestTestResult, X as toCIInfo, Y as toRawCIInfo } from './index-DF16Xl5i.js';
3
3
 
4
4
  /**
5
5
  * Notification types for webhook integrations (Slack, Teams).
@@ -155,8 +155,14 @@ type OutputFormat = "astro" | "behavior-manifest-json" | "confluence" | "cucumbe
155
155
  type SortTestCasesMode = "id" | "source" | "none";
156
156
  /** Output mode for report routing */
157
157
  type OutputMode = "aggregated" | "colocated";
158
- /** Colocated output style */
159
- type ColocatedStyle = "mirrored" | "adjacent";
158
+ /**
159
+ * Colocated output style:
160
+ * - `mirrored` — preserve the source directory tree under outputDir (default)
161
+ * - `adjacent` — write next to each source file (ignores outputDir)
162
+ * - `flat` — one page per file directly under outputDir, named by its clean
163
+ * stem (e.g. `convert-currency.md`); best for a browsable docs nav
164
+ */
165
+ type ColocatedStyle = "mirrored" | "adjacent" | "flat";
160
166
  /** Output rule for routing reports based on source file patterns */
161
167
  interface OutputRule {
162
168
  /** Glob pattern to match sourceFile (uses micromatch, forward slashes) */
@@ -706,7 +712,7 @@ interface ReportCoverageSummary {
706
712
  functionsPct?: number;
707
713
  statementsPct?: number;
708
714
  }
709
- type ReportDocEntry = ReportDocNote | ReportDocTag | ReportDocKv | ReportDocCode | ReportDocTable | ReportDocLink | ReportDocSection | ReportDocMermaid | ReportDocScreenshot | ReportDocCustom;
715
+ type ReportDocEntry = ReportDocNote | ReportDocTag | ReportDocKv | ReportDocCode | ReportDocTable | ReportDocLink | ReportDocSection | ReportDocMermaid | ReportDocScreenshot | ReportDocVideo | ReportDocCustom;
710
716
  interface ReportDocNote {
711
717
  kind: "note";
712
718
  text: string;
@@ -770,6 +776,14 @@ interface ReportDocScreenshot {
770
776
  phase: DocPhase;
771
777
  children?: ReportDocEntry[];
772
778
  }
779
+ interface ReportDocVideo {
780
+ kind: "video";
781
+ path: string;
782
+ caption?: string;
783
+ poster?: string;
784
+ phase: DocPhase;
785
+ children?: ReportDocEntry[];
786
+ }
773
787
  interface ReportDocCustom {
774
788
  kind: "custom";
775
789
  type: string;
@@ -1987,13 +2001,27 @@ interface StarlightBadge {
1987
2001
  }
1988
2002
  interface AstroFormatterOptions {
1989
2003
  assetsBaseUrl?: string;
2004
+ /**
2005
+ * Title each page by its own suite/file rather than the configured title.
2006
+ * Set by colocated mode (one page per file) so the docs nav reads with
2007
+ * distinct, meaningful labels.
2008
+ */
2009
+ perFileTitle?: boolean;
1990
2010
  markdown?: Omit<MarkdownOptions, "includeFrontMatter" | "includeSummaryTable" | "includeMetadata" | "stepStyle">;
1991
2011
  }
1992
2012
  declare class AstroFormatter {
1993
2013
  private markdownFormatter;
1994
2014
  private title;
2015
+ private perFileTitle;
1995
2016
  constructor(options?: AstroFormatterOptions);
1996
2017
  format(run: TestRunResult): string;
2018
+ /**
2019
+ * Title for the page. A per-file page (one source file — i.e. colocated mode)
2020
+ * is titled by its suite/describe name, falling back to a humanized filename,
2021
+ * so the docs nav reads "Convert Currency" not "User Stories" six times over.
2022
+ * Multi-file (aggregated) pages keep the configured title.
2023
+ */
2024
+ private deriveTitle;
1997
2025
  private buildFrontmatter;
1998
2026
  static computeBadge(testCases: Pick<TestCaseResult, "status">[]): StarlightBadge;
1999
2027
  }
@@ -3038,6 +3066,14 @@ interface GenerateCompareResult {
3038
3066
  files: string[];
3039
3067
  diff: RunDiffResult;
3040
3068
  }
3069
+ /**
3070
+ * Join an output name with a format extension, collapsing a stutter when the
3071
+ * chosen name already carries the format's tag. With the default name "index",
3072
+ * `story-report-json` writes `index.story-report.json`; but if the caller names
3073
+ * the file `story-report`, this yields `story-report.json`, not
3074
+ * `story-report.story-report.json`.
3075
+ */
3076
+ declare function joinNameAndExt(name: string, ext: string): string;
3041
3077
  /**
3042
3078
  * High-level report generator that combines multiple formatters.
3043
3079
  *
@@ -3107,4 +3143,4 @@ declare function normalizeVitestResults(testModules: Parameters<typeof adaptVite
3107
3143
  */
3108
3144
  declare function normalizePlaywrightResults(testResults: Parameters<typeof adaptPlaywrightRun>[0], adapterOptions?: Parameters<typeof adaptPlaywrightRun>[1], canonicalizeOptions?: CanonicalizeOptions): TestRunResult;
3109
3145
 
3110
- export { type AstroAssetResult, AstroFormatter, type AstroFormatterOptions as AstroFormatterOpts, Attachment, type BehaviorDebuggerIssue, type BehaviorDiff, type BehaviorDiffEntry, type BehaviorManifest, BehaviorManifestJsonFormatter, type BehaviorManifestJsonOptions, type BehaviorSourceFile, type BehaviorTag, type BundleOptions, type BundleResult, CIProvider, type CanonicalizeOptions, type ChangeType, type ChangedFile, type ChangedFileReview, type ColocatedStyle, type CompareFormat, type CompareFormatterOptions, type ConfluenceAuth, ConfluenceFormatter, type ConfluenceFormatterOptions as ConfluenceFormatterOpts, type CopyMarkdownAssetsOptions, CucumberHtmlFormatter, type CucumberHtmlOptions, CucumberJsonFormatter, type CucumberJsonOptions, CucumberMessagesFormatter, type CucumberMessagesOptions, DocEntry, DocPhase, ES_THEME_TOKENS_CSS, ES_THEME_TOKEN_VALUES, type EvidenceStrength, type ExecutableStoriesConfig, type FetchFn, type FileChangeKind, type FlakinessLevel, type Formatter, type FormatterOptions, type GenerateArgs, type GenerateCompareResult, type GenerateDeps, type GenerateResult, type GenericWebhookNotifierOptions, type HistoryEntry, type HistoryStore, HtmlFormatter, type HtmlOptions, type HtmlTheme, type HtmlThemeName, type IJsonDataTable, type IJsonDocString, type IJsonEmbedding, type IJsonFeature, type IJsonScenario, type IJsonStep, type IJsonStepArgument, type IJsonStepResult, type IJsonTableRow, type IJsonTag, JUnitFormatter, type JUnitOptions, type JiraAuth, type JiraPublishMode, type ListScenariosArgs, type ListScenariosDeps, type Logger, MIN_FLAKINESS_SAMPLES, MIN_METRIC_SAMPLES, MIN_PERF_SAMPLES, MarkdownFormatter, type MarkdownFormatterOptions, type MarkdownOptions, type MarkdownRenderers, NormalizedTicket, type NotificationSummary, type NotifyCondition, OtelSpan, type OtelTraceContext, type OutputConfig, type OutputFormat, type OutputMode, type OutputRule, type PerformanceTrend, type PublishConfluenceArgs, type PublishConfluenceDeps, type PublishConfluenceResult, type PublishJiraArgs, type PublishJiraDeps, type PublishJiraResult, RawAttachment, RawCIInfo, RawRun, RawStatus, type ReportAttachment, type ReportCIInfo, type ReportCoverageSummary, type ReportDocCode, type ReportDocCustom, type ReportDocEntry, type ReportDocKv, type ReportDocLink, type ReportDocMermaid, type ReportDocNote, type ReportDocScreenshot, type ReportDocSection, type ReportDocTable, type ReportDocTag, type ReportFeature, ReportGenerator, type ReportScenario, type ReportStep, type ReportSummary, type ReportTicket, type ResolvedFormatterOptions, type ReviewAudience, type ReviewBand, type ReviewClaim, type ReviewContext, ReviewHtmlFormatter, type ReviewHtmlOptions, ReviewMarkdownFormatter, type ReviewMarkdownOptions, type ReviewResult, type ReviewSummary, RunDiffHtmlFormatter, type RunDiffHtmlOptions, RunDiffMarkdownFormatter, type RunDiffMarkdownOptions, type RunDiffResult, type RunDiffSummary, STORY_REPORT_SCHEMA_MAJOR, STORY_REPORT_SCHEMA_VERSION, type ScenarioChangeFlags, type ScenarioChangeKind, type ScenarioDiff, type ScenarioIndex, type ScenarioIndexFilters, type ScenarioIndexItem, ScenarioIndexJsonFormatter, type ScenarioIndexJsonOptions, type ScenarioIndexStep, type ScenarioSnapshot, type SortTestCasesMode, type StabilityGrade, type StarlightBadge, StepResult, type StoryReport, StoryReportJsonFormatter, type StoryReportJsonOptions, type StoryReportSchemaVersion, StoryStep, TestCaseResult, type TestHistory, type TestMetrics, TestRunResult, TestStatus$1 as TestStatus, CIInfo as TypedCIInfo, type ValidationResult, type WatchDeps, type WatchHandle, type WatchOptions, type WebhookPayload, type WebhookSignerHmac, type WriteFile, adaptJestRun, adaptPlaywrightRun, adaptVitestRun, assertValidRun, buildReview, bundleAssets, calculateFlakiness, calculateStability, canonicalizeRun, classifyStatusChange, clearVersionCache, computeTestMetrics, copyMarkdownAssets, createPrCommentSummary, createReportGenerator, deriveAudience, deriveChangeType, deriveStepResults, detectCI, detectPerformanceTrend, diffRuns, diffStoryReports, findGitDir, formatDuration, generateRunComparison, generateRunId, generateTestCaseId, getAvailableThemes, getCssOnlyThemes, gradeEvidence, hasSufficientHistory, isReviewableSource, isTestFile, listScenarios, loadHistory, mergeStepResults, msToNanoseconds, nanosecondsToMs, normalizeJestResults, normalizePlaywrightResults, normalizeStatus, normalizeVitestResults, parseEnvelopes, parseNdjson, publishConfluencePage, publishJiraIssue, readBranchName, readGitSha, readPackageVersion, regenerateArtifacts, resolveAttachment, resolveAttachments, resolveTheme, resolveTraceUrl, rewriteAssetPaths, saveHistory, scenariosCoveringPaths, sendNotifications, sendSlackNotification, sendTeamsNotification, sendWebhookNotification, signBody, slugify, startWatch, stripAnsi, toBehaviorManifest, toScenarioIndex, toStoryReport, tryGetActiveOtelContext, updateHistory, validateCanonicalRun };
3146
+ export { type AstroAssetResult, AstroFormatter, type AstroFormatterOptions as AstroFormatterOpts, Attachment, type BehaviorDebuggerIssue, type BehaviorDiff, type BehaviorDiffEntry, type BehaviorManifest, BehaviorManifestJsonFormatter, type BehaviorManifestJsonOptions, type BehaviorSourceFile, type BehaviorTag, type BundleOptions, type BundleResult, CIProvider, type CanonicalizeOptions, type ChangeType, type ChangedFile, type ChangedFileReview, type ColocatedStyle, type CompareFormat, type CompareFormatterOptions, type ConfluenceAuth, ConfluenceFormatter, type ConfluenceFormatterOptions as ConfluenceFormatterOpts, type CopyMarkdownAssetsOptions, CucumberHtmlFormatter, type CucumberHtmlOptions, CucumberJsonFormatter, type CucumberJsonOptions, CucumberMessagesFormatter, type CucumberMessagesOptions, DocEntry, DocPhase, ES_THEME_TOKENS_CSS, ES_THEME_TOKEN_VALUES, type EvidenceStrength, type ExecutableStoriesConfig, type FetchFn, type FileChangeKind, type FlakinessLevel, type Formatter, type FormatterOptions, type GenerateArgs, type GenerateCompareResult, type GenerateDeps, type GenerateResult, type GenericWebhookNotifierOptions, type HistoryEntry, type HistoryStore, HtmlFormatter, type HtmlOptions, type HtmlTheme, type HtmlThemeName, type IJsonDataTable, type IJsonDocString, type IJsonEmbedding, type IJsonFeature, type IJsonScenario, type IJsonStep, type IJsonStepArgument, type IJsonStepResult, type IJsonTableRow, type IJsonTag, JUnitFormatter, type JUnitOptions, type JiraAuth, type JiraPublishMode, type ListScenariosArgs, type ListScenariosDeps, type Logger, MIN_FLAKINESS_SAMPLES, MIN_METRIC_SAMPLES, MIN_PERF_SAMPLES, MarkdownFormatter, type MarkdownFormatterOptions, type MarkdownOptions, type MarkdownRenderers, NormalizedTicket, type NotificationSummary, type NotifyCondition, OtelSpan, type OtelTraceContext, type OutputConfig, type OutputFormat, type OutputMode, type OutputRule, type PerformanceTrend, type PublishConfluenceArgs, type PublishConfluenceDeps, type PublishConfluenceResult, type PublishJiraArgs, type PublishJiraDeps, type PublishJiraResult, RawAttachment, RawCIInfo, RawRun, RawStatus, type ReportAttachment, type ReportCIInfo, type ReportCoverageSummary, type ReportDocCode, type ReportDocCustom, type ReportDocEntry, type ReportDocKv, type ReportDocLink, type ReportDocMermaid, type ReportDocNote, type ReportDocScreenshot, type ReportDocSection, type ReportDocTable, type ReportDocTag, type ReportFeature, ReportGenerator, type ReportScenario, type ReportStep, type ReportSummary, type ReportTicket, type ResolvedFormatterOptions, type ReviewAudience, type ReviewBand, type ReviewClaim, type ReviewContext, ReviewHtmlFormatter, type ReviewHtmlOptions, ReviewMarkdownFormatter, type ReviewMarkdownOptions, type ReviewResult, type ReviewSummary, RunDiffHtmlFormatter, type RunDiffHtmlOptions, RunDiffMarkdownFormatter, type RunDiffMarkdownOptions, type RunDiffResult, type RunDiffSummary, STORY_REPORT_SCHEMA_MAJOR, STORY_REPORT_SCHEMA_VERSION, type ScenarioChangeFlags, type ScenarioChangeKind, type ScenarioDiff, type ScenarioIndex, type ScenarioIndexFilters, type ScenarioIndexItem, ScenarioIndexJsonFormatter, type ScenarioIndexJsonOptions, type ScenarioIndexStep, type ScenarioSnapshot, type SortTestCasesMode, type StabilityGrade, type StarlightBadge, StepResult, type StoryReport, StoryReportJsonFormatter, type StoryReportJsonOptions, type StoryReportSchemaVersion, StoryStep, TestCaseResult, type TestHistory, type TestMetrics, TestRunResult, TestStatus$1 as TestStatus, CIInfo as TypedCIInfo, type ValidationResult, type WatchDeps, type WatchHandle, type WatchOptions, type WebhookPayload, type WebhookSignerHmac, type WriteFile, adaptJestRun, adaptPlaywrightRun, adaptVitestRun, assertValidRun, buildReview, bundleAssets, calculateFlakiness, calculateStability, canonicalizeRun, classifyStatusChange, clearVersionCache, computeTestMetrics, copyMarkdownAssets, createPrCommentSummary, createReportGenerator, deriveAudience, deriveChangeType, deriveStepResults, detectCI, detectPerformanceTrend, diffRuns, diffStoryReports, findGitDir, formatDuration, generateRunComparison, generateRunId, generateTestCaseId, getAvailableThemes, getCssOnlyThemes, gradeEvidence, hasSufficientHistory, isReviewableSource, isTestFile, joinNameAndExt, listScenarios, loadHistory, mergeStepResults, msToNanoseconds, nanosecondsToMs, normalizeJestResults, normalizePlaywrightResults, normalizeStatus, normalizeVitestResults, parseEnvelopes, parseNdjson, publishConfluencePage, publishJiraIssue, readBranchName, readGitSha, readPackageVersion, regenerateArtifacts, resolveAttachment, resolveAttachments, resolveTheme, resolveTraceUrl, rewriteAssetPaths, saveHistory, scenariosCoveringPaths, sendNotifications, sendSlackNotification, sendTeamsNotification, sendWebhookNotification, signBody, slugify, startWatch, stripAnsi, toBehaviorManifest, toScenarioIndex, toStoryReport, tryGetActiveOtelContext, updateHistory, validateCanonicalRun };
package/dist/index.js CHANGED
@@ -763,6 +763,15 @@ function copyDocEntry(entry) {
763
763
  phase: entry.phase,
764
764
  ...children
765
765
  };
766
+ case "video":
767
+ return {
768
+ kind: "video",
769
+ path: entry.path,
770
+ ...entry.caption ? { caption: entry.caption } : {},
771
+ ...entry.poster ? { poster: entry.poster } : {},
772
+ phase: entry.phase,
773
+ ...children
774
+ };
766
775
  case "custom":
767
776
  return {
768
777
  kind: "custom",
@@ -13924,6 +13933,23 @@ function renderDocScreenshot(entry, deps) {
13924
13933
  ${entry.alt ? `<div class="doc-screenshot-caption">${deps.escapeHtml(entry.alt)}</div>` : ""}
13925
13934
  </div>`;
13926
13935
  }
13936
+ function renderDocVideo(entry, deps) {
13937
+ const isRemote = /^(?:https?:|data:)/i.test(entry.path);
13938
+ const isAbsoluteFsPath = !isRemote && /^(?:[/\\]|[A-Za-z]:[/\\])/.test(entry.path);
13939
+ const captionHtml = entry.caption ? `<div class="doc-video-caption">${deps.escapeHtml(entry.caption)}</div>` : "";
13940
+ if ((deps.embedScreenshots ?? true) && isAbsoluteFsPath) {
13941
+ return `<div class="doc-video doc-video-missing">
13942
+ <div class="doc-video-missing-label">Video unavailable</div>
13943
+ <div class="doc-video-missing-path">${deps.escapeHtml(entry.path)}</div>
13944
+ ${captionHtml}
13945
+ </div>`;
13946
+ }
13947
+ const poster = entry.poster ? ` poster="${deps.escapeHtml(entry.poster)}"` : "";
13948
+ return `<div class="doc-video">
13949
+ <video class="doc-video-player" controls preload="metadata"${poster} src="${deps.escapeHtml(entry.path)}"></video>
13950
+ ${captionHtml}
13951
+ </div>`;
13952
+ }
13927
13953
  function renderDocCustom(entry, deps) {
13928
13954
  if (entry.type === "visual" && entry.data && typeof entry.data === "object") {
13929
13955
  const data = entry.data;
@@ -13977,6 +14003,9 @@ function renderDocEntry(entry, deps) {
13977
14003
  case "screenshot":
13978
14004
  html = renderDocScreenshot(entry, deps);
13979
14005
  break;
14006
+ case "video":
14007
+ html = renderDocVideo(entry, deps);
14008
+ break;
13980
14009
  case "custom":
13981
14010
  html = renderDocCustom(entry, deps);
13982
14011
  break;
@@ -15303,6 +15332,19 @@ var MarkdownFormatter = class {
15303
15332
  case "screenshot":
15304
15333
  lines.push(`${indent}![${entry.alt ?? "Screenshot"}](${entry.path})`);
15305
15334
  break;
15335
+ case "video": {
15336
+ const poster = entry.poster ? ` poster="${entry.poster}"` : "";
15337
+ lines.push(`${indent}`);
15338
+ lines.push(`${indent}<video controls preload="metadata"${poster} class="doc-video">`);
15339
+ lines.push(`${indent} <source src="${entry.path}" />`);
15340
+ lines.push(`${indent}</video>`);
15341
+ if (entry.caption) {
15342
+ lines.push(`${indent}`);
15343
+ lines.push(`${indent}*${entry.caption}*`);
15344
+ }
15345
+ lines.push(`${indent}`);
15346
+ break;
15347
+ }
15306
15348
  case "custom":
15307
15349
  if (entry.type === "visual" && entry.data && typeof entry.data === "object") {
15308
15350
  const data = entry.data;
@@ -16462,6 +16504,8 @@ function formatDocEntry(doc) {
16462
16504
  return `${escapeHtml2(doc.title ?? "mermaid diagram")}: <code>${escapeHtml2(doc.code)}</code>`;
16463
16505
  case "screenshot":
16464
16506
  return `${doc.alt ? `${escapeHtml2(doc.alt)}: ` : ""}${escapeHtml2(doc.path)}`;
16507
+ case "video":
16508
+ return `${doc.caption ? `${escapeHtml2(doc.caption)}: ` : ""}${escapeHtml2(doc.path)}`;
16465
16509
  case "custom":
16466
16510
  return `${escapeHtml2(doc.type)}: ${escapeHtml2(JSON.stringify(doc.data))}`;
16467
16511
  }
@@ -16918,6 +16962,8 @@ function formatDocEntry2(doc) {
16918
16962
  return `${doc.title ?? "mermaid diagram"}: \`${doc.code}\``;
16919
16963
  case "screenshot":
16920
16964
  return `${doc.alt ? `${doc.alt}: ` : ""}${doc.path}`;
16965
+ case "video":
16966
+ return `${doc.caption ? `${doc.caption}: ` : ""}${doc.path}`;
16921
16967
  case "custom":
16922
16968
  return `${doc.type}: ${JSON.stringify(doc.data)}`;
16923
16969
  }
@@ -17163,19 +17209,35 @@ function replaceAssetRef(html, original, replacement) {
17163
17209
  return html;
17164
17210
  }
17165
17211
 
17212
+ // src/utils/source-file.ts
17213
+ function cleanTestStem(fileName) {
17214
+ const base = fileName.split(/[\\/]/).pop() ?? fileName;
17215
+ const stripped = base.replace(/\.(story\.)?(test|spec|cy)\.[cm]?[jt]sx?$/i, "");
17216
+ if (stripped !== base) return stripped;
17217
+ return base.replace(/\.[^.]+$/, "");
17218
+ }
17219
+ function humanizeSourceFile(fileName) {
17220
+ return cleanTestStem(fileName).split(/[-_.\s]+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
17221
+ }
17222
+
17166
17223
  // src/formatters/astro.ts
17167
17224
  var AstroFormatter = class _AstroFormatter {
17168
17225
  markdownFormatter;
17169
17226
  title;
17227
+ perFileTitle;
17170
17228
  constructor(options = {}) {
17171
17229
  this.title = options.markdown?.title ?? "User Stories";
17230
+ this.perFileTitle = options.perFileTitle ?? false;
17172
17231
  this.markdownFormatter = new MarkdownFormatter({
17173
17232
  ...options.markdown,
17174
17233
  title: this.title,
17175
17234
  stepStyle: "gherkin",
17176
17235
  includeFrontMatter: false,
17177
17236
  includeSummaryTable: false,
17178
- includeMetadata: false
17237
+ includeMetadata: false,
17238
+ // A per-file page is one file already — group by suite/describe so the
17239
+ // body shows clean section headings, not the redundant source path.
17240
+ groupBy: this.perFileTitle ? "suite" : options.markdown?.groupBy ?? "file"
17179
17241
  });
17180
17242
  }
17181
17243
  format(run) {
@@ -17185,13 +17247,31 @@ var AstroFormatter = class _AstroFormatter {
17185
17247
  return `${frontmatter}
17186
17248
  ${body}`;
17187
17249
  }
17250
+ /**
17251
+ * Title for the page. A per-file page (one source file — i.e. colocated mode)
17252
+ * is titled by its suite/describe name, falling back to a humanized filename,
17253
+ * so the docs nav reads "Convert Currency" not "User Stories" six times over.
17254
+ * Multi-file (aggregated) pages keep the configured title.
17255
+ */
17256
+ deriveTitle(run) {
17257
+ if (!this.perFileTitle) return this.title;
17258
+ const sourceFiles = new Set(
17259
+ run.testCases.map((tc) => tc.sourceFile).filter((f) => f && f !== "unknown")
17260
+ );
17261
+ if (sourceFiles.size !== 1) return this.title;
17262
+ const suites = new Set(
17263
+ run.testCases.map((tc) => tc.titlePath?.[0]).filter((s) => Boolean(s))
17264
+ );
17265
+ if (suites.size === 1) return [...suites][0];
17266
+ return humanizeSourceFile([...sourceFiles][0]) || this.title;
17267
+ }
17188
17268
  buildFrontmatter(run) {
17189
17269
  const badge = _AstroFormatter.computeBadge(run.testCases);
17190
17270
  const count = run.testCases.length;
17191
17271
  const description = `${count} scenario${count !== 1 ? "s" : ""} \u2014 ${badge.text.toLowerCase()}`;
17192
17272
  const lines = [
17193
17273
  "---",
17194
- `title: ${this.title}`,
17274
+ `title: ${yamlScalar(this.deriveTitle(run))}`,
17195
17275
  `description: ${description}`,
17196
17276
  "sidebar:",
17197
17277
  " badge:",
@@ -17209,6 +17289,12 @@ ${body}`;
17209
17289
  return { text: "Passed", variant: "success" };
17210
17290
  }
17211
17291
  };
17292
+ function yamlScalar(value) {
17293
+ if (/[:#[\]{}&*!|>'"%@`]|^[\s-]|\s$/.test(value)) {
17294
+ return `'${value.replace(/'/g, "''")}'`;
17295
+ }
17296
+ return value;
17297
+ }
17212
17298
 
17213
17299
  // src/formatters/confluence.ts
17214
17300
  var ConfluenceFormatter = class {
@@ -17485,6 +17571,15 @@ ${tc.errorStack}` : "");
17485
17571
  ])
17486
17572
  );
17487
17573
  break;
17574
+ case "video":
17575
+ content.push(
17576
+ paragraph([
17577
+ text(entry.caption ?? "Video", strong()),
17578
+ text(": "),
17579
+ link(entry.path, entry.path)
17580
+ ])
17581
+ );
17582
+ break;
17488
17583
  case "custom":
17489
17584
  content.push(paragraph([text(`[${entry.type}]`, strong())]));
17490
17585
  content.push(codeBlock(JSON.stringify(entry.data ?? null, null, 2), "json"));
@@ -17654,6 +17749,13 @@ function scanMarkdownAssets(markdown) {
17654
17749
  found.add(src);
17655
17750
  }
17656
17751
  }
17752
+ const posterRe = /<video[^>]+\bposter=["']([^"']+)["'][^>]*>/gi;
17753
+ while ((match = posterRe.exec(stripped)) !== null) {
17754
+ const src = match[1].trim();
17755
+ if (isLocalPath(src)) {
17756
+ found.add(src);
17757
+ }
17758
+ }
17657
17759
  return Array.from(found);
17658
17760
  }
17659
17761
  function splitByCode(markdown) {
@@ -17704,6 +17806,19 @@ function rewriteProseSegment(prose, assetsBaseUrl, pathMap) {
17704
17806
  return `${pre}${assetsBaseUrl}/${trimmed}${post}`;
17705
17807
  }
17706
17808
  );
17809
+ result = result.replace(
17810
+ /(<video[^>]+\bposter=["'])([^"']+)(["'][^>]*>)/gi,
17811
+ (full, pre, src, post) => {
17812
+ const trimmed = src.trim();
17813
+ if (!isLocalPath(trimmed)) return full;
17814
+ if (pathMap) {
17815
+ const mapped = pathMap.get(trimmed);
17816
+ if (mapped === void 0) return full;
17817
+ return `${pre}${assetsBaseUrl}/${mapped}${post}`;
17818
+ }
17819
+ return `${pre}${assetsBaseUrl}/${trimmed}${post}`;
17820
+ }
17821
+ );
17707
17822
  return result;
17708
17823
  }
17709
17824
  function rewriteAssetPaths(markdown, assetsBaseUrl, pathMap) {
@@ -20734,6 +20849,10 @@ var FORMAT_EXTENSIONS = {
20734
20849
  "scenario-index-json": ".scenarios-index.json",
20735
20850
  "story-report-json": ".story-report.json"
20736
20851
  };
20852
+ function joinNameAndExt(name, ext) {
20853
+ const stutter = `.${name}.`;
20854
+ return ext.startsWith(stutter) ? `${name}.${ext.slice(stutter.length)}` : `${name}${ext}`;
20855
+ }
20737
20856
  var TEST_EXTENSIONS = [
20738
20857
  ".test.ts",
20739
20858
  ".test.tsx",
@@ -20759,7 +20878,7 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
20759
20878
  const ext = FORMAT_EXTENSIONS[format];
20760
20879
  const effectiveName = outputName + (outputNameSuffix ?? "");
20761
20880
  if (mode === "aggregated") {
20762
- return toPosix(path10.join(baseOutputDir, `${effectiveName}${ext}`));
20881
+ return toPosix(path10.join(baseOutputDir, joinNameAndExt(effectiveName, ext)));
20763
20882
  }
20764
20883
  const normalizedSource = toPosix(sourceFile);
20765
20884
  const dirOfSource = path10.posix.dirname(normalizedSource);
@@ -20774,6 +20893,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
20774
20893
  if (colocatedStyle === "adjacent") {
20775
20894
  return toPosix(path10.posix.join(dirOfSource, fileName));
20776
20895
  }
20896
+ if (colocatedStyle === "flat") {
20897
+ return toPosix(path10.posix.join(baseOutputDir, `${cleanTestStem(normalizedSource)}${ext}`));
20898
+ }
20777
20899
  return toPosix(path10.posix.join(baseOutputDir, dirOfSource, fileName));
20778
20900
  }
20779
20901
  function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
@@ -21013,7 +21135,7 @@ var ReportGenerator = class {
21013
21135
  if (groups.size === 0 && this.options.output.mode === "aggregated") {
21014
21136
  const ext = FORMAT_EXTENSIONS[format];
21015
21137
  const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
21016
- const outputPath = toPosix(path10.join(this.options.outputDir, `${effectiveName}${ext}`));
21138
+ const outputPath = toPosix(path10.join(this.options.outputDir, joinNameAndExt(effectiveName, ext)));
21017
21139
  const content = await this.formatContent(run, format);
21018
21140
  const dir = path10.dirname(outputPath);
21019
21141
  await fsPromises.mkdir(dir, { recursive: true });
@@ -21093,6 +21215,8 @@ var ReportGenerator = class {
21093
21215
  case "astro": {
21094
21216
  const formatter = new AstroFormatter({
21095
21217
  assetsBaseUrl: this.options.astro.assetsBaseUrl,
21218
+ // Colocated = one page per file, so title each by its own suite/file.
21219
+ perFileTitle: this.options.output.mode === "colocated",
21096
21220
  markdown: this.options.astro.markdown
21097
21221
  });
21098
21222
  return formatter.format(run);
@@ -21245,6 +21369,7 @@ export {
21245
21369
  hasSufficientHistory,
21246
21370
  isReviewableSource,
21247
21371
  isTestFile,
21372
+ joinNameAndExt,
21248
21373
  listScenarios,
21249
21374
  loadHistory,
21250
21375
  mergeStepResults,