executable-stories-formatters 0.7.14 → 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/README.md +19 -0
- package/dist/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/cli.js +489 -60
- package/dist/cli.js.map +1 -1
- package/dist/{index-fqrm5-Xr.d.cts → index-BiAYcEiz.d.cts} +1 -1
- package/dist/{index-fqrm5-Xr.d.ts → index-BiAYcEiz.d.ts} +1 -1
- package/dist/index.cjs +460 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +274 -13
- package/dist/index.d.ts +274 -13
- package/dist/index.js +454 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schemas/story-report-v1.json +410 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import "fs";
|
|
3
|
-
import * as
|
|
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
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
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 =
|
|
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(
|
|
15227
|
-
const lower =
|
|
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
|
|
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
|
|
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 =
|
|
16312
|
-
const baseName = sanitize(
|
|
16709
|
+
const ext = path4.extname(sourcePath);
|
|
16710
|
+
const baseName = sanitize(path4.basename(sourcePath, ext));
|
|
16313
16711
|
const destName = `${baseName}-${hash}${ext}`;
|
|
16314
|
-
const destPath =
|
|
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 =
|
|
16327
|
-
const assetsDir =
|
|
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 =
|
|
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
|
|
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 !
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
18396
|
+
return path7.resolve(current, match[1]);
|
|
17999
18397
|
}
|
|
18000
18398
|
}
|
|
18001
18399
|
return candidate;
|
|
18002
18400
|
}
|
|
18003
|
-
const parent =
|
|
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 =
|
|
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
|
|
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 =
|
|
18462
|
+
let current = path8.resolve(startDir);
|
|
18065
18463
|
while (true) {
|
|
18066
|
-
const pkgPath =
|
|
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 =
|
|
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(
|
|
19459
|
+
return toPosix(path9.join(baseOutputDir, `${effectiveName}${ext}`));
|
|
19061
19460
|
}
|
|
19062
19461
|
const normalizedSource = toPosix(sourceFile);
|
|
19063
|
-
const dirOfSource =
|
|
19064
|
-
let baseName =
|
|
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(
|
|
19472
|
+
return toPosix(path9.posix.join(dirOfSource, fileName));
|
|
19074
19473
|
}
|
|
19075
|
-
return toPosix(
|
|
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 =
|
|
19274
|
-
const 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(
|
|
19707
|
+
const outputPath = toPosix(path9.join(this.options.outputDir, `${effectiveName}${ext}`));
|
|
19306
19708
|
const content = await this.formatContent(run, format);
|
|
19307
|
-
const dir =
|
|
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 =
|
|
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(
|
|
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
|