executable-stories-formatters 0.7.14 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/cli.js +706 -62
- 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 +677 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +275 -14
- package/dist/index.d.ts +275 -14
- package/dist/index.js +671 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schemas/story-report-v1.json +410 -0
package/dist/index.cjs
CHANGED
|
@@ -35,6 +35,8 @@ __export(src_exports, {
|
|
|
35
35
|
CucumberHtmlFormatter: () => CucumberHtmlFormatter,
|
|
36
36
|
CucumberJsonFormatter: () => CucumberJsonFormatter,
|
|
37
37
|
CucumberMessagesFormatter: () => CucumberMessagesFormatter,
|
|
38
|
+
ES_THEME_TOKENS_CSS: () => ES_THEME_TOKENS_CSS,
|
|
39
|
+
ES_THEME_TOKEN_VALUES: () => ES_THEME_TOKEN_VALUES,
|
|
38
40
|
HtmlFormatter: () => HtmlFormatter,
|
|
39
41
|
JUnitFormatter: () => JUnitFormatter,
|
|
40
42
|
MIN_FLAKINESS_SAMPLES: () => MIN_FLAKINESS_SAMPLES,
|
|
@@ -45,6 +47,9 @@ __export(src_exports, {
|
|
|
45
47
|
RunDiffHtmlFormatter: () => RunDiffHtmlFormatter,
|
|
46
48
|
RunDiffMarkdownFormatter: () => RunDiffMarkdownFormatter,
|
|
47
49
|
STORY_META_KEY: () => STORY_META_KEY,
|
|
50
|
+
STORY_REPORT_SCHEMA_MAJOR: () => STORY_REPORT_SCHEMA_MAJOR,
|
|
51
|
+
STORY_REPORT_SCHEMA_VERSION: () => STORY_REPORT_SCHEMA_VERSION,
|
|
52
|
+
StoryReportJsonFormatter: () => StoryReportJsonFormatter,
|
|
48
53
|
adaptJestRun: () => adaptJestRun,
|
|
49
54
|
adaptPlaywrightRun: () => adaptPlaywrightRun,
|
|
50
55
|
adaptVitestRun: () => adaptVitestRun,
|
|
@@ -101,13 +106,14 @@ __export(src_exports, {
|
|
|
101
106
|
stripAnsi: () => stripAnsi,
|
|
102
107
|
toCIInfo: () => toCIInfo,
|
|
103
108
|
toRawCIInfo: () => toRawCIInfo,
|
|
109
|
+
toStoryReport: () => toStoryReport,
|
|
104
110
|
tryGetActiveOtelContext: () => tryGetActiveOtelContext,
|
|
105
111
|
updateHistory: () => updateHistory,
|
|
106
112
|
validateCanonicalRun: () => validateCanonicalRun
|
|
107
113
|
});
|
|
108
114
|
module.exports = __toCommonJS(src_exports);
|
|
109
115
|
var fs8 = require("fs");
|
|
110
|
-
var
|
|
116
|
+
var path9 = __toESM(require("path"), 1);
|
|
111
117
|
var fsPromises = __toESM(require("fs/promises"), 1);
|
|
112
118
|
|
|
113
119
|
// src/converters/acl/status.ts
|
|
@@ -785,9 +791,299 @@ ${doc.markdown}`,
|
|
|
785
791
|
}
|
|
786
792
|
};
|
|
787
793
|
|
|
794
|
+
// src/converters/story-report.ts
|
|
795
|
+
var import_node_path = require("path");
|
|
796
|
+
|
|
797
|
+
// src/types/story-report.ts
|
|
798
|
+
var STORY_REPORT_SCHEMA_VERSION = "1.0";
|
|
799
|
+
var STORY_REPORT_SCHEMA_MAJOR = 1;
|
|
800
|
+
|
|
801
|
+
// src/converters/story-report.ts
|
|
802
|
+
function reportSlug(text2) {
|
|
803
|
+
return text2.toLowerCase().replace(/[/\\.]+/g, "-").replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
804
|
+
}
|
|
805
|
+
function toRelativeSourceFile(sourceFile, projectRoot) {
|
|
806
|
+
if (!sourceFile) return sourceFile;
|
|
807
|
+
const normalized = sourceFile.split(import_node_path.posix.sep).join("/");
|
|
808
|
+
const root = projectRoot.split(import_node_path.posix.sep).join("/").replace(/\/$/, "");
|
|
809
|
+
if (root && normalized.startsWith(root + "/")) return normalized.slice(root.length + 1);
|
|
810
|
+
return normalized;
|
|
811
|
+
}
|
|
812
|
+
function fileBasenameTitle(sourceFile) {
|
|
813
|
+
const base = sourceFile.split("/").pop() ?? sourceFile;
|
|
814
|
+
return base.replace(/\.(story\.)?(test|spec)\.[tj]sx?$/, "").replace(/\.[tj]sx?$/, "");
|
|
815
|
+
}
|
|
816
|
+
function emptySummary() {
|
|
817
|
+
return { total: 0, passed: 0, failed: 0, skipped: 0, pending: 0, durationMs: 0 };
|
|
818
|
+
}
|
|
819
|
+
function addToSummary(summary, status, durationMs) {
|
|
820
|
+
summary.total += 1;
|
|
821
|
+
summary[status] += 1;
|
|
822
|
+
summary.durationMs += durationMs;
|
|
823
|
+
}
|
|
824
|
+
function isKeyword(value) {
|
|
825
|
+
return value === "Given" || value === "When" || value === "Then" || value === "And" || value === "But";
|
|
826
|
+
}
|
|
827
|
+
function copyDocEntries(entries) {
|
|
828
|
+
if (!entries || entries.length === 0) return [];
|
|
829
|
+
return entries.map(copyDocEntry);
|
|
830
|
+
}
|
|
831
|
+
function copyDocEntry(entry) {
|
|
832
|
+
const children = entry.children ? { children: copyDocEntries(entry.children) } : {};
|
|
833
|
+
switch (entry.kind) {
|
|
834
|
+
case "note":
|
|
835
|
+
return { kind: "note", text: entry.text, phase: entry.phase, ...children };
|
|
836
|
+
case "tag":
|
|
837
|
+
return { kind: "tag", names: [...entry.names], phase: entry.phase, ...children };
|
|
838
|
+
case "kv":
|
|
839
|
+
return { kind: "kv", label: entry.label, value: entry.value, phase: entry.phase, ...children };
|
|
840
|
+
case "code":
|
|
841
|
+
return {
|
|
842
|
+
kind: "code",
|
|
843
|
+
label: entry.label,
|
|
844
|
+
content: entry.content,
|
|
845
|
+
...entry.lang ? { lang: entry.lang } : {},
|
|
846
|
+
phase: entry.phase,
|
|
847
|
+
...children
|
|
848
|
+
};
|
|
849
|
+
case "table":
|
|
850
|
+
return {
|
|
851
|
+
kind: "table",
|
|
852
|
+
label: entry.label,
|
|
853
|
+
columns: [...entry.columns],
|
|
854
|
+
rows: entry.rows.map((r) => [...r]),
|
|
855
|
+
phase: entry.phase,
|
|
856
|
+
...children
|
|
857
|
+
};
|
|
858
|
+
case "link":
|
|
859
|
+
return { kind: "link", label: entry.label, url: entry.url, phase: entry.phase, ...children };
|
|
860
|
+
case "section":
|
|
861
|
+
return { kind: "section", title: entry.title, markdown: entry.markdown, phase: entry.phase, ...children };
|
|
862
|
+
case "mermaid":
|
|
863
|
+
return {
|
|
864
|
+
kind: "mermaid",
|
|
865
|
+
code: entry.code,
|
|
866
|
+
...entry.title ? { title: entry.title } : {},
|
|
867
|
+
phase: entry.phase,
|
|
868
|
+
...children
|
|
869
|
+
};
|
|
870
|
+
case "screenshot":
|
|
871
|
+
return {
|
|
872
|
+
kind: "screenshot",
|
|
873
|
+
path: entry.path,
|
|
874
|
+
...entry.alt ? { alt: entry.alt } : {},
|
|
875
|
+
phase: entry.phase,
|
|
876
|
+
...children
|
|
877
|
+
};
|
|
878
|
+
case "custom":
|
|
879
|
+
return {
|
|
880
|
+
kind: "custom",
|
|
881
|
+
type: entry.type,
|
|
882
|
+
data: entry.data,
|
|
883
|
+
phase: entry.phase,
|
|
884
|
+
...children
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
function buildStep(args) {
|
|
889
|
+
const step = {
|
|
890
|
+
id: `${args.scenarioId}--step-${args.index}`,
|
|
891
|
+
index: args.index,
|
|
892
|
+
keyword: args.keyword,
|
|
893
|
+
text: args.text,
|
|
894
|
+
status: args.status,
|
|
895
|
+
durationMs: args.durationMs,
|
|
896
|
+
docEntries: args.docEntries
|
|
897
|
+
};
|
|
898
|
+
if (args.errorMessage !== void 0) step.errorMessage = args.errorMessage;
|
|
899
|
+
if (args.mode !== void 0) step.mode = args.mode;
|
|
900
|
+
return step;
|
|
901
|
+
}
|
|
902
|
+
function buildSteps(scenarioId, tc) {
|
|
903
|
+
const declared = tc.story.steps ?? [];
|
|
904
|
+
const results = tc.stepResults;
|
|
905
|
+
const max = Math.max(declared.length, results.length);
|
|
906
|
+
const steps = [];
|
|
907
|
+
for (let i = 0; i < max; i++) {
|
|
908
|
+
const decl = declared[i];
|
|
909
|
+
const res = results[i];
|
|
910
|
+
const keywordSource = decl?.keyword;
|
|
911
|
+
const keyword = keywordSource && isKeyword(keywordSource) ? keywordSource : "Given";
|
|
912
|
+
const text2 = decl?.text ?? "";
|
|
913
|
+
const status = res?.status ?? "pending";
|
|
914
|
+
const durationMs = res?.durationMs ?? decl?.durationMs ?? 0;
|
|
915
|
+
const docEntries = copyDocEntries(decl?.docs);
|
|
916
|
+
steps.push(buildStep({
|
|
917
|
+
scenarioId,
|
|
918
|
+
index: i,
|
|
919
|
+
keyword,
|
|
920
|
+
text: text2,
|
|
921
|
+
status,
|
|
922
|
+
durationMs,
|
|
923
|
+
...res?.errorMessage !== void 0 ? { errorMessage: res.errorMessage } : {},
|
|
924
|
+
...decl?.mode !== void 0 ? { mode: decl.mode } : {},
|
|
925
|
+
docEntries
|
|
926
|
+
}));
|
|
927
|
+
}
|
|
928
|
+
return steps;
|
|
929
|
+
}
|
|
930
|
+
function buildAttachments(tc) {
|
|
931
|
+
return tc.attachments.map((a) => ({
|
|
932
|
+
name: a.name,
|
|
933
|
+
mediaType: a.mediaType,
|
|
934
|
+
body: a.body,
|
|
935
|
+
contentEncoding: a.contentEncoding
|
|
936
|
+
}));
|
|
937
|
+
}
|
|
938
|
+
function buildScenario(tc, featureId) {
|
|
939
|
+
const titleRaw = tc.story.scenario?.trim() || "(untitled scenario)";
|
|
940
|
+
const id = `${featureId}--${reportSlug(titleRaw) || `case-${tc.id}`}`;
|
|
941
|
+
const steps = buildSteps(id, tc);
|
|
942
|
+
const scenario = {
|
|
943
|
+
id,
|
|
944
|
+
title: titleRaw,
|
|
945
|
+
status: tc.status,
|
|
946
|
+
durationMs: tc.durationMs,
|
|
947
|
+
tags: [...tc.tags],
|
|
948
|
+
retry: tc.retry,
|
|
949
|
+
retries: tc.retries,
|
|
950
|
+
docEntries: copyDocEntries(tc.story.docs),
|
|
951
|
+
steps,
|
|
952
|
+
attachments: buildAttachments(tc)
|
|
953
|
+
};
|
|
954
|
+
if (tc.sourceLine && tc.sourceLine > 0) scenario.sourceLine = tc.sourceLine;
|
|
955
|
+
if (tc.errorMessage !== void 0) scenario.errorMessage = tc.errorMessage;
|
|
956
|
+
if (tc.errorStack !== void 0) scenario.errorStack = tc.errorStack;
|
|
957
|
+
const tickets = tc.story.tickets;
|
|
958
|
+
if (tickets && tickets.length > 0) {
|
|
959
|
+
scenario.tickets = tickets.map((t) => t.url ? { id: t.id, url: t.url } : { id: t.id });
|
|
960
|
+
}
|
|
961
|
+
return scenario;
|
|
962
|
+
}
|
|
963
|
+
function deriveFeatureTitle(group, relSourceFile) {
|
|
964
|
+
for (const tc of group) {
|
|
965
|
+
const head = tc.titlePath?.[0];
|
|
966
|
+
if (head && head.trim()) return head.trim();
|
|
967
|
+
}
|
|
968
|
+
return fileBasenameTitle(relSourceFile);
|
|
969
|
+
}
|
|
970
|
+
function compareScenarios(a, b) {
|
|
971
|
+
const aLine = a.sourceLine ?? Number.POSITIVE_INFINITY;
|
|
972
|
+
const bLine = b.sourceLine ?? Number.POSITIVE_INFINITY;
|
|
973
|
+
if (aLine !== bLine) return aLine - bLine;
|
|
974
|
+
return a.title.localeCompare(b.title);
|
|
975
|
+
}
|
|
976
|
+
function buildFeature(relSourceFile, group) {
|
|
977
|
+
const id = `feature-${reportSlug(relSourceFile.replace(/\.[^.]+$/, "")) || "untitled"}`;
|
|
978
|
+
const title = deriveFeatureTitle(group, relSourceFile);
|
|
979
|
+
const summary = emptySummary();
|
|
980
|
+
const scenarios = [];
|
|
981
|
+
for (const tc of group) {
|
|
982
|
+
const scenario = buildScenario(tc, id);
|
|
983
|
+
scenarios.push(scenario);
|
|
984
|
+
addToSummary(summary, scenario.status, scenario.durationMs);
|
|
985
|
+
}
|
|
986
|
+
scenarios.sort(compareScenarios);
|
|
987
|
+
return { id, title, sourceFile: relSourceFile, summary, scenarios };
|
|
988
|
+
}
|
|
989
|
+
function ensureUniqueFeatureIds(features) {
|
|
990
|
+
const seen = /* @__PURE__ */ new Map();
|
|
991
|
+
for (const f of features) {
|
|
992
|
+
const count = seen.get(f.id) ?? 0;
|
|
993
|
+
if (count > 0) f.id = `${f.id}-${count + 1}`;
|
|
994
|
+
seen.set(f.id, count + 1);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function ensureUniqueScenarioIds(feature) {
|
|
998
|
+
const seen = /* @__PURE__ */ new Map();
|
|
999
|
+
for (const s of feature.scenarios) {
|
|
1000
|
+
const count = seen.get(s.id) ?? 0;
|
|
1001
|
+
if (count > 0) {
|
|
1002
|
+
const newId = `${s.id}-${count + 1}`;
|
|
1003
|
+
for (const step of s.steps) {
|
|
1004
|
+
step.id = step.id.replace(s.id, newId);
|
|
1005
|
+
}
|
|
1006
|
+
s.id = newId;
|
|
1007
|
+
}
|
|
1008
|
+
seen.set(s.id, count + 1);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
function toStoryReport(run) {
|
|
1012
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1013
|
+
for (const tc of run.testCases) {
|
|
1014
|
+
const rel = toRelativeSourceFile(tc.sourceFile, run.projectRoot);
|
|
1015
|
+
const existing = groups.get(rel);
|
|
1016
|
+
if (existing) existing.push(tc);
|
|
1017
|
+
else groups.set(rel, [tc]);
|
|
1018
|
+
}
|
|
1019
|
+
const features = [];
|
|
1020
|
+
for (const [rel, group] of groups) {
|
|
1021
|
+
features.push(buildFeature(rel, group));
|
|
1022
|
+
}
|
|
1023
|
+
features.sort((a, b) => a.title.localeCompare(b.title));
|
|
1024
|
+
ensureUniqueFeatureIds(features);
|
|
1025
|
+
for (const f of features) ensureUniqueScenarioIds(f);
|
|
1026
|
+
const summary = emptySummary();
|
|
1027
|
+
for (const f of features) {
|
|
1028
|
+
summary.total += f.summary.total;
|
|
1029
|
+
summary.passed += f.summary.passed;
|
|
1030
|
+
summary.failed += f.summary.failed;
|
|
1031
|
+
summary.skipped += f.summary.skipped;
|
|
1032
|
+
summary.pending += f.summary.pending;
|
|
1033
|
+
summary.durationMs += f.summary.durationMs;
|
|
1034
|
+
}
|
|
1035
|
+
const report = {
|
|
1036
|
+
schemaVersion: STORY_REPORT_SCHEMA_VERSION,
|
|
1037
|
+
runId: run.runId,
|
|
1038
|
+
startedAtMs: run.startedAtMs,
|
|
1039
|
+
finishedAtMs: run.finishedAtMs,
|
|
1040
|
+
durationMs: run.durationMs,
|
|
1041
|
+
projectRoot: run.projectRoot,
|
|
1042
|
+
summary,
|
|
1043
|
+
features
|
|
1044
|
+
};
|
|
1045
|
+
if (run.packageVersion) report.packageVersion = run.packageVersion;
|
|
1046
|
+
if (run.gitSha) report.gitSha = run.gitSha;
|
|
1047
|
+
if (run.ci) {
|
|
1048
|
+
const ci = { name: run.ci.name };
|
|
1049
|
+
if (run.ci.url) ci.url = run.ci.url;
|
|
1050
|
+
if (run.ci.buildNumber) ci.buildNumber = run.ci.buildNumber;
|
|
1051
|
+
if (run.ci.branch) ci.branch = run.ci.branch;
|
|
1052
|
+
if (run.ci.commitSha) ci.commitSha = run.ci.commitSha;
|
|
1053
|
+
if (run.ci.prNumber) ci.prNumber = run.ci.prNumber;
|
|
1054
|
+
report.ci = ci;
|
|
1055
|
+
}
|
|
1056
|
+
if (run.coverage) {
|
|
1057
|
+
const cov = {};
|
|
1058
|
+
if (run.coverage.linesPct !== void 0) cov.linesPct = run.coverage.linesPct;
|
|
1059
|
+
if (run.coverage.branchesPct !== void 0) cov.branchesPct = run.coverage.branchesPct;
|
|
1060
|
+
if (run.coverage.functionsPct !== void 0) cov.functionsPct = run.coverage.functionsPct;
|
|
1061
|
+
if (run.coverage.statementsPct !== void 0) cov.statementsPct = run.coverage.statementsPct;
|
|
1062
|
+
report.coverage = cov;
|
|
1063
|
+
}
|
|
1064
|
+
return report;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/formatters/story-report-json.ts
|
|
1068
|
+
var StoryReportJsonFormatter = class {
|
|
1069
|
+
options;
|
|
1070
|
+
constructor(options = {}) {
|
|
1071
|
+
this.options = {
|
|
1072
|
+
pretty: options.pretty ?? true
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
toReport(run) {
|
|
1076
|
+
return toStoryReport(run);
|
|
1077
|
+
}
|
|
1078
|
+
format(run) {
|
|
1079
|
+
const report = toStoryReport(run);
|
|
1080
|
+
return this.options.pretty ? JSON.stringify(report, null, 2) : JSON.stringify(report);
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
|
|
788
1084
|
// src/formatters/html/renderers/index.ts
|
|
789
1085
|
var fs2 = __toESM(require("fs"), 1);
|
|
790
|
-
var
|
|
1086
|
+
var path3 = __toESM(require("path"), 1);
|
|
791
1087
|
|
|
792
1088
|
// src/formatters/html/template.ts
|
|
793
1089
|
var JS_THEME = `
|
|
@@ -1138,11 +1434,53 @@ function initKeyboardShortcuts() {
|
|
|
1138
1434
|
});
|
|
1139
1435
|
}
|
|
1140
1436
|
|
|
1141
|
-
// Collapse/expand functionality
|
|
1437
|
+
// Collapse/expand functionality (persisted in localStorage)
|
|
1438
|
+
var COLLAPSE_KEY = 'es-collapsed-ids';
|
|
1439
|
+
|
|
1440
|
+
function loadCollapseState() {
|
|
1441
|
+
try {
|
|
1442
|
+
var raw = localStorage.getItem(COLLAPSE_KEY);
|
|
1443
|
+
return raw ? new Set(JSON.parse(raw)) : new Set();
|
|
1444
|
+
} catch (e) {
|
|
1445
|
+
return new Set();
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function saveCollapseState(set) {
|
|
1450
|
+
try {
|
|
1451
|
+
localStorage.setItem(COLLAPSE_KEY, JSON.stringify(Array.from(set)));
|
|
1452
|
+
} catch (e) { /* quota or disabled */ }
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
function persistCollapse(container) {
|
|
1456
|
+
if (!container || !container.id) return;
|
|
1457
|
+
var state = loadCollapseState();
|
|
1458
|
+
if (container.classList.contains('collapsed')) {
|
|
1459
|
+
state.add(container.id);
|
|
1460
|
+
} else {
|
|
1461
|
+
state.delete(container.id);
|
|
1462
|
+
}
|
|
1463
|
+
saveCollapseState(state);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1142
1466
|
function toggleCollapse(header, container) {
|
|
1143
1467
|
container?.classList.toggle('collapsed');
|
|
1144
1468
|
const isCollapsed = container?.classList.contains('collapsed');
|
|
1145
1469
|
header.setAttribute('aria-expanded', !isCollapsed);
|
|
1470
|
+
persistCollapse(container);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function restoreCollapseState() {
|
|
1474
|
+
var state = loadCollapseState();
|
|
1475
|
+
if (state.size === 0) return;
|
|
1476
|
+
state.forEach(function(id) {
|
|
1477
|
+
var el = document.getElementById(id);
|
|
1478
|
+
if (!el) return;
|
|
1479
|
+
if (!el.classList.contains('feature') && !el.classList.contains('scenario')) return;
|
|
1480
|
+
el.classList.add('collapsed');
|
|
1481
|
+
var header = el.querySelector('.feature-header, .scenario-header');
|
|
1482
|
+
if (header) header.setAttribute('aria-expanded', 'false');
|
|
1483
|
+
});
|
|
1146
1484
|
}
|
|
1147
1485
|
|
|
1148
1486
|
function initCollapse() {
|
|
@@ -1189,14 +1527,20 @@ function expandAll() {
|
|
|
1189
1527
|
const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
|
|
1190
1528
|
header?.setAttribute('aria-expanded', 'true');
|
|
1191
1529
|
});
|
|
1530
|
+
saveCollapseState(new Set());
|
|
1192
1531
|
}
|
|
1193
1532
|
|
|
1194
1533
|
function collapseAll() {
|
|
1534
|
+
var ids = new Set();
|
|
1195
1535
|
document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {
|
|
1196
1536
|
el.classList.add('collapsed');
|
|
1197
1537
|
const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
|
|
1198
1538
|
header?.setAttribute('aria-expanded', 'false');
|
|
1539
|
+
if (el.id && (el.classList.contains('feature') || el.classList.contains('scenario'))) {
|
|
1540
|
+
ids.add(el.id);
|
|
1541
|
+
}
|
|
1199
1542
|
});
|
|
1543
|
+
saveCollapseState(ids);
|
|
1200
1544
|
}
|
|
1201
1545
|
|
|
1202
1546
|
// Detail level toggle
|
|
@@ -1323,6 +1667,68 @@ function copyScenarioAsMarkdown(scenarioId) {
|
|
|
1323
1667
|
});
|
|
1324
1668
|
}
|
|
1325
1669
|
|
|
1670
|
+
// Copy scenario as Claude-ready prompt (failure investigation context)
|
|
1671
|
+
function copyScenarioAsPrompt(scenarioId) {
|
|
1672
|
+
var scenario = document.getElementById(scenarioId);
|
|
1673
|
+
if (!scenario) return;
|
|
1674
|
+
|
|
1675
|
+
var feature = scenario.closest('.feature');
|
|
1676
|
+
var featureTitle = feature ? (feature.querySelector('.feature-title') || {}).textContent || '' : '';
|
|
1677
|
+
var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';
|
|
1678
|
+
var statusEl = scenario.querySelector('.status-icon');
|
|
1679
|
+
var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
|
|
1680
|
+
statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
|
|
1681
|
+
statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
|
|
1682
|
+
var sourceLink = scenario.querySelector('.source-link');
|
|
1683
|
+
var source = sourceLink ? sourceLink.textContent || '' : '';
|
|
1684
|
+
var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.trim(); });
|
|
1685
|
+
var steps = scenario.querySelectorAll('.step, .step.continuation');
|
|
1686
|
+
|
|
1687
|
+
var lines = [];
|
|
1688
|
+
lines.push('I need help investigating a failing executable-story scenario.');
|
|
1689
|
+
lines.push('');
|
|
1690
|
+
if (featureTitle.trim()) lines.push('Feature: ' + featureTitle.trim());
|
|
1691
|
+
lines.push('Scenario: ' + title.trim());
|
|
1692
|
+
lines.push('Status: ' + status);
|
|
1693
|
+
if (source.trim()) lines.push('Source: ' + source.trim());
|
|
1694
|
+
if (tags.length > 0) lines.push('Tags: ' + tags.join(', '));
|
|
1695
|
+
lines.push('');
|
|
1696
|
+
lines.push('Steps:');
|
|
1697
|
+
steps.forEach(function(step) {
|
|
1698
|
+
var keyword = step.getAttribute('data-keyword') || '';
|
|
1699
|
+
var text = step.getAttribute('data-text') || '';
|
|
1700
|
+
var stepStatusEl = step.querySelector('.step-status');
|
|
1701
|
+
var marker = ' ';
|
|
1702
|
+
if (stepStatusEl) {
|
|
1703
|
+
if (stepStatusEl.classList.contains('status-failed')) marker = 'x ';
|
|
1704
|
+
else if (stepStatusEl.classList.contains('status-passed')) marker = '+ ';
|
|
1705
|
+
else if (stepStatusEl.classList.contains('status-skipped')) marker = '- ';
|
|
1706
|
+
}
|
|
1707
|
+
lines.push(marker + keyword + ' ' + text);
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
var errorBox = scenario.querySelector('.error-message');
|
|
1711
|
+
if (errorBox) {
|
|
1712
|
+
lines.push('');
|
|
1713
|
+
lines.push('Error:');
|
|
1714
|
+
lines.push((errorBox.textContent || '').trim());
|
|
1715
|
+
}
|
|
1716
|
+
var stackBox = scenario.querySelector('.error-stack');
|
|
1717
|
+
if (stackBox) {
|
|
1718
|
+
lines.push('');
|
|
1719
|
+
lines.push('Stack:');
|
|
1720
|
+
lines.push((stackBox.textContent || '').trim());
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
lines.push('');
|
|
1724
|
+
lines.push('Please read the source file, identify the root cause, and propose a fix.');
|
|
1725
|
+
|
|
1726
|
+
var text = lines.join('\\n');
|
|
1727
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
1728
|
+
showCopyToast(scenario);
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1326
1732
|
// Hash scroll on load
|
|
1327
1733
|
function initHashScroll() {
|
|
1328
1734
|
if (!location.hash) return;
|
|
@@ -1492,6 +1898,7 @@ function generateScript(options) {
|
|
|
1492
1898
|
initCalls.push("initStatusFilter();");
|
|
1493
1899
|
initCalls.push("initKeyboardShortcuts();");
|
|
1494
1900
|
initCalls.push("initCollapse();");
|
|
1901
|
+
initCalls.push("restoreCollapseState();");
|
|
1495
1902
|
initCalls.push("initDetailLevel();");
|
|
1496
1903
|
initCalls.push("applyAllFilters();");
|
|
1497
1904
|
initCalls.push("initHashScroll();");
|
|
@@ -1596,6 +2003,106 @@ function escapeHtml(str) {
|
|
|
1596
2003
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1597
2004
|
}
|
|
1598
2005
|
|
|
2006
|
+
// src/theme/tokens.ts
|
|
2007
|
+
var ES_THEME_TOKENS_CSS = `
|
|
2008
|
+
:root,
|
|
2009
|
+
[data-theme="light"] {
|
|
2010
|
+
--es-color-bg: #ffffff;
|
|
2011
|
+
--es-color-fg: #111827;
|
|
2012
|
+
--es-color-muted: #6b7280;
|
|
2013
|
+
--es-color-border: #e5e7eb;
|
|
2014
|
+
--es-color-surface: #f9fafb;
|
|
2015
|
+
--es-color-link: #2563eb;
|
|
2016
|
+
--es-color-passed: #16a34a;
|
|
2017
|
+
--es-color-failed: #dc2626;
|
|
2018
|
+
--es-color-skipped: #9ca3af;
|
|
2019
|
+
--es-color-pending: #d97706;
|
|
2020
|
+
--es-color-passed-bg: #f0fdf4;
|
|
2021
|
+
--es-color-failed-bg: #fef2f2;
|
|
2022
|
+
--es-color-skipped-bg: #f3f4f6;
|
|
2023
|
+
--es-color-pending-bg: #fffbeb;
|
|
2024
|
+
--es-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
2025
|
+
--es-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
2026
|
+
--es-size-base: 1rem;
|
|
2027
|
+
--es-size-sm: 0.875rem;
|
|
2028
|
+
--es-size-xs: 0.75rem;
|
|
2029
|
+
--es-size-h1: 1.875rem;
|
|
2030
|
+
--es-size-h2: 1.5rem;
|
|
2031
|
+
--es-size-h3: 1.25rem;
|
|
2032
|
+
--es-space-1: 0.25rem;
|
|
2033
|
+
--es-space-2: 0.5rem;
|
|
2034
|
+
--es-space-3: 0.75rem;
|
|
2035
|
+
--es-space-4: 1rem;
|
|
2036
|
+
--es-space-6: 1.5rem;
|
|
2037
|
+
--es-space-8: 2rem;
|
|
2038
|
+
--es-radius: 0.5rem;
|
|
2039
|
+
--es-line: 1.6;
|
|
2040
|
+
--es-measure: 72ch;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
@media (prefers-color-scheme: dark) {
|
|
2044
|
+
:root {
|
|
2045
|
+
--es-color-bg: #0b0f17;
|
|
2046
|
+
--es-color-fg: #e5e7eb;
|
|
2047
|
+
--es-color-muted: #9ca3af;
|
|
2048
|
+
--es-color-border: #1f2937;
|
|
2049
|
+
--es-color-surface: #111827;
|
|
2050
|
+
--es-color-link: #60a5fa;
|
|
2051
|
+
--es-color-passed: #4ade80;
|
|
2052
|
+
--es-color-failed: #f87171;
|
|
2053
|
+
--es-color-skipped: #6b7280;
|
|
2054
|
+
--es-color-pending: #fbbf24;
|
|
2055
|
+
--es-color-passed-bg: rgba(74, 222, 128, 0.08);
|
|
2056
|
+
--es-color-failed-bg: rgba(248, 113, 113, 0.08);
|
|
2057
|
+
--es-color-skipped-bg: rgba(107, 114, 128, 0.08);
|
|
2058
|
+
--es-color-pending-bg: rgba(251, 191, 36, 0.08);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
[data-theme="dark"] {
|
|
2063
|
+
--es-color-bg: #0b0f17;
|
|
2064
|
+
--es-color-fg: #e5e7eb;
|
|
2065
|
+
--es-color-muted: #9ca3af;
|
|
2066
|
+
--es-color-border: #1f2937;
|
|
2067
|
+
--es-color-surface: #111827;
|
|
2068
|
+
--es-color-link: #60a5fa;
|
|
2069
|
+
--es-color-passed: #4ade80;
|
|
2070
|
+
--es-color-failed: #f87171;
|
|
2071
|
+
--es-color-skipped: #6b7280;
|
|
2072
|
+
--es-color-pending: #fbbf24;
|
|
2073
|
+
--es-color-passed-bg: rgba(74, 222, 128, 0.08);
|
|
2074
|
+
--es-color-failed-bg: rgba(248, 113, 113, 0.08);
|
|
2075
|
+
--es-color-skipped-bg: rgba(107, 114, 128, 0.08);
|
|
2076
|
+
--es-color-pending-bg: rgba(251, 191, 36, 0.08);
|
|
2077
|
+
}
|
|
2078
|
+
`.trim();
|
|
2079
|
+
var ES_THEME_TOKEN_VALUES = {
|
|
2080
|
+
light: {
|
|
2081
|
+
"--es-color-bg": "#ffffff",
|
|
2082
|
+
"--es-color-fg": "#111827",
|
|
2083
|
+
"--es-color-muted": "#6b7280",
|
|
2084
|
+
"--es-color-border": "#e5e7eb",
|
|
2085
|
+
"--es-color-surface": "#f9fafb",
|
|
2086
|
+
"--es-color-link": "#2563eb",
|
|
2087
|
+
"--es-color-passed": "#16a34a",
|
|
2088
|
+
"--es-color-failed": "#dc2626",
|
|
2089
|
+
"--es-color-skipped": "#9ca3af",
|
|
2090
|
+
"--es-color-pending": "#d97706"
|
|
2091
|
+
},
|
|
2092
|
+
dark: {
|
|
2093
|
+
"--es-color-bg": "#0b0f17",
|
|
2094
|
+
"--es-color-fg": "#e5e7eb",
|
|
2095
|
+
"--es-color-muted": "#9ca3af",
|
|
2096
|
+
"--es-color-border": "#1f2937",
|
|
2097
|
+
"--es-color-surface": "#111827",
|
|
2098
|
+
"--es-color-link": "#60a5fa",
|
|
2099
|
+
"--es-color-passed": "#4ade80",
|
|
2100
|
+
"--es-color-failed": "#f87171",
|
|
2101
|
+
"--es-color-skipped": "#6b7280",
|
|
2102
|
+
"--es-color-pending": "#fbbf24"
|
|
2103
|
+
}
|
|
2104
|
+
};
|
|
2105
|
+
|
|
1599
2106
|
// src/formatters/html/styles.ts
|
|
1600
2107
|
var CSS_STYLES = `
|
|
1601
2108
|
/* ============================================================================
|
|
@@ -1603,6 +2110,14 @@ var CSS_STYLES = `
|
|
|
1603
2110
|
============================================================================ */
|
|
1604
2111
|
@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');
|
|
1605
2112
|
|
|
2113
|
+
/* ============================================================================
|
|
2114
|
+
executable-stories canonical tokens (--es-*).
|
|
2115
|
+
Shared with executable-stories-react. Override on :root or any ancestor of
|
|
2116
|
+
the report to re-color both the standalone HTML and the React component.
|
|
2117
|
+
============================================================================ */
|
|
2118
|
+
${ES_THEME_TOKENS_CSS}
|
|
2119
|
+
|
|
2120
|
+
|
|
1606
2121
|
/* ============================================================================
|
|
1607
2122
|
CSS Custom Properties - Light Mode (Default)
|
|
1608
2123
|
Cucumber-branded shadcn/ui base theme
|
|
@@ -3491,6 +4006,33 @@ body {
|
|
|
3491
4006
|
color: var(--primary);
|
|
3492
4007
|
}
|
|
3493
4008
|
|
|
4009
|
+
.copy-prompt-btn {
|
|
4010
|
+
display: inline-flex;
|
|
4011
|
+
align-items: center;
|
|
4012
|
+
justify-content: center;
|
|
4013
|
+
width: 1.5rem;
|
|
4014
|
+
height: 1.5rem;
|
|
4015
|
+
border: none;
|
|
4016
|
+
background: none;
|
|
4017
|
+
color: var(--muted-foreground);
|
|
4018
|
+
cursor: pointer;
|
|
4019
|
+
opacity: 0.6;
|
|
4020
|
+
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
4021
|
+
font-size: 0.95rem;
|
|
4022
|
+
padding: 0;
|
|
4023
|
+
flex-shrink: 0;
|
|
4024
|
+
}
|
|
4025
|
+
|
|
4026
|
+
.scenario-header:hover .copy-prompt-btn,
|
|
4027
|
+
.copy-prompt-btn:focus-visible {
|
|
4028
|
+
opacity: 1;
|
|
4029
|
+
}
|
|
4030
|
+
|
|
4031
|
+
.copy-prompt-btn:hover {
|
|
4032
|
+
color: var(--primary);
|
|
4033
|
+
transform: scale(1.15);
|
|
4034
|
+
}
|
|
4035
|
+
|
|
3494
4036
|
/* ============================================================================
|
|
3495
4037
|
Keyboard Navigation
|
|
3496
4038
|
============================================================================ */
|
|
@@ -3728,6 +4270,82 @@ a.toc-title:hover {
|
|
|
3728
4270
|
outline-offset: 2px;
|
|
3729
4271
|
}
|
|
3730
4272
|
|
|
4273
|
+
/* ============================================================================
|
|
4274
|
+
Mobile responsive refinements
|
|
4275
|
+
============================================================================ */
|
|
4276
|
+
@media (max-width: 640px) {
|
|
4277
|
+
.container {
|
|
4278
|
+
padding: 0.875rem;
|
|
4279
|
+
}
|
|
4280
|
+
|
|
4281
|
+
.header {
|
|
4282
|
+
flex-direction: column;
|
|
4283
|
+
align-items: stretch;
|
|
4284
|
+
gap: 0.75rem;
|
|
4285
|
+
}
|
|
4286
|
+
|
|
4287
|
+
.header-actions {
|
|
4288
|
+
flex-wrap: wrap;
|
|
4289
|
+
gap: 0.5rem;
|
|
4290
|
+
}
|
|
4291
|
+
|
|
4292
|
+
.search-input {
|
|
4293
|
+
width: 100%;
|
|
4294
|
+
flex: 1 1 100%;
|
|
4295
|
+
min-width: 0;
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4298
|
+
.header h1 {
|
|
4299
|
+
font-size: 1.25rem;
|
|
4300
|
+
}
|
|
4301
|
+
|
|
4302
|
+
.scenario-header,
|
|
4303
|
+
.feature-header {
|
|
4304
|
+
flex-wrap: wrap;
|
|
4305
|
+
gap: 0.5rem;
|
|
4306
|
+
}
|
|
4307
|
+
|
|
4308
|
+
.scenario-meta {
|
|
4309
|
+
flex-wrap: wrap;
|
|
4310
|
+
}
|
|
4311
|
+
|
|
4312
|
+
.scenario-actions {
|
|
4313
|
+
flex-wrap: wrap;
|
|
4314
|
+
}
|
|
4315
|
+
|
|
4316
|
+
/* Always-visible action buttons on touch (no hover) */
|
|
4317
|
+
.copy-scenario-btn,
|
|
4318
|
+
.copy-prompt-btn,
|
|
4319
|
+
.permalink-anchor {
|
|
4320
|
+
opacity: 1 !important;
|
|
4321
|
+
}
|
|
4322
|
+
|
|
4323
|
+
.summary-card {
|
|
4324
|
+
padding: 0.75rem 0.875rem;
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4327
|
+
.summary-card .value {
|
|
4328
|
+
font-size: 1.5rem;
|
|
4329
|
+
}
|
|
4330
|
+
|
|
4331
|
+
.tag-bar {
|
|
4332
|
+
overflow-x: auto;
|
|
4333
|
+
-webkit-overflow-scrolling: touch;
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
.shortcuts-overlay {
|
|
4337
|
+
padding: 1rem;
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
|
|
4341
|
+
@media (hover: none) and (pointer: coarse) {
|
|
4342
|
+
.copy-scenario-btn,
|
|
4343
|
+
.copy-prompt-btn,
|
|
4344
|
+
.permalink-anchor {
|
|
4345
|
+
opacity: 1;
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
|
|
3731
4349
|
`;
|
|
3732
4350
|
|
|
3733
4351
|
// src/formatters/html/themes/default.ts
|
|
@@ -13454,6 +14072,7 @@ function renderScenario(args, deps) {
|
|
|
13454
14072
|
</div>
|
|
13455
14073
|
<div class="scenario-actions">
|
|
13456
14074
|
<button class="copy-scenario-btn" onclick="copyScenarioAsMarkdown('scenario-${tc.id}')" aria-label="Copy scenario as markdown" title="Copy as Markdown"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>
|
|
14075
|
+
${tc.status === "failed" ? `<button class="copy-prompt-btn" onclick="copyScenarioAsPrompt('scenario-${tc.id}')" aria-label="Copy as Claude prompt" title="Copy as AI prompt">\u2728</button>` : ""}
|
|
13457
14076
|
<button class="permalink-anchor" onclick="copyPermalink('scenario-${tc.id}')" aria-label="Copy link to scenario" title="Copy link">#</button>
|
|
13458
14077
|
<span class="scenario-duration">${duration}</span>
|
|
13459
14078
|
</div>
|
|
@@ -13853,7 +14472,7 @@ var SCREENSHOT_MIME_BY_EXT = {
|
|
|
13853
14472
|
};
|
|
13854
14473
|
function readScreenshotAsDataUri(filePath) {
|
|
13855
14474
|
try {
|
|
13856
|
-
const ext =
|
|
14475
|
+
const ext = path3.extname(filePath).slice(1).toLowerCase();
|
|
13857
14476
|
const mime = SCREENSHOT_MIME_BY_EXT[ext];
|
|
13858
14477
|
if (!mime) return void 0;
|
|
13859
14478
|
if (!fs2.existsSync(filePath)) return void 0;
|
|
@@ -15330,8 +15949,8 @@ function extractDocAttachments(step) {
|
|
|
15330
15949
|
}
|
|
15331
15950
|
return attachments;
|
|
15332
15951
|
}
|
|
15333
|
-
function guessMediaType(
|
|
15334
|
-
const lower =
|
|
15952
|
+
function guessMediaType(path10) {
|
|
15953
|
+
const lower = path10.toLowerCase();
|
|
15335
15954
|
if (lower.endsWith(".png")) return "image/png";
|
|
15336
15955
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
15337
15956
|
if (lower.endsWith(".gif")) return "image/gif";
|
|
@@ -16376,7 +16995,7 @@ function selectTestCases(args, deps) {
|
|
|
16376
16995
|
|
|
16377
16996
|
// src/bundler/bundle-assets.ts
|
|
16378
16997
|
var fs4 = __toESM(require("fs"), 1);
|
|
16379
|
-
var
|
|
16998
|
+
var path5 = __toESM(require("path"), 1);
|
|
16380
16999
|
|
|
16381
17000
|
// src/bundler/scan-html-assets.ts
|
|
16382
17001
|
function scanHtmlAssets(html) {
|
|
@@ -16407,7 +17026,7 @@ function isLocalAssetRef(ref) {
|
|
|
16407
17026
|
|
|
16408
17027
|
// src/bundler/copy-asset.ts
|
|
16409
17028
|
var fs3 = __toESM(require("fs"), 1);
|
|
16410
|
-
var
|
|
17029
|
+
var path4 = __toESM(require("path"), 1);
|
|
16411
17030
|
var crypto = __toESM(require("crypto"), 1);
|
|
16412
17031
|
function copyAsset(sourcePath, assetsDir) {
|
|
16413
17032
|
if (!fs3.existsSync(assetsDir)) {
|
|
@@ -16415,10 +17034,10 @@ function copyAsset(sourcePath, assetsDir) {
|
|
|
16415
17034
|
}
|
|
16416
17035
|
const content = fs3.readFileSync(sourcePath);
|
|
16417
17036
|
const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
16418
|
-
const ext =
|
|
16419
|
-
const baseName = sanitize(
|
|
17037
|
+
const ext = path4.extname(sourcePath);
|
|
17038
|
+
const baseName = sanitize(path4.basename(sourcePath, ext));
|
|
16420
17039
|
const destName = `${baseName}-${hash}${ext}`;
|
|
16421
|
-
const destPath =
|
|
17040
|
+
const destPath = path4.join(assetsDir, destName);
|
|
16422
17041
|
if (!fs3.existsSync(destPath)) {
|
|
16423
17042
|
fs3.copyFileSync(sourcePath, destPath);
|
|
16424
17043
|
}
|
|
@@ -16430,14 +17049,14 @@ function sanitize(name) {
|
|
|
16430
17049
|
|
|
16431
17050
|
// src/bundler/bundle-assets.ts
|
|
16432
17051
|
function bundleAssets(htmlPath, options = {}) {
|
|
16433
|
-
const htmlDir =
|
|
16434
|
-
const assetsDir =
|
|
17052
|
+
const htmlDir = path5.dirname(htmlPath);
|
|
17053
|
+
const assetsDir = path5.join(htmlDir, "assets");
|
|
16435
17054
|
let html = fs4.readFileSync(htmlPath, "utf8");
|
|
16436
17055
|
const refs = scanHtmlAssets(html);
|
|
16437
17056
|
let copiedCount = 0;
|
|
16438
17057
|
const missing = [];
|
|
16439
17058
|
for (const ref of refs) {
|
|
16440
|
-
const absolutePath =
|
|
17059
|
+
const absolutePath = path5.resolve(htmlDir, ref);
|
|
16441
17060
|
if (!fs4.existsSync(absolutePath)) {
|
|
16442
17061
|
missing.push(ref);
|
|
16443
17062
|
continue;
|
|
@@ -16935,14 +17554,14 @@ function groupBy7(items, keyFn) {
|
|
|
16935
17554
|
|
|
16936
17555
|
// src/formatters/astro-assets.ts
|
|
16937
17556
|
var fs5 = __toESM(require("fs"), 1);
|
|
16938
|
-
var
|
|
17557
|
+
var path6 = __toESM(require("path"), 1);
|
|
16939
17558
|
var SKIP_PREFIXES = ["http://", "https://", "data:", "#"];
|
|
16940
17559
|
function isLocalPath(src) {
|
|
16941
17560
|
const trimmed = src.trim();
|
|
16942
17561
|
if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
|
|
16943
17562
|
return false;
|
|
16944
17563
|
}
|
|
16945
|
-
return !
|
|
17564
|
+
return !path6.posix.isAbsolute(trimmed) && !path6.win32.isAbsolute(trimmed);
|
|
16946
17565
|
}
|
|
16947
17566
|
function stripCodeContent(markdown) {
|
|
16948
17567
|
let result = markdown.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$/gm, "");
|
|
@@ -17036,7 +17655,7 @@ function copyMarkdownAssets(options) {
|
|
|
17036
17655
|
const pathMap = /* @__PURE__ */ new Map();
|
|
17037
17656
|
const missing = [];
|
|
17038
17657
|
for (const ref of refs) {
|
|
17039
|
-
const absPath =
|
|
17658
|
+
const absPath = path6.resolve(markdownDir, ref);
|
|
17040
17659
|
if (!fs5.existsSync(absPath)) {
|
|
17041
17660
|
if (!allowMissing) {
|
|
17042
17661
|
throw new Error(`Asset not found: ${absPath}`);
|
|
@@ -18061,24 +18680,24 @@ function pickleStepArgumentToDocs(ps) {
|
|
|
18061
18680
|
|
|
18062
18681
|
// src/utils/git-info.ts
|
|
18063
18682
|
var fs6 = __toESM(require("fs"), 1);
|
|
18064
|
-
var
|
|
18683
|
+
var path7 = __toESM(require("path"), 1);
|
|
18065
18684
|
function readGitSha(cwd = process.cwd()) {
|
|
18066
18685
|
const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;
|
|
18067
18686
|
if (envSha) return envSha;
|
|
18068
18687
|
const gitDir = findGitDir(cwd);
|
|
18069
18688
|
if (!gitDir) return void 0;
|
|
18070
18689
|
try {
|
|
18071
|
-
const headPath =
|
|
18690
|
+
const headPath = path7.join(gitDir, "HEAD");
|
|
18072
18691
|
const head = fs6.readFileSync(headPath, "utf8").trim();
|
|
18073
18692
|
if (!head.startsWith("ref:")) {
|
|
18074
18693
|
return head;
|
|
18075
18694
|
}
|
|
18076
18695
|
const refPath = head.replace("ref:", "").trim();
|
|
18077
|
-
const refFile =
|
|
18696
|
+
const refFile = path7.join(gitDir, refPath);
|
|
18078
18697
|
if (fs6.existsSync(refFile)) {
|
|
18079
18698
|
return fs6.readFileSync(refFile, "utf8").trim();
|
|
18080
18699
|
}
|
|
18081
|
-
const packedRefs =
|
|
18700
|
+
const packedRefs = path7.join(gitDir, "packed-refs");
|
|
18082
18701
|
if (fs6.existsSync(packedRefs)) {
|
|
18083
18702
|
const content = fs6.readFileSync(packedRefs, "utf8");
|
|
18084
18703
|
for (const line of content.split("\n")) {
|
|
@@ -18095,19 +18714,19 @@ function readGitSha(cwd = process.cwd()) {
|
|
|
18095
18714
|
function findGitDir(start) {
|
|
18096
18715
|
let current = start;
|
|
18097
18716
|
while (true) {
|
|
18098
|
-
const candidate =
|
|
18717
|
+
const candidate = path7.join(current, ".git");
|
|
18099
18718
|
if (fs6.existsSync(candidate)) {
|
|
18100
18719
|
const stat = fs6.statSync(candidate);
|
|
18101
18720
|
if (stat.isFile()) {
|
|
18102
18721
|
const content = fs6.readFileSync(candidate, "utf8").trim();
|
|
18103
18722
|
const match = content.match(/^gitdir: (.+)$/);
|
|
18104
18723
|
if (match) {
|
|
18105
|
-
return
|
|
18724
|
+
return path7.resolve(current, match[1]);
|
|
18106
18725
|
}
|
|
18107
18726
|
}
|
|
18108
18727
|
return candidate;
|
|
18109
18728
|
}
|
|
18110
|
-
const parent =
|
|
18729
|
+
const parent = path7.dirname(current);
|
|
18111
18730
|
if (parent === current) return void 0;
|
|
18112
18731
|
current = parent;
|
|
18113
18732
|
}
|
|
@@ -18118,7 +18737,7 @@ function readBranchName(cwd = process.cwd()) {
|
|
|
18118
18737
|
const gitDir = findGitDir(cwd);
|
|
18119
18738
|
if (!gitDir) return void 0;
|
|
18120
18739
|
try {
|
|
18121
|
-
const headPath =
|
|
18740
|
+
const headPath = path7.join(gitDir, "HEAD");
|
|
18122
18741
|
const head = fs6.readFileSync(headPath, "utf8").trim();
|
|
18123
18742
|
if (head.startsWith("ref:")) {
|
|
18124
18743
|
const refPath = head.replace("ref:", "").trim();
|
|
@@ -18157,7 +18776,7 @@ function nanosecondsToMs(ns) {
|
|
|
18157
18776
|
|
|
18158
18777
|
// src/utils/metadata.ts
|
|
18159
18778
|
var fs7 = __toESM(require("fs"), 1);
|
|
18160
|
-
var
|
|
18779
|
+
var path8 = __toESM(require("path"), 1);
|
|
18161
18780
|
var versionCache = /* @__PURE__ */ new Map();
|
|
18162
18781
|
function readPackageVersion(root) {
|
|
18163
18782
|
if (versionCache.has(root)) {
|
|
@@ -18168,9 +18787,9 @@ function readPackageVersion(root) {
|
|
|
18168
18787
|
return version;
|
|
18169
18788
|
}
|
|
18170
18789
|
function findPackageVersion(startDir) {
|
|
18171
|
-
let current =
|
|
18790
|
+
let current = path8.resolve(startDir);
|
|
18172
18791
|
while (true) {
|
|
18173
|
-
const pkgPath =
|
|
18792
|
+
const pkgPath = path8.join(current, "package.json");
|
|
18174
18793
|
try {
|
|
18175
18794
|
if (fs7.existsSync(pkgPath)) {
|
|
18176
18795
|
const raw = fs7.readFileSync(pkgPath, "utf8");
|
|
@@ -18179,7 +18798,7 @@ function findPackageVersion(startDir) {
|
|
|
18179
18798
|
}
|
|
18180
18799
|
} catch {
|
|
18181
18800
|
}
|
|
18182
|
-
const parent =
|
|
18801
|
+
const parent = path8.dirname(current);
|
|
18183
18802
|
if (parent === current) {
|
|
18184
18803
|
return void 0;
|
|
18185
18804
|
}
|
|
@@ -19138,7 +19757,8 @@ var FORMAT_EXTENSIONS = {
|
|
|
19138
19757
|
junit: ".junit.xml",
|
|
19139
19758
|
"cucumber-json": ".cucumber.json",
|
|
19140
19759
|
"cucumber-messages": ".ndjson",
|
|
19141
|
-
confluence: ".adf.json"
|
|
19760
|
+
confluence: ".adf.json",
|
|
19761
|
+
"story-report-json": ".story-report.json"
|
|
19142
19762
|
};
|
|
19143
19763
|
var TEST_EXTENSIONS = [
|
|
19144
19764
|
".test.ts",
|
|
@@ -19165,11 +19785,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
19165
19785
|
const ext = FORMAT_EXTENSIONS[format];
|
|
19166
19786
|
const effectiveName = outputName + (outputNameSuffix ?? "");
|
|
19167
19787
|
if (mode === "aggregated") {
|
|
19168
|
-
return toPosix(
|
|
19788
|
+
return toPosix(path9.join(baseOutputDir, `${effectiveName}${ext}`));
|
|
19169
19789
|
}
|
|
19170
19790
|
const normalizedSource = toPosix(sourceFile);
|
|
19171
|
-
const dirOfSource =
|
|
19172
|
-
let baseName =
|
|
19791
|
+
const dirOfSource = path9.posix.dirname(normalizedSource);
|
|
19792
|
+
let baseName = path9.posix.basename(normalizedSource);
|
|
19173
19793
|
for (const testExt of TEST_EXTENSIONS) {
|
|
19174
19794
|
if (baseName.endsWith(testExt)) {
|
|
19175
19795
|
baseName = baseName.slice(0, -testExt.length);
|
|
@@ -19178,9 +19798,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
19178
19798
|
}
|
|
19179
19799
|
const fileName = `${baseName}.${effectiveName}${ext}`;
|
|
19180
19800
|
if (colocatedStyle === "adjacent") {
|
|
19181
|
-
return toPosix(
|
|
19801
|
+
return toPosix(path9.posix.join(dirOfSource, fileName));
|
|
19182
19802
|
}
|
|
19183
|
-
return toPosix(
|
|
19803
|
+
return toPosix(path9.posix.join(baseOutputDir, dirOfSource, fileName));
|
|
19184
19804
|
}
|
|
19185
19805
|
function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
|
|
19186
19806
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -19249,7 +19869,7 @@ var ReportGenerator = class {
|
|
|
19249
19869
|
exclude: options.exclude ?? [],
|
|
19250
19870
|
includeTags: options.includeTags ?? [],
|
|
19251
19871
|
excludeTags: options.excludeTags ?? [],
|
|
19252
|
-
formats: options.formats ?? ["
|
|
19872
|
+
formats: options.formats ?? ["html"],
|
|
19253
19873
|
outputDir: options.outputDir ?? "reports",
|
|
19254
19874
|
outputName: options.outputName ?? "index",
|
|
19255
19875
|
outputNameTimestamp: options.outputNameTimestamp ?? false,
|
|
@@ -19263,6 +19883,9 @@ var ReportGenerator = class {
|
|
|
19263
19883
|
cucumberJson: {
|
|
19264
19884
|
pretty: options.cucumberJson?.pretty ?? false
|
|
19265
19885
|
},
|
|
19886
|
+
storyReportJson: {
|
|
19887
|
+
pretty: options.storyReportJson?.pretty ?? true
|
|
19888
|
+
},
|
|
19266
19889
|
cucumberMessages: {
|
|
19267
19890
|
uriStrategy: options.cucumberMessages?.uriStrategy ?? "sourceFile",
|
|
19268
19891
|
includeSynthetics: options.cucumberMessages?.includeSynthetics ?? true,
|
|
@@ -19378,8 +20001,8 @@ var ReportGenerator = class {
|
|
|
19378
20001
|
if (astroPaths) {
|
|
19379
20002
|
for (const mdPath of astroPaths) {
|
|
19380
20003
|
const content = await fsPromises.readFile(mdPath, "utf8");
|
|
19381
|
-
const mdDir =
|
|
19382
|
-
const assetsDir =
|
|
20004
|
+
const mdDir = path9.dirname(mdPath);
|
|
20005
|
+
const assetsDir = path9.resolve(this.options.astro.assetsDir);
|
|
19383
20006
|
const result = copyMarkdownAssets({
|
|
19384
20007
|
markdown: content,
|
|
19385
20008
|
markdownDir: mdDir,
|
|
@@ -19410,9 +20033,9 @@ var ReportGenerator = class {
|
|
|
19410
20033
|
if (groups.size === 0 && this.options.output.mode === "aggregated") {
|
|
19411
20034
|
const ext = FORMAT_EXTENSIONS[format];
|
|
19412
20035
|
const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
|
|
19413
|
-
const outputPath = toPosix(
|
|
20036
|
+
const outputPath = toPosix(path9.join(this.options.outputDir, `${effectiveName}${ext}`));
|
|
19414
20037
|
const content = await this.formatContent(run, format);
|
|
19415
|
-
const dir =
|
|
20038
|
+
const dir = path9.dirname(outputPath);
|
|
19416
20039
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
19417
20040
|
await this.deps.writeFile(outputPath, content);
|
|
19418
20041
|
return [outputPath];
|
|
@@ -19424,7 +20047,7 @@ var ReportGenerator = class {
|
|
|
19424
20047
|
testCases
|
|
19425
20048
|
};
|
|
19426
20049
|
const content = await this.formatContent(groupRun, format);
|
|
19427
|
-
const dir =
|
|
20050
|
+
const dir = path9.dirname(outputPath);
|
|
19428
20051
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
19429
20052
|
await this.deps.writeFile(outputPath, content);
|
|
19430
20053
|
writtenPaths.push(outputPath);
|
|
@@ -19531,6 +20154,12 @@ var ReportGenerator = class {
|
|
|
19531
20154
|
});
|
|
19532
20155
|
return formatter.format(run);
|
|
19533
20156
|
}
|
|
20157
|
+
case "story-report-json": {
|
|
20158
|
+
const formatter = new StoryReportJsonFormatter({
|
|
20159
|
+
pretty: this.options.storyReportJson.pretty
|
|
20160
|
+
});
|
|
20161
|
+
return formatter.format(run);
|
|
20162
|
+
}
|
|
19534
20163
|
default:
|
|
19535
20164
|
throw new Error(`Unknown format: ${format}`);
|
|
19536
20165
|
}
|
|
@@ -19547,7 +20176,7 @@ async function generateRunComparison(args) {
|
|
|
19547
20176
|
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
19548
20177
|
for (const format of args.formats) {
|
|
19549
20178
|
const ext = format === "html" ? ".html" : ".md";
|
|
19550
|
-
const outputPath = toPosix(
|
|
20179
|
+
const outputPath = toPosix(path9.join(outputDir, `${outputName}${ext}`));
|
|
19551
20180
|
const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
|
|
19552
20181
|
await fsPromises.writeFile(outputPath, content, "utf8");
|
|
19553
20182
|
files.push(outputPath);
|
|
@@ -19573,6 +20202,8 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
19573
20202
|
CucumberHtmlFormatter,
|
|
19574
20203
|
CucumberJsonFormatter,
|
|
19575
20204
|
CucumberMessagesFormatter,
|
|
20205
|
+
ES_THEME_TOKENS_CSS,
|
|
20206
|
+
ES_THEME_TOKEN_VALUES,
|
|
19576
20207
|
HtmlFormatter,
|
|
19577
20208
|
JUnitFormatter,
|
|
19578
20209
|
MIN_FLAKINESS_SAMPLES,
|
|
@@ -19583,6 +20214,9 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
19583
20214
|
RunDiffHtmlFormatter,
|
|
19584
20215
|
RunDiffMarkdownFormatter,
|
|
19585
20216
|
STORY_META_KEY,
|
|
20217
|
+
STORY_REPORT_SCHEMA_MAJOR,
|
|
20218
|
+
STORY_REPORT_SCHEMA_VERSION,
|
|
20219
|
+
StoryReportJsonFormatter,
|
|
19586
20220
|
adaptJestRun,
|
|
19587
20221
|
adaptPlaywrightRun,
|
|
19588
20222
|
adaptVitestRun,
|
|
@@ -19639,6 +20273,7 @@ function normalizePlaywrightResults(testResults, adapterOptions, canonicalizeOpt
|
|
|
19639
20273
|
stripAnsi,
|
|
19640
20274
|
toCIInfo,
|
|
19641
20275
|
toRawCIInfo,
|
|
20276
|
+
toStoryReport,
|
|
19642
20277
|
tryGetActiveOtelContext,
|
|
19643
20278
|
updateHistory,
|
|
19644
20279
|
validateCanonicalRun
|