executable-stories-formatters 0.7.13 → 0.7.15

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
2
  import "fs";
3
- import * as path8 from "path";
3
+ import * as path9 from "path";
4
4
  import * as fsPromises from "fs/promises";
5
5
 
6
6
  // src/converters/acl/status.ts
@@ -678,9 +678,299 @@ ${doc.markdown}`,
678
678
  }
679
679
  };
680
680
 
681
+ // src/converters/story-report.ts
682
+ import { posix as path2 } from "path";
683
+
684
+ // src/types/story-report.ts
685
+ var STORY_REPORT_SCHEMA_VERSION = "1.0";
686
+ var STORY_REPORT_SCHEMA_MAJOR = 1;
687
+
688
+ // src/converters/story-report.ts
689
+ function reportSlug(text2) {
690
+ return text2.toLowerCase().replace(/[/\\.]+/g, "-").replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
691
+ }
692
+ function toRelativeSourceFile(sourceFile, projectRoot) {
693
+ if (!sourceFile) return sourceFile;
694
+ const normalized = sourceFile.split(path2.sep).join("/");
695
+ const root = projectRoot.split(path2.sep).join("/").replace(/\/$/, "");
696
+ if (root && normalized.startsWith(root + "/")) return normalized.slice(root.length + 1);
697
+ return normalized;
698
+ }
699
+ function fileBasenameTitle(sourceFile) {
700
+ const base = sourceFile.split("/").pop() ?? sourceFile;
701
+ return base.replace(/\.(story\.)?(test|spec)\.[tj]sx?$/, "").replace(/\.[tj]sx?$/, "");
702
+ }
703
+ function emptySummary() {
704
+ return { total: 0, passed: 0, failed: 0, skipped: 0, pending: 0, durationMs: 0 };
705
+ }
706
+ function addToSummary(summary, status, durationMs) {
707
+ summary.total += 1;
708
+ summary[status] += 1;
709
+ summary.durationMs += durationMs;
710
+ }
711
+ function isKeyword(value) {
712
+ return value === "Given" || value === "When" || value === "Then" || value === "And" || value === "But";
713
+ }
714
+ function copyDocEntries(entries) {
715
+ if (!entries || entries.length === 0) return [];
716
+ return entries.map(copyDocEntry);
717
+ }
718
+ function copyDocEntry(entry) {
719
+ const children = entry.children ? { children: copyDocEntries(entry.children) } : {};
720
+ switch (entry.kind) {
721
+ case "note":
722
+ return { kind: "note", text: entry.text, phase: entry.phase, ...children };
723
+ case "tag":
724
+ return { kind: "tag", names: [...entry.names], phase: entry.phase, ...children };
725
+ case "kv":
726
+ return { kind: "kv", label: entry.label, value: entry.value, phase: entry.phase, ...children };
727
+ case "code":
728
+ return {
729
+ kind: "code",
730
+ label: entry.label,
731
+ content: entry.content,
732
+ ...entry.lang ? { lang: entry.lang } : {},
733
+ phase: entry.phase,
734
+ ...children
735
+ };
736
+ case "table":
737
+ return {
738
+ kind: "table",
739
+ label: entry.label,
740
+ columns: [...entry.columns],
741
+ rows: entry.rows.map((r) => [...r]),
742
+ phase: entry.phase,
743
+ ...children
744
+ };
745
+ case "link":
746
+ return { kind: "link", label: entry.label, url: entry.url, phase: entry.phase, ...children };
747
+ case "section":
748
+ return { kind: "section", title: entry.title, markdown: entry.markdown, phase: entry.phase, ...children };
749
+ case "mermaid":
750
+ return {
751
+ kind: "mermaid",
752
+ code: entry.code,
753
+ ...entry.title ? { title: entry.title } : {},
754
+ phase: entry.phase,
755
+ ...children
756
+ };
757
+ case "screenshot":
758
+ return {
759
+ kind: "screenshot",
760
+ path: entry.path,
761
+ ...entry.alt ? { alt: entry.alt } : {},
762
+ phase: entry.phase,
763
+ ...children
764
+ };
765
+ case "custom":
766
+ return {
767
+ kind: "custom",
768
+ type: entry.type,
769
+ data: entry.data,
770
+ phase: entry.phase,
771
+ ...children
772
+ };
773
+ }
774
+ }
775
+ function buildStep(args) {
776
+ const step = {
777
+ id: `${args.scenarioId}--step-${args.index}`,
778
+ index: args.index,
779
+ keyword: args.keyword,
780
+ text: args.text,
781
+ status: args.status,
782
+ durationMs: args.durationMs,
783
+ docEntries: args.docEntries
784
+ };
785
+ if (args.errorMessage !== void 0) step.errorMessage = args.errorMessage;
786
+ if (args.mode !== void 0) step.mode = args.mode;
787
+ return step;
788
+ }
789
+ function buildSteps(scenarioId, tc) {
790
+ const declared = tc.story.steps ?? [];
791
+ const results = tc.stepResults;
792
+ const max = Math.max(declared.length, results.length);
793
+ const steps = [];
794
+ for (let i = 0; i < max; i++) {
795
+ const decl = declared[i];
796
+ const res = results[i];
797
+ const keywordSource = decl?.keyword;
798
+ const keyword = keywordSource && isKeyword(keywordSource) ? keywordSource : "Given";
799
+ const text2 = decl?.text ?? "";
800
+ const status = res?.status ?? "pending";
801
+ const durationMs = res?.durationMs ?? decl?.durationMs ?? 0;
802
+ const docEntries = copyDocEntries(decl?.docs);
803
+ steps.push(buildStep({
804
+ scenarioId,
805
+ index: i,
806
+ keyword,
807
+ text: text2,
808
+ status,
809
+ durationMs,
810
+ ...res?.errorMessage !== void 0 ? { errorMessage: res.errorMessage } : {},
811
+ ...decl?.mode !== void 0 ? { mode: decl.mode } : {},
812
+ docEntries
813
+ }));
814
+ }
815
+ return steps;
816
+ }
817
+ function buildAttachments(tc) {
818
+ return tc.attachments.map((a) => ({
819
+ name: a.name,
820
+ mediaType: a.mediaType,
821
+ body: a.body,
822
+ contentEncoding: a.contentEncoding
823
+ }));
824
+ }
825
+ function buildScenario(tc, featureId) {
826
+ const titleRaw = tc.story.scenario?.trim() || "(untitled scenario)";
827
+ const id = `${featureId}--${reportSlug(titleRaw) || `case-${tc.id}`}`;
828
+ const steps = buildSteps(id, tc);
829
+ const scenario = {
830
+ id,
831
+ title: titleRaw,
832
+ status: tc.status,
833
+ durationMs: tc.durationMs,
834
+ tags: [...tc.tags],
835
+ retry: tc.retry,
836
+ retries: tc.retries,
837
+ docEntries: copyDocEntries(tc.story.docs),
838
+ steps,
839
+ attachments: buildAttachments(tc)
840
+ };
841
+ if (tc.sourceLine && tc.sourceLine > 0) scenario.sourceLine = tc.sourceLine;
842
+ if (tc.errorMessage !== void 0) scenario.errorMessage = tc.errorMessage;
843
+ if (tc.errorStack !== void 0) scenario.errorStack = tc.errorStack;
844
+ const tickets = tc.story.tickets;
845
+ if (tickets && tickets.length > 0) {
846
+ scenario.tickets = tickets.map((t) => t.url ? { id: t.id, url: t.url } : { id: t.id });
847
+ }
848
+ return scenario;
849
+ }
850
+ function deriveFeatureTitle(group, relSourceFile) {
851
+ for (const tc of group) {
852
+ const head = tc.titlePath?.[0];
853
+ if (head && head.trim()) return head.trim();
854
+ }
855
+ return fileBasenameTitle(relSourceFile);
856
+ }
857
+ function compareScenarios(a, b) {
858
+ const aLine = a.sourceLine ?? Number.POSITIVE_INFINITY;
859
+ const bLine = b.sourceLine ?? Number.POSITIVE_INFINITY;
860
+ if (aLine !== bLine) return aLine - bLine;
861
+ return a.title.localeCompare(b.title);
862
+ }
863
+ function buildFeature(relSourceFile, group) {
864
+ const id = `feature-${reportSlug(relSourceFile.replace(/\.[^.]+$/, "")) || "untitled"}`;
865
+ const title = deriveFeatureTitle(group, relSourceFile);
866
+ const summary = emptySummary();
867
+ const scenarios = [];
868
+ for (const tc of group) {
869
+ const scenario = buildScenario(tc, id);
870
+ scenarios.push(scenario);
871
+ addToSummary(summary, scenario.status, scenario.durationMs);
872
+ }
873
+ scenarios.sort(compareScenarios);
874
+ return { id, title, sourceFile: relSourceFile, summary, scenarios };
875
+ }
876
+ function ensureUniqueFeatureIds(features) {
877
+ const seen = /* @__PURE__ */ new Map();
878
+ for (const f of features) {
879
+ const count = seen.get(f.id) ?? 0;
880
+ if (count > 0) f.id = `${f.id}-${count + 1}`;
881
+ seen.set(f.id, count + 1);
882
+ }
883
+ }
884
+ function ensureUniqueScenarioIds(feature) {
885
+ const seen = /* @__PURE__ */ new Map();
886
+ for (const s of feature.scenarios) {
887
+ const count = seen.get(s.id) ?? 0;
888
+ if (count > 0) {
889
+ const newId = `${s.id}-${count + 1}`;
890
+ for (const step of s.steps) {
891
+ step.id = step.id.replace(s.id, newId);
892
+ }
893
+ s.id = newId;
894
+ }
895
+ seen.set(s.id, count + 1);
896
+ }
897
+ }
898
+ function toStoryReport(run) {
899
+ const groups = /* @__PURE__ */ new Map();
900
+ for (const tc of run.testCases) {
901
+ const rel = toRelativeSourceFile(tc.sourceFile, run.projectRoot);
902
+ const existing = groups.get(rel);
903
+ if (existing) existing.push(tc);
904
+ else groups.set(rel, [tc]);
905
+ }
906
+ const features = [];
907
+ for (const [rel, group] of groups) {
908
+ features.push(buildFeature(rel, group));
909
+ }
910
+ features.sort((a, b) => a.title.localeCompare(b.title));
911
+ ensureUniqueFeatureIds(features);
912
+ for (const f of features) ensureUniqueScenarioIds(f);
913
+ const summary = emptySummary();
914
+ for (const f of features) {
915
+ summary.total += f.summary.total;
916
+ summary.passed += f.summary.passed;
917
+ summary.failed += f.summary.failed;
918
+ summary.skipped += f.summary.skipped;
919
+ summary.pending += f.summary.pending;
920
+ summary.durationMs += f.summary.durationMs;
921
+ }
922
+ const report = {
923
+ schemaVersion: STORY_REPORT_SCHEMA_VERSION,
924
+ runId: run.runId,
925
+ startedAtMs: run.startedAtMs,
926
+ finishedAtMs: run.finishedAtMs,
927
+ durationMs: run.durationMs,
928
+ projectRoot: run.projectRoot,
929
+ summary,
930
+ features
931
+ };
932
+ if (run.packageVersion) report.packageVersion = run.packageVersion;
933
+ if (run.gitSha) report.gitSha = run.gitSha;
934
+ if (run.ci) {
935
+ const ci = { name: run.ci.name };
936
+ if (run.ci.url) ci.url = run.ci.url;
937
+ if (run.ci.buildNumber) ci.buildNumber = run.ci.buildNumber;
938
+ if (run.ci.branch) ci.branch = run.ci.branch;
939
+ if (run.ci.commitSha) ci.commitSha = run.ci.commitSha;
940
+ if (run.ci.prNumber) ci.prNumber = run.ci.prNumber;
941
+ report.ci = ci;
942
+ }
943
+ if (run.coverage) {
944
+ const cov = {};
945
+ if (run.coverage.linesPct !== void 0) cov.linesPct = run.coverage.linesPct;
946
+ if (run.coverage.branchesPct !== void 0) cov.branchesPct = run.coverage.branchesPct;
947
+ if (run.coverage.functionsPct !== void 0) cov.functionsPct = run.coverage.functionsPct;
948
+ if (run.coverage.statementsPct !== void 0) cov.statementsPct = run.coverage.statementsPct;
949
+ report.coverage = cov;
950
+ }
951
+ return report;
952
+ }
953
+
954
+ // src/formatters/story-report-json.ts
955
+ var StoryReportJsonFormatter = class {
956
+ options;
957
+ constructor(options = {}) {
958
+ this.options = {
959
+ pretty: options.pretty ?? true
960
+ };
961
+ }
962
+ toReport(run) {
963
+ return toStoryReport(run);
964
+ }
965
+ format(run) {
966
+ const report = toStoryReport(run);
967
+ return this.options.pretty ? JSON.stringify(report, null, 2) : JSON.stringify(report);
968
+ }
969
+ };
970
+
681
971
  // src/formatters/html/renderers/index.ts
682
972
  import * as fs2 from "fs";
683
- import * as path2 from "path";
973
+ import * as path3 from "path";
684
974
 
685
975
  // src/formatters/html/template.ts
686
976
  var JS_THEME = `
@@ -1489,6 +1779,106 @@ function escapeHtml(str) {
1489
1779
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1490
1780
  }
1491
1781
 
1782
+ // src/theme/tokens.ts
1783
+ var ES_THEME_TOKENS_CSS = `
1784
+ :root,
1785
+ [data-theme="light"] {
1786
+ --es-color-bg: #ffffff;
1787
+ --es-color-fg: #111827;
1788
+ --es-color-muted: #6b7280;
1789
+ --es-color-border: #e5e7eb;
1790
+ --es-color-surface: #f9fafb;
1791
+ --es-color-link: #2563eb;
1792
+ --es-color-passed: #16a34a;
1793
+ --es-color-failed: #dc2626;
1794
+ --es-color-skipped: #9ca3af;
1795
+ --es-color-pending: #d97706;
1796
+ --es-color-passed-bg: #f0fdf4;
1797
+ --es-color-failed-bg: #fef2f2;
1798
+ --es-color-skipped-bg: #f3f4f6;
1799
+ --es-color-pending-bg: #fffbeb;
1800
+ --es-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
1801
+ --es-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
1802
+ --es-size-base: 1rem;
1803
+ --es-size-sm: 0.875rem;
1804
+ --es-size-xs: 0.75rem;
1805
+ --es-size-h1: 1.875rem;
1806
+ --es-size-h2: 1.5rem;
1807
+ --es-size-h3: 1.25rem;
1808
+ --es-space-1: 0.25rem;
1809
+ --es-space-2: 0.5rem;
1810
+ --es-space-3: 0.75rem;
1811
+ --es-space-4: 1rem;
1812
+ --es-space-6: 1.5rem;
1813
+ --es-space-8: 2rem;
1814
+ --es-radius: 0.5rem;
1815
+ --es-line: 1.6;
1816
+ --es-measure: 72ch;
1817
+ }
1818
+
1819
+ @media (prefers-color-scheme: dark) {
1820
+ :root {
1821
+ --es-color-bg: #0b0f17;
1822
+ --es-color-fg: #e5e7eb;
1823
+ --es-color-muted: #9ca3af;
1824
+ --es-color-border: #1f2937;
1825
+ --es-color-surface: #111827;
1826
+ --es-color-link: #60a5fa;
1827
+ --es-color-passed: #4ade80;
1828
+ --es-color-failed: #f87171;
1829
+ --es-color-skipped: #6b7280;
1830
+ --es-color-pending: #fbbf24;
1831
+ --es-color-passed-bg: rgba(74, 222, 128, 0.08);
1832
+ --es-color-failed-bg: rgba(248, 113, 113, 0.08);
1833
+ --es-color-skipped-bg: rgba(107, 114, 128, 0.08);
1834
+ --es-color-pending-bg: rgba(251, 191, 36, 0.08);
1835
+ }
1836
+ }
1837
+
1838
+ [data-theme="dark"] {
1839
+ --es-color-bg: #0b0f17;
1840
+ --es-color-fg: #e5e7eb;
1841
+ --es-color-muted: #9ca3af;
1842
+ --es-color-border: #1f2937;
1843
+ --es-color-surface: #111827;
1844
+ --es-color-link: #60a5fa;
1845
+ --es-color-passed: #4ade80;
1846
+ --es-color-failed: #f87171;
1847
+ --es-color-skipped: #6b7280;
1848
+ --es-color-pending: #fbbf24;
1849
+ --es-color-passed-bg: rgba(74, 222, 128, 0.08);
1850
+ --es-color-failed-bg: rgba(248, 113, 113, 0.08);
1851
+ --es-color-skipped-bg: rgba(107, 114, 128, 0.08);
1852
+ --es-color-pending-bg: rgba(251, 191, 36, 0.08);
1853
+ }
1854
+ `.trim();
1855
+ var ES_THEME_TOKEN_VALUES = {
1856
+ light: {
1857
+ "--es-color-bg": "#ffffff",
1858
+ "--es-color-fg": "#111827",
1859
+ "--es-color-muted": "#6b7280",
1860
+ "--es-color-border": "#e5e7eb",
1861
+ "--es-color-surface": "#f9fafb",
1862
+ "--es-color-link": "#2563eb",
1863
+ "--es-color-passed": "#16a34a",
1864
+ "--es-color-failed": "#dc2626",
1865
+ "--es-color-skipped": "#9ca3af",
1866
+ "--es-color-pending": "#d97706"
1867
+ },
1868
+ dark: {
1869
+ "--es-color-bg": "#0b0f17",
1870
+ "--es-color-fg": "#e5e7eb",
1871
+ "--es-color-muted": "#9ca3af",
1872
+ "--es-color-border": "#1f2937",
1873
+ "--es-color-surface": "#111827",
1874
+ "--es-color-link": "#60a5fa",
1875
+ "--es-color-passed": "#4ade80",
1876
+ "--es-color-failed": "#f87171",
1877
+ "--es-color-skipped": "#6b7280",
1878
+ "--es-color-pending": "#fbbf24"
1879
+ }
1880
+ };
1881
+
1492
1882
  // src/formatters/html/styles.ts
1493
1883
  var CSS_STYLES = `
1494
1884
  /* ============================================================================
@@ -1496,6 +1886,14 @@ var CSS_STYLES = `
1496
1886
  ============================================================================ */
1497
1887
  @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');
1498
1888
 
1889
+ /* ============================================================================
1890
+ executable-stories canonical tokens (--es-*).
1891
+ Shared with executable-stories-react. Override on :root or any ancestor of
1892
+ the report to re-color both the standalone HTML and the React component.
1893
+ ============================================================================ */
1894
+ ${ES_THEME_TOKENS_CSS}
1895
+
1896
+
1499
1897
  /* ============================================================================
1500
1898
  CSS Custom Properties - Light Mode (Default)
1501
1899
  Cucumber-branded shadcn/ui base theme
@@ -13746,7 +14144,7 @@ var SCREENSHOT_MIME_BY_EXT = {
13746
14144
  };
13747
14145
  function readScreenshotAsDataUri(filePath) {
13748
14146
  try {
13749
- const ext = path2.extname(filePath).slice(1).toLowerCase();
14147
+ const ext = path3.extname(filePath).slice(1).toLowerCase();
13750
14148
  const mime = SCREENSHOT_MIME_BY_EXT[ext];
13751
14149
  if (!mime) return void 0;
13752
14150
  if (!fs2.existsSync(filePath)) return void 0;
@@ -15223,8 +15621,8 @@ function extractDocAttachments(step) {
15223
15621
  }
15224
15622
  return attachments;
15225
15623
  }
15226
- function guessMediaType(path9) {
15227
- const lower = path9.toLowerCase();
15624
+ function guessMediaType(path10) {
15625
+ const lower = path10.toLowerCase();
15228
15626
  if (lower.endsWith(".png")) return "image/png";
15229
15627
  if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
15230
15628
  if (lower.endsWith(".gif")) return "image/gif";
@@ -16269,7 +16667,7 @@ function selectTestCases(args, deps) {
16269
16667
 
16270
16668
  // src/bundler/bundle-assets.ts
16271
16669
  import * as fs4 from "fs";
16272
- import * as path4 from "path";
16670
+ import * as path5 from "path";
16273
16671
 
16274
16672
  // src/bundler/scan-html-assets.ts
16275
16673
  function scanHtmlAssets(html) {
@@ -16300,7 +16698,7 @@ function isLocalAssetRef(ref) {
16300
16698
 
16301
16699
  // src/bundler/copy-asset.ts
16302
16700
  import * as fs3 from "fs";
16303
- import * as path3 from "path";
16701
+ import * as path4 from "path";
16304
16702
  import * as crypto from "crypto";
16305
16703
  function copyAsset(sourcePath, assetsDir) {
16306
16704
  if (!fs3.existsSync(assetsDir)) {
@@ -16308,10 +16706,10 @@ function copyAsset(sourcePath, assetsDir) {
16308
16706
  }
16309
16707
  const content = fs3.readFileSync(sourcePath);
16310
16708
  const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
16311
- const ext = path3.extname(sourcePath);
16312
- const baseName = sanitize(path3.basename(sourcePath, ext));
16709
+ const ext = path4.extname(sourcePath);
16710
+ const baseName = sanitize(path4.basename(sourcePath, ext));
16313
16711
  const destName = `${baseName}-${hash}${ext}`;
16314
- const destPath = path3.join(assetsDir, destName);
16712
+ const destPath = path4.join(assetsDir, destName);
16315
16713
  if (!fs3.existsSync(destPath)) {
16316
16714
  fs3.copyFileSync(sourcePath, destPath);
16317
16715
  }
@@ -16323,14 +16721,14 @@ function sanitize(name) {
16323
16721
 
16324
16722
  // src/bundler/bundle-assets.ts
16325
16723
  function bundleAssets(htmlPath, options = {}) {
16326
- const htmlDir = path4.dirname(htmlPath);
16327
- const assetsDir = path4.join(htmlDir, "assets");
16724
+ const htmlDir = path5.dirname(htmlPath);
16725
+ const assetsDir = path5.join(htmlDir, "assets");
16328
16726
  let html = fs4.readFileSync(htmlPath, "utf8");
16329
16727
  const refs = scanHtmlAssets(html);
16330
16728
  let copiedCount = 0;
16331
16729
  const missing = [];
16332
16730
  for (const ref of refs) {
16333
- const absolutePath = path4.resolve(htmlDir, ref);
16731
+ const absolutePath = path5.resolve(htmlDir, ref);
16334
16732
  if (!fs4.existsSync(absolutePath)) {
16335
16733
  missing.push(ref);
16336
16734
  continue;
@@ -16828,14 +17226,14 @@ function groupBy7(items, keyFn) {
16828
17226
 
16829
17227
  // src/formatters/astro-assets.ts
16830
17228
  import * as fs5 from "fs";
16831
- import * as path5 from "path";
17229
+ import * as path6 from "path";
16832
17230
  var SKIP_PREFIXES = ["http://", "https://", "data:", "#"];
16833
17231
  function isLocalPath(src) {
16834
17232
  const trimmed = src.trim();
16835
17233
  if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
16836
17234
  return false;
16837
17235
  }
16838
- return !path5.posix.isAbsolute(trimmed) && !path5.win32.isAbsolute(trimmed);
17236
+ return !path6.posix.isAbsolute(trimmed) && !path6.win32.isAbsolute(trimmed);
16839
17237
  }
16840
17238
  function stripCodeContent(markdown) {
16841
17239
  let result = markdown.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$/gm, "");
@@ -16929,7 +17327,7 @@ function copyMarkdownAssets(options) {
16929
17327
  const pathMap = /* @__PURE__ */ new Map();
16930
17328
  const missing = [];
16931
17329
  for (const ref of refs) {
16932
- const absPath = path5.resolve(markdownDir, ref);
17330
+ const absPath = path6.resolve(markdownDir, ref);
16933
17331
  if (!fs5.existsSync(absPath)) {
16934
17332
  if (!allowMissing) {
16935
17333
  throw new Error(`Asset not found: ${absPath}`);
@@ -17954,24 +18352,24 @@ function pickleStepArgumentToDocs(ps) {
17954
18352
 
17955
18353
  // src/utils/git-info.ts
17956
18354
  import * as fs6 from "fs";
17957
- import * as path6 from "path";
18355
+ import * as path7 from "path";
17958
18356
  function readGitSha(cwd = process.cwd()) {
17959
18357
  const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;
17960
18358
  if (envSha) return envSha;
17961
18359
  const gitDir = findGitDir(cwd);
17962
18360
  if (!gitDir) return void 0;
17963
18361
  try {
17964
- const headPath = path6.join(gitDir, "HEAD");
18362
+ const headPath = path7.join(gitDir, "HEAD");
17965
18363
  const head = fs6.readFileSync(headPath, "utf8").trim();
17966
18364
  if (!head.startsWith("ref:")) {
17967
18365
  return head;
17968
18366
  }
17969
18367
  const refPath = head.replace("ref:", "").trim();
17970
- const refFile = path6.join(gitDir, refPath);
18368
+ const refFile = path7.join(gitDir, refPath);
17971
18369
  if (fs6.existsSync(refFile)) {
17972
18370
  return fs6.readFileSync(refFile, "utf8").trim();
17973
18371
  }
17974
- const packedRefs = path6.join(gitDir, "packed-refs");
18372
+ const packedRefs = path7.join(gitDir, "packed-refs");
17975
18373
  if (fs6.existsSync(packedRefs)) {
17976
18374
  const content = fs6.readFileSync(packedRefs, "utf8");
17977
18375
  for (const line of content.split("\n")) {
@@ -17988,19 +18386,19 @@ function readGitSha(cwd = process.cwd()) {
17988
18386
  function findGitDir(start) {
17989
18387
  let current = start;
17990
18388
  while (true) {
17991
- const candidate = path6.join(current, ".git");
18389
+ const candidate = path7.join(current, ".git");
17992
18390
  if (fs6.existsSync(candidate)) {
17993
18391
  const stat = fs6.statSync(candidate);
17994
18392
  if (stat.isFile()) {
17995
18393
  const content = fs6.readFileSync(candidate, "utf8").trim();
17996
18394
  const match = content.match(/^gitdir: (.+)$/);
17997
18395
  if (match) {
17998
- return path6.resolve(current, match[1]);
18396
+ return path7.resolve(current, match[1]);
17999
18397
  }
18000
18398
  }
18001
18399
  return candidate;
18002
18400
  }
18003
- const parent = path6.dirname(current);
18401
+ const parent = path7.dirname(current);
18004
18402
  if (parent === current) return void 0;
18005
18403
  current = parent;
18006
18404
  }
@@ -18011,7 +18409,7 @@ function readBranchName(cwd = process.cwd()) {
18011
18409
  const gitDir = findGitDir(cwd);
18012
18410
  if (!gitDir) return void 0;
18013
18411
  try {
18014
- const headPath = path6.join(gitDir, "HEAD");
18412
+ const headPath = path7.join(gitDir, "HEAD");
18015
18413
  const head = fs6.readFileSync(headPath, "utf8").trim();
18016
18414
  if (head.startsWith("ref:")) {
18017
18415
  const refPath = head.replace("ref:", "").trim();
@@ -18050,7 +18448,7 @@ function nanosecondsToMs(ns) {
18050
18448
 
18051
18449
  // src/utils/metadata.ts
18052
18450
  import * as fs7 from "fs";
18053
- import * as path7 from "path";
18451
+ import * as path8 from "path";
18054
18452
  var versionCache = /* @__PURE__ */ new Map();
18055
18453
  function readPackageVersion(root) {
18056
18454
  if (versionCache.has(root)) {
@@ -18061,9 +18459,9 @@ function readPackageVersion(root) {
18061
18459
  return version;
18062
18460
  }
18063
18461
  function findPackageVersion(startDir) {
18064
- let current = path7.resolve(startDir);
18462
+ let current = path8.resolve(startDir);
18065
18463
  while (true) {
18066
- const pkgPath = path7.join(current, "package.json");
18464
+ const pkgPath = path8.join(current, "package.json");
18067
18465
  try {
18068
18466
  if (fs7.existsSync(pkgPath)) {
18069
18467
  const raw = fs7.readFileSync(pkgPath, "utf8");
@@ -18072,7 +18470,7 @@ function findPackageVersion(startDir) {
18072
18470
  }
18073
18471
  } catch {
18074
18472
  }
18075
- const parent = path7.dirname(current);
18473
+ const parent = path8.dirname(current);
18076
18474
  if (parent === current) {
18077
18475
  return void 0;
18078
18476
  }
@@ -19030,7 +19428,8 @@ var FORMAT_EXTENSIONS = {
19030
19428
  junit: ".junit.xml",
19031
19429
  "cucumber-json": ".cucumber.json",
19032
19430
  "cucumber-messages": ".ndjson",
19033
- confluence: ".adf.json"
19431
+ confluence: ".adf.json",
19432
+ "story-report-json": ".story-report.json"
19034
19433
  };
19035
19434
  var TEST_EXTENSIONS = [
19036
19435
  ".test.ts",
@@ -19057,11 +19456,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
19057
19456
  const ext = FORMAT_EXTENSIONS[format];
19058
19457
  const effectiveName = outputName + (outputNameSuffix ?? "");
19059
19458
  if (mode === "aggregated") {
19060
- return toPosix(path8.join(baseOutputDir, `${effectiveName}${ext}`));
19459
+ return toPosix(path9.join(baseOutputDir, `${effectiveName}${ext}`));
19061
19460
  }
19062
19461
  const normalizedSource = toPosix(sourceFile);
19063
- const dirOfSource = path8.posix.dirname(normalizedSource);
19064
- let baseName = path8.posix.basename(normalizedSource);
19462
+ const dirOfSource = path9.posix.dirname(normalizedSource);
19463
+ let baseName = path9.posix.basename(normalizedSource);
19065
19464
  for (const testExt of TEST_EXTENSIONS) {
19066
19465
  if (baseName.endsWith(testExt)) {
19067
19466
  baseName = baseName.slice(0, -testExt.length);
@@ -19070,9 +19469,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
19070
19469
  }
19071
19470
  const fileName = `${baseName}.${effectiveName}${ext}`;
19072
19471
  if (colocatedStyle === "adjacent") {
19073
- return toPosix(path8.posix.join(dirOfSource, fileName));
19472
+ return toPosix(path9.posix.join(dirOfSource, fileName));
19074
19473
  }
19075
- return toPosix(path8.posix.join(baseOutputDir, dirOfSource, fileName));
19474
+ return toPosix(path9.posix.join(baseOutputDir, dirOfSource, fileName));
19076
19475
  }
19077
19476
  function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
19078
19477
  const groups = /* @__PURE__ */ new Map();
@@ -19155,6 +19554,9 @@ var ReportGenerator = class {
19155
19554
  cucumberJson: {
19156
19555
  pretty: options.cucumberJson?.pretty ?? false
19157
19556
  },
19557
+ storyReportJson: {
19558
+ pretty: options.storyReportJson?.pretty ?? true
19559
+ },
19158
19560
  cucumberMessages: {
19159
19561
  uriStrategy: options.cucumberMessages?.uriStrategy ?? "sourceFile",
19160
19562
  includeSynthetics: options.cucumberMessages?.includeSynthetics ?? true,
@@ -19270,8 +19672,8 @@ var ReportGenerator = class {
19270
19672
  if (astroPaths) {
19271
19673
  for (const mdPath of astroPaths) {
19272
19674
  const content = await fsPromises.readFile(mdPath, "utf8");
19273
- const mdDir = path8.dirname(mdPath);
19274
- const assetsDir = path8.resolve(this.options.astro.assetsDir);
19675
+ const mdDir = path9.dirname(mdPath);
19676
+ const assetsDir = path9.resolve(this.options.astro.assetsDir);
19275
19677
  const result = copyMarkdownAssets({
19276
19678
  markdown: content,
19277
19679
  markdownDir: mdDir,
@@ -19302,9 +19704,9 @@ var ReportGenerator = class {
19302
19704
  if (groups.size === 0 && this.options.output.mode === "aggregated") {
19303
19705
  const ext = FORMAT_EXTENSIONS[format];
19304
19706
  const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
19305
- const outputPath = toPosix(path8.join(this.options.outputDir, `${effectiveName}${ext}`));
19707
+ const outputPath = toPosix(path9.join(this.options.outputDir, `${effectiveName}${ext}`));
19306
19708
  const content = await this.formatContent(run, format);
19307
- const dir = path8.dirname(outputPath);
19709
+ const dir = path9.dirname(outputPath);
19308
19710
  await fsPromises.mkdir(dir, { recursive: true });
19309
19711
  await this.deps.writeFile(outputPath, content);
19310
19712
  return [outputPath];
@@ -19316,7 +19718,7 @@ var ReportGenerator = class {
19316
19718
  testCases
19317
19719
  };
19318
19720
  const content = await this.formatContent(groupRun, format);
19319
- const dir = path8.dirname(outputPath);
19721
+ const dir = path9.dirname(outputPath);
19320
19722
  await fsPromises.mkdir(dir, { recursive: true });
19321
19723
  await this.deps.writeFile(outputPath, content);
19322
19724
  writtenPaths.push(outputPath);
@@ -19423,6 +19825,12 @@ var ReportGenerator = class {
19423
19825
  });
19424
19826
  return formatter.format(run);
19425
19827
  }
19828
+ case "story-report-json": {
19829
+ const formatter = new StoryReportJsonFormatter({
19830
+ pretty: this.options.storyReportJson.pretty
19831
+ });
19832
+ return formatter.format(run);
19833
+ }
19426
19834
  default:
19427
19835
  throw new Error(`Unknown format: ${format}`);
19428
19836
  }
@@ -19439,7 +19847,7 @@ async function generateRunComparison(args) {
19439
19847
  await fsPromises.mkdir(outputDir, { recursive: true });
19440
19848
  for (const format of args.formats) {
19441
19849
  const ext = format === "html" ? ".html" : ".md";
19442
- const outputPath = toPosix(path8.join(outputDir, `${outputName}${ext}`));
19850
+ const outputPath = toPosix(path9.join(outputDir, `${outputName}${ext}`));
19443
19851
  const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
19444
19852
  await fsPromises.writeFile(outputPath, content, "utf8");
19445
19853
  files.push(outputPath);
@@ -19464,6 +19872,8 @@ export {
19464
19872
  CucumberHtmlFormatter,
19465
19873
  CucumberJsonFormatter,
19466
19874
  CucumberMessagesFormatter,
19875
+ ES_THEME_TOKENS_CSS,
19876
+ ES_THEME_TOKEN_VALUES,
19467
19877
  HtmlFormatter,
19468
19878
  JUnitFormatter,
19469
19879
  MIN_FLAKINESS_SAMPLES,
@@ -19474,6 +19884,9 @@ export {
19474
19884
  RunDiffHtmlFormatter,
19475
19885
  RunDiffMarkdownFormatter,
19476
19886
  STORY_META_KEY,
19887
+ STORY_REPORT_SCHEMA_MAJOR,
19888
+ STORY_REPORT_SCHEMA_VERSION,
19889
+ StoryReportJsonFormatter,
19477
19890
  adaptJestRun,
19478
19891
  adaptPlaywrightRun,
19479
19892
  adaptVitestRun,
@@ -19530,6 +19943,7 @@ export {
19530
19943
  stripAnsi,
19531
19944
  toCIInfo,
19532
19945
  toRawCIInfo,
19946
+ toStoryReport,
19533
19947
  tryGetActiveOtelContext,
19534
19948
  updateHistory,
19535
19949
  validateCanonicalRun