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.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 = `
|
|
@@ -1031,11 +1321,53 @@ function initKeyboardShortcuts() {
|
|
|
1031
1321
|
});
|
|
1032
1322
|
}
|
|
1033
1323
|
|
|
1034
|
-
// Collapse/expand functionality
|
|
1324
|
+
// Collapse/expand functionality (persisted in localStorage)
|
|
1325
|
+
var COLLAPSE_KEY = 'es-collapsed-ids';
|
|
1326
|
+
|
|
1327
|
+
function loadCollapseState() {
|
|
1328
|
+
try {
|
|
1329
|
+
var raw = localStorage.getItem(COLLAPSE_KEY);
|
|
1330
|
+
return raw ? new Set(JSON.parse(raw)) : new Set();
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
return new Set();
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
function saveCollapseState(set) {
|
|
1337
|
+
try {
|
|
1338
|
+
localStorage.setItem(COLLAPSE_KEY, JSON.stringify(Array.from(set)));
|
|
1339
|
+
} catch (e) { /* quota or disabled */ }
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
function persistCollapse(container) {
|
|
1343
|
+
if (!container || !container.id) return;
|
|
1344
|
+
var state = loadCollapseState();
|
|
1345
|
+
if (container.classList.contains('collapsed')) {
|
|
1346
|
+
state.add(container.id);
|
|
1347
|
+
} else {
|
|
1348
|
+
state.delete(container.id);
|
|
1349
|
+
}
|
|
1350
|
+
saveCollapseState(state);
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1035
1353
|
function toggleCollapse(header, container) {
|
|
1036
1354
|
container?.classList.toggle('collapsed');
|
|
1037
1355
|
const isCollapsed = container?.classList.contains('collapsed');
|
|
1038
1356
|
header.setAttribute('aria-expanded', !isCollapsed);
|
|
1357
|
+
persistCollapse(container);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
function restoreCollapseState() {
|
|
1361
|
+
var state = loadCollapseState();
|
|
1362
|
+
if (state.size === 0) return;
|
|
1363
|
+
state.forEach(function(id) {
|
|
1364
|
+
var el = document.getElementById(id);
|
|
1365
|
+
if (!el) return;
|
|
1366
|
+
if (!el.classList.contains('feature') && !el.classList.contains('scenario')) return;
|
|
1367
|
+
el.classList.add('collapsed');
|
|
1368
|
+
var header = el.querySelector('.feature-header, .scenario-header');
|
|
1369
|
+
if (header) header.setAttribute('aria-expanded', 'false');
|
|
1370
|
+
});
|
|
1039
1371
|
}
|
|
1040
1372
|
|
|
1041
1373
|
function initCollapse() {
|
|
@@ -1082,14 +1414,20 @@ function expandAll() {
|
|
|
1082
1414
|
const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
|
|
1083
1415
|
header?.setAttribute('aria-expanded', 'true');
|
|
1084
1416
|
});
|
|
1417
|
+
saveCollapseState(new Set());
|
|
1085
1418
|
}
|
|
1086
1419
|
|
|
1087
1420
|
function collapseAll() {
|
|
1421
|
+
var ids = new Set();
|
|
1088
1422
|
document.querySelectorAll('.feature, .scenario, .trace-view').forEach(el => {
|
|
1089
1423
|
el.classList.add('collapsed');
|
|
1090
1424
|
const header = el.querySelector('.feature-header, .scenario-header, .trace-view-header');
|
|
1091
1425
|
header?.setAttribute('aria-expanded', 'false');
|
|
1426
|
+
if (el.id && (el.classList.contains('feature') || el.classList.contains('scenario'))) {
|
|
1427
|
+
ids.add(el.id);
|
|
1428
|
+
}
|
|
1092
1429
|
});
|
|
1430
|
+
saveCollapseState(ids);
|
|
1093
1431
|
}
|
|
1094
1432
|
|
|
1095
1433
|
// Detail level toggle
|
|
@@ -1216,6 +1554,68 @@ function copyScenarioAsMarkdown(scenarioId) {
|
|
|
1216
1554
|
});
|
|
1217
1555
|
}
|
|
1218
1556
|
|
|
1557
|
+
// Copy scenario as Claude-ready prompt (failure investigation context)
|
|
1558
|
+
function copyScenarioAsPrompt(scenarioId) {
|
|
1559
|
+
var scenario = document.getElementById(scenarioId);
|
|
1560
|
+
if (!scenario) return;
|
|
1561
|
+
|
|
1562
|
+
var feature = scenario.closest('.feature');
|
|
1563
|
+
var featureTitle = feature ? (feature.querySelector('.feature-title') || {}).textContent || '' : '';
|
|
1564
|
+
var title = (scenario.querySelector('.scenario-name') || {}).textContent || '';
|
|
1565
|
+
var statusEl = scenario.querySelector('.status-icon');
|
|
1566
|
+
var status = statusEl && statusEl.classList.contains('status-passed') ? 'passed' :
|
|
1567
|
+
statusEl && statusEl.classList.contains('status-failed') ? 'failed' :
|
|
1568
|
+
statusEl && statusEl.classList.contains('status-skipped') ? 'skipped' : 'pending';
|
|
1569
|
+
var sourceLink = scenario.querySelector('.source-link');
|
|
1570
|
+
var source = sourceLink ? sourceLink.textContent || '' : '';
|
|
1571
|
+
var tags = Array.from(scenario.querySelectorAll('.scenario-meta .tag')).map(function(t) { return t.textContent.trim(); });
|
|
1572
|
+
var steps = scenario.querySelectorAll('.step, .step.continuation');
|
|
1573
|
+
|
|
1574
|
+
var lines = [];
|
|
1575
|
+
lines.push('I need help investigating a failing executable-story scenario.');
|
|
1576
|
+
lines.push('');
|
|
1577
|
+
if (featureTitle.trim()) lines.push('Feature: ' + featureTitle.trim());
|
|
1578
|
+
lines.push('Scenario: ' + title.trim());
|
|
1579
|
+
lines.push('Status: ' + status);
|
|
1580
|
+
if (source.trim()) lines.push('Source: ' + source.trim());
|
|
1581
|
+
if (tags.length > 0) lines.push('Tags: ' + tags.join(', '));
|
|
1582
|
+
lines.push('');
|
|
1583
|
+
lines.push('Steps:');
|
|
1584
|
+
steps.forEach(function(step) {
|
|
1585
|
+
var keyword = step.getAttribute('data-keyword') || '';
|
|
1586
|
+
var text = step.getAttribute('data-text') || '';
|
|
1587
|
+
var stepStatusEl = step.querySelector('.step-status');
|
|
1588
|
+
var marker = ' ';
|
|
1589
|
+
if (stepStatusEl) {
|
|
1590
|
+
if (stepStatusEl.classList.contains('status-failed')) marker = 'x ';
|
|
1591
|
+
else if (stepStatusEl.classList.contains('status-passed')) marker = '+ ';
|
|
1592
|
+
else if (stepStatusEl.classList.contains('status-skipped')) marker = '- ';
|
|
1593
|
+
}
|
|
1594
|
+
lines.push(marker + keyword + ' ' + text);
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
var errorBox = scenario.querySelector('.error-message');
|
|
1598
|
+
if (errorBox) {
|
|
1599
|
+
lines.push('');
|
|
1600
|
+
lines.push('Error:');
|
|
1601
|
+
lines.push((errorBox.textContent || '').trim());
|
|
1602
|
+
}
|
|
1603
|
+
var stackBox = scenario.querySelector('.error-stack');
|
|
1604
|
+
if (stackBox) {
|
|
1605
|
+
lines.push('');
|
|
1606
|
+
lines.push('Stack:');
|
|
1607
|
+
lines.push((stackBox.textContent || '').trim());
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
lines.push('');
|
|
1611
|
+
lines.push('Please read the source file, identify the root cause, and propose a fix.');
|
|
1612
|
+
|
|
1613
|
+
var text = lines.join('\\n');
|
|
1614
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
1615
|
+
showCopyToast(scenario);
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1219
1619
|
// Hash scroll on load
|
|
1220
1620
|
function initHashScroll() {
|
|
1221
1621
|
if (!location.hash) return;
|
|
@@ -1385,6 +1785,7 @@ function generateScript(options) {
|
|
|
1385
1785
|
initCalls.push("initStatusFilter();");
|
|
1386
1786
|
initCalls.push("initKeyboardShortcuts();");
|
|
1387
1787
|
initCalls.push("initCollapse();");
|
|
1788
|
+
initCalls.push("restoreCollapseState();");
|
|
1388
1789
|
initCalls.push("initDetailLevel();");
|
|
1389
1790
|
initCalls.push("applyAllFilters();");
|
|
1390
1791
|
initCalls.push("initHashScroll();");
|
|
@@ -1489,6 +1890,106 @@ function escapeHtml(str) {
|
|
|
1489
1890
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1490
1891
|
}
|
|
1491
1892
|
|
|
1893
|
+
// src/theme/tokens.ts
|
|
1894
|
+
var ES_THEME_TOKENS_CSS = `
|
|
1895
|
+
:root,
|
|
1896
|
+
[data-theme="light"] {
|
|
1897
|
+
--es-color-bg: #ffffff;
|
|
1898
|
+
--es-color-fg: #111827;
|
|
1899
|
+
--es-color-muted: #6b7280;
|
|
1900
|
+
--es-color-border: #e5e7eb;
|
|
1901
|
+
--es-color-surface: #f9fafb;
|
|
1902
|
+
--es-color-link: #2563eb;
|
|
1903
|
+
--es-color-passed: #16a34a;
|
|
1904
|
+
--es-color-failed: #dc2626;
|
|
1905
|
+
--es-color-skipped: #9ca3af;
|
|
1906
|
+
--es-color-pending: #d97706;
|
|
1907
|
+
--es-color-passed-bg: #f0fdf4;
|
|
1908
|
+
--es-color-failed-bg: #fef2f2;
|
|
1909
|
+
--es-color-skipped-bg: #f3f4f6;
|
|
1910
|
+
--es-color-pending-bg: #fffbeb;
|
|
1911
|
+
--es-font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
1912
|
+
--es-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
|
|
1913
|
+
--es-size-base: 1rem;
|
|
1914
|
+
--es-size-sm: 0.875rem;
|
|
1915
|
+
--es-size-xs: 0.75rem;
|
|
1916
|
+
--es-size-h1: 1.875rem;
|
|
1917
|
+
--es-size-h2: 1.5rem;
|
|
1918
|
+
--es-size-h3: 1.25rem;
|
|
1919
|
+
--es-space-1: 0.25rem;
|
|
1920
|
+
--es-space-2: 0.5rem;
|
|
1921
|
+
--es-space-3: 0.75rem;
|
|
1922
|
+
--es-space-4: 1rem;
|
|
1923
|
+
--es-space-6: 1.5rem;
|
|
1924
|
+
--es-space-8: 2rem;
|
|
1925
|
+
--es-radius: 0.5rem;
|
|
1926
|
+
--es-line: 1.6;
|
|
1927
|
+
--es-measure: 72ch;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
@media (prefers-color-scheme: dark) {
|
|
1931
|
+
:root {
|
|
1932
|
+
--es-color-bg: #0b0f17;
|
|
1933
|
+
--es-color-fg: #e5e7eb;
|
|
1934
|
+
--es-color-muted: #9ca3af;
|
|
1935
|
+
--es-color-border: #1f2937;
|
|
1936
|
+
--es-color-surface: #111827;
|
|
1937
|
+
--es-color-link: #60a5fa;
|
|
1938
|
+
--es-color-passed: #4ade80;
|
|
1939
|
+
--es-color-failed: #f87171;
|
|
1940
|
+
--es-color-skipped: #6b7280;
|
|
1941
|
+
--es-color-pending: #fbbf24;
|
|
1942
|
+
--es-color-passed-bg: rgba(74, 222, 128, 0.08);
|
|
1943
|
+
--es-color-failed-bg: rgba(248, 113, 113, 0.08);
|
|
1944
|
+
--es-color-skipped-bg: rgba(107, 114, 128, 0.08);
|
|
1945
|
+
--es-color-pending-bg: rgba(251, 191, 36, 0.08);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
[data-theme="dark"] {
|
|
1950
|
+
--es-color-bg: #0b0f17;
|
|
1951
|
+
--es-color-fg: #e5e7eb;
|
|
1952
|
+
--es-color-muted: #9ca3af;
|
|
1953
|
+
--es-color-border: #1f2937;
|
|
1954
|
+
--es-color-surface: #111827;
|
|
1955
|
+
--es-color-link: #60a5fa;
|
|
1956
|
+
--es-color-passed: #4ade80;
|
|
1957
|
+
--es-color-failed: #f87171;
|
|
1958
|
+
--es-color-skipped: #6b7280;
|
|
1959
|
+
--es-color-pending: #fbbf24;
|
|
1960
|
+
--es-color-passed-bg: rgba(74, 222, 128, 0.08);
|
|
1961
|
+
--es-color-failed-bg: rgba(248, 113, 113, 0.08);
|
|
1962
|
+
--es-color-skipped-bg: rgba(107, 114, 128, 0.08);
|
|
1963
|
+
--es-color-pending-bg: rgba(251, 191, 36, 0.08);
|
|
1964
|
+
}
|
|
1965
|
+
`.trim();
|
|
1966
|
+
var ES_THEME_TOKEN_VALUES = {
|
|
1967
|
+
light: {
|
|
1968
|
+
"--es-color-bg": "#ffffff",
|
|
1969
|
+
"--es-color-fg": "#111827",
|
|
1970
|
+
"--es-color-muted": "#6b7280",
|
|
1971
|
+
"--es-color-border": "#e5e7eb",
|
|
1972
|
+
"--es-color-surface": "#f9fafb",
|
|
1973
|
+
"--es-color-link": "#2563eb",
|
|
1974
|
+
"--es-color-passed": "#16a34a",
|
|
1975
|
+
"--es-color-failed": "#dc2626",
|
|
1976
|
+
"--es-color-skipped": "#9ca3af",
|
|
1977
|
+
"--es-color-pending": "#d97706"
|
|
1978
|
+
},
|
|
1979
|
+
dark: {
|
|
1980
|
+
"--es-color-bg": "#0b0f17",
|
|
1981
|
+
"--es-color-fg": "#e5e7eb",
|
|
1982
|
+
"--es-color-muted": "#9ca3af",
|
|
1983
|
+
"--es-color-border": "#1f2937",
|
|
1984
|
+
"--es-color-surface": "#111827",
|
|
1985
|
+
"--es-color-link": "#60a5fa",
|
|
1986
|
+
"--es-color-passed": "#4ade80",
|
|
1987
|
+
"--es-color-failed": "#f87171",
|
|
1988
|
+
"--es-color-skipped": "#6b7280",
|
|
1989
|
+
"--es-color-pending": "#fbbf24"
|
|
1990
|
+
}
|
|
1991
|
+
};
|
|
1992
|
+
|
|
1492
1993
|
// src/formatters/html/styles.ts
|
|
1493
1994
|
var CSS_STYLES = `
|
|
1494
1995
|
/* ============================================================================
|
|
@@ -1496,6 +1997,14 @@ var CSS_STYLES = `
|
|
|
1496
1997
|
============================================================================ */
|
|
1497
1998
|
@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
1999
|
|
|
2000
|
+
/* ============================================================================
|
|
2001
|
+
executable-stories canonical tokens (--es-*).
|
|
2002
|
+
Shared with executable-stories-react. Override on :root or any ancestor of
|
|
2003
|
+
the report to re-color both the standalone HTML and the React component.
|
|
2004
|
+
============================================================================ */
|
|
2005
|
+
${ES_THEME_TOKENS_CSS}
|
|
2006
|
+
|
|
2007
|
+
|
|
1499
2008
|
/* ============================================================================
|
|
1500
2009
|
CSS Custom Properties - Light Mode (Default)
|
|
1501
2010
|
Cucumber-branded shadcn/ui base theme
|
|
@@ -3384,6 +3893,33 @@ body {
|
|
|
3384
3893
|
color: var(--primary);
|
|
3385
3894
|
}
|
|
3386
3895
|
|
|
3896
|
+
.copy-prompt-btn {
|
|
3897
|
+
display: inline-flex;
|
|
3898
|
+
align-items: center;
|
|
3899
|
+
justify-content: center;
|
|
3900
|
+
width: 1.5rem;
|
|
3901
|
+
height: 1.5rem;
|
|
3902
|
+
border: none;
|
|
3903
|
+
background: none;
|
|
3904
|
+
color: var(--muted-foreground);
|
|
3905
|
+
cursor: pointer;
|
|
3906
|
+
opacity: 0.6;
|
|
3907
|
+
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
3908
|
+
font-size: 0.95rem;
|
|
3909
|
+
padding: 0;
|
|
3910
|
+
flex-shrink: 0;
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3913
|
+
.scenario-header:hover .copy-prompt-btn,
|
|
3914
|
+
.copy-prompt-btn:focus-visible {
|
|
3915
|
+
opacity: 1;
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
.copy-prompt-btn:hover {
|
|
3919
|
+
color: var(--primary);
|
|
3920
|
+
transform: scale(1.15);
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3387
3923
|
/* ============================================================================
|
|
3388
3924
|
Keyboard Navigation
|
|
3389
3925
|
============================================================================ */
|
|
@@ -3621,6 +4157,82 @@ a.toc-title:hover {
|
|
|
3621
4157
|
outline-offset: 2px;
|
|
3622
4158
|
}
|
|
3623
4159
|
|
|
4160
|
+
/* ============================================================================
|
|
4161
|
+
Mobile responsive refinements
|
|
4162
|
+
============================================================================ */
|
|
4163
|
+
@media (max-width: 640px) {
|
|
4164
|
+
.container {
|
|
4165
|
+
padding: 0.875rem;
|
|
4166
|
+
}
|
|
4167
|
+
|
|
4168
|
+
.header {
|
|
4169
|
+
flex-direction: column;
|
|
4170
|
+
align-items: stretch;
|
|
4171
|
+
gap: 0.75rem;
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4174
|
+
.header-actions {
|
|
4175
|
+
flex-wrap: wrap;
|
|
4176
|
+
gap: 0.5rem;
|
|
4177
|
+
}
|
|
4178
|
+
|
|
4179
|
+
.search-input {
|
|
4180
|
+
width: 100%;
|
|
4181
|
+
flex: 1 1 100%;
|
|
4182
|
+
min-width: 0;
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4185
|
+
.header h1 {
|
|
4186
|
+
font-size: 1.25rem;
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
.scenario-header,
|
|
4190
|
+
.feature-header {
|
|
4191
|
+
flex-wrap: wrap;
|
|
4192
|
+
gap: 0.5rem;
|
|
4193
|
+
}
|
|
4194
|
+
|
|
4195
|
+
.scenario-meta {
|
|
4196
|
+
flex-wrap: wrap;
|
|
4197
|
+
}
|
|
4198
|
+
|
|
4199
|
+
.scenario-actions {
|
|
4200
|
+
flex-wrap: wrap;
|
|
4201
|
+
}
|
|
4202
|
+
|
|
4203
|
+
/* Always-visible action buttons on touch (no hover) */
|
|
4204
|
+
.copy-scenario-btn,
|
|
4205
|
+
.copy-prompt-btn,
|
|
4206
|
+
.permalink-anchor {
|
|
4207
|
+
opacity: 1 !important;
|
|
4208
|
+
}
|
|
4209
|
+
|
|
4210
|
+
.summary-card {
|
|
4211
|
+
padding: 0.75rem 0.875rem;
|
|
4212
|
+
}
|
|
4213
|
+
|
|
4214
|
+
.summary-card .value {
|
|
4215
|
+
font-size: 1.5rem;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
.tag-bar {
|
|
4219
|
+
overflow-x: auto;
|
|
4220
|
+
-webkit-overflow-scrolling: touch;
|
|
4221
|
+
}
|
|
4222
|
+
|
|
4223
|
+
.shortcuts-overlay {
|
|
4224
|
+
padding: 1rem;
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
@media (hover: none) and (pointer: coarse) {
|
|
4229
|
+
.copy-scenario-btn,
|
|
4230
|
+
.copy-prompt-btn,
|
|
4231
|
+
.permalink-anchor {
|
|
4232
|
+
opacity: 1;
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
|
|
3624
4236
|
`;
|
|
3625
4237
|
|
|
3626
4238
|
// src/formatters/html/themes/default.ts
|
|
@@ -13347,6 +13959,7 @@ function renderScenario(args, deps) {
|
|
|
13347
13959
|
</div>
|
|
13348
13960
|
<div class="scenario-actions">
|
|
13349
13961
|
<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>
|
|
13962
|
+
${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>` : ""}
|
|
13350
13963
|
<button class="permalink-anchor" onclick="copyPermalink('scenario-${tc.id}')" aria-label="Copy link to scenario" title="Copy link">#</button>
|
|
13351
13964
|
<span class="scenario-duration">${duration}</span>
|
|
13352
13965
|
</div>
|
|
@@ -13746,7 +14359,7 @@ var SCREENSHOT_MIME_BY_EXT = {
|
|
|
13746
14359
|
};
|
|
13747
14360
|
function readScreenshotAsDataUri(filePath) {
|
|
13748
14361
|
try {
|
|
13749
|
-
const ext =
|
|
14362
|
+
const ext = path3.extname(filePath).slice(1).toLowerCase();
|
|
13750
14363
|
const mime = SCREENSHOT_MIME_BY_EXT[ext];
|
|
13751
14364
|
if (!mime) return void 0;
|
|
13752
14365
|
if (!fs2.existsSync(filePath)) return void 0;
|
|
@@ -15223,8 +15836,8 @@ function extractDocAttachments(step) {
|
|
|
15223
15836
|
}
|
|
15224
15837
|
return attachments;
|
|
15225
15838
|
}
|
|
15226
|
-
function guessMediaType(
|
|
15227
|
-
const lower =
|
|
15839
|
+
function guessMediaType(path10) {
|
|
15840
|
+
const lower = path10.toLowerCase();
|
|
15228
15841
|
if (lower.endsWith(".png")) return "image/png";
|
|
15229
15842
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
|
15230
15843
|
if (lower.endsWith(".gif")) return "image/gif";
|
|
@@ -16269,7 +16882,7 @@ function selectTestCases(args, deps) {
|
|
|
16269
16882
|
|
|
16270
16883
|
// src/bundler/bundle-assets.ts
|
|
16271
16884
|
import * as fs4 from "fs";
|
|
16272
|
-
import * as
|
|
16885
|
+
import * as path5 from "path";
|
|
16273
16886
|
|
|
16274
16887
|
// src/bundler/scan-html-assets.ts
|
|
16275
16888
|
function scanHtmlAssets(html) {
|
|
@@ -16300,7 +16913,7 @@ function isLocalAssetRef(ref) {
|
|
|
16300
16913
|
|
|
16301
16914
|
// src/bundler/copy-asset.ts
|
|
16302
16915
|
import * as fs3 from "fs";
|
|
16303
|
-
import * as
|
|
16916
|
+
import * as path4 from "path";
|
|
16304
16917
|
import * as crypto from "crypto";
|
|
16305
16918
|
function copyAsset(sourcePath, assetsDir) {
|
|
16306
16919
|
if (!fs3.existsSync(assetsDir)) {
|
|
@@ -16308,10 +16921,10 @@ function copyAsset(sourcePath, assetsDir) {
|
|
|
16308
16921
|
}
|
|
16309
16922
|
const content = fs3.readFileSync(sourcePath);
|
|
16310
16923
|
const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 8);
|
|
16311
|
-
const ext =
|
|
16312
|
-
const baseName = sanitize(
|
|
16924
|
+
const ext = path4.extname(sourcePath);
|
|
16925
|
+
const baseName = sanitize(path4.basename(sourcePath, ext));
|
|
16313
16926
|
const destName = `${baseName}-${hash}${ext}`;
|
|
16314
|
-
const destPath =
|
|
16927
|
+
const destPath = path4.join(assetsDir, destName);
|
|
16315
16928
|
if (!fs3.existsSync(destPath)) {
|
|
16316
16929
|
fs3.copyFileSync(sourcePath, destPath);
|
|
16317
16930
|
}
|
|
@@ -16323,14 +16936,14 @@ function sanitize(name) {
|
|
|
16323
16936
|
|
|
16324
16937
|
// src/bundler/bundle-assets.ts
|
|
16325
16938
|
function bundleAssets(htmlPath, options = {}) {
|
|
16326
|
-
const htmlDir =
|
|
16327
|
-
const assetsDir =
|
|
16939
|
+
const htmlDir = path5.dirname(htmlPath);
|
|
16940
|
+
const assetsDir = path5.join(htmlDir, "assets");
|
|
16328
16941
|
let html = fs4.readFileSync(htmlPath, "utf8");
|
|
16329
16942
|
const refs = scanHtmlAssets(html);
|
|
16330
16943
|
let copiedCount = 0;
|
|
16331
16944
|
const missing = [];
|
|
16332
16945
|
for (const ref of refs) {
|
|
16333
|
-
const absolutePath =
|
|
16946
|
+
const absolutePath = path5.resolve(htmlDir, ref);
|
|
16334
16947
|
if (!fs4.existsSync(absolutePath)) {
|
|
16335
16948
|
missing.push(ref);
|
|
16336
16949
|
continue;
|
|
@@ -16828,14 +17441,14 @@ function groupBy7(items, keyFn) {
|
|
|
16828
17441
|
|
|
16829
17442
|
// src/formatters/astro-assets.ts
|
|
16830
17443
|
import * as fs5 from "fs";
|
|
16831
|
-
import * as
|
|
17444
|
+
import * as path6 from "path";
|
|
16832
17445
|
var SKIP_PREFIXES = ["http://", "https://", "data:", "#"];
|
|
16833
17446
|
function isLocalPath(src) {
|
|
16834
17447
|
const trimmed = src.trim();
|
|
16835
17448
|
if (SKIP_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
|
|
16836
17449
|
return false;
|
|
16837
17450
|
}
|
|
16838
|
-
return !
|
|
17451
|
+
return !path6.posix.isAbsolute(trimmed) && !path6.win32.isAbsolute(trimmed);
|
|
16839
17452
|
}
|
|
16840
17453
|
function stripCodeContent(markdown) {
|
|
16841
17454
|
let result = markdown.replace(/^[ \t]*(`{3,}|~{3,})[^\n]*\n[\s\S]*?^[ \t]*\1\s*$/gm, "");
|
|
@@ -16929,7 +17542,7 @@ function copyMarkdownAssets(options) {
|
|
|
16929
17542
|
const pathMap = /* @__PURE__ */ new Map();
|
|
16930
17543
|
const missing = [];
|
|
16931
17544
|
for (const ref of refs) {
|
|
16932
|
-
const absPath =
|
|
17545
|
+
const absPath = path6.resolve(markdownDir, ref);
|
|
16933
17546
|
if (!fs5.existsSync(absPath)) {
|
|
16934
17547
|
if (!allowMissing) {
|
|
16935
17548
|
throw new Error(`Asset not found: ${absPath}`);
|
|
@@ -17954,24 +18567,24 @@ function pickleStepArgumentToDocs(ps) {
|
|
|
17954
18567
|
|
|
17955
18568
|
// src/utils/git-info.ts
|
|
17956
18569
|
import * as fs6 from "fs";
|
|
17957
|
-
import * as
|
|
18570
|
+
import * as path7 from "path";
|
|
17958
18571
|
function readGitSha(cwd = process.cwd()) {
|
|
17959
18572
|
const envSha = process.env.GITHUB_SHA || process.env.GIT_COMMIT || process.env.CI_COMMIT_SHA;
|
|
17960
18573
|
if (envSha) return envSha;
|
|
17961
18574
|
const gitDir = findGitDir(cwd);
|
|
17962
18575
|
if (!gitDir) return void 0;
|
|
17963
18576
|
try {
|
|
17964
|
-
const headPath =
|
|
18577
|
+
const headPath = path7.join(gitDir, "HEAD");
|
|
17965
18578
|
const head = fs6.readFileSync(headPath, "utf8").trim();
|
|
17966
18579
|
if (!head.startsWith("ref:")) {
|
|
17967
18580
|
return head;
|
|
17968
18581
|
}
|
|
17969
18582
|
const refPath = head.replace("ref:", "").trim();
|
|
17970
|
-
const refFile =
|
|
18583
|
+
const refFile = path7.join(gitDir, refPath);
|
|
17971
18584
|
if (fs6.existsSync(refFile)) {
|
|
17972
18585
|
return fs6.readFileSync(refFile, "utf8").trim();
|
|
17973
18586
|
}
|
|
17974
|
-
const packedRefs =
|
|
18587
|
+
const packedRefs = path7.join(gitDir, "packed-refs");
|
|
17975
18588
|
if (fs6.existsSync(packedRefs)) {
|
|
17976
18589
|
const content = fs6.readFileSync(packedRefs, "utf8");
|
|
17977
18590
|
for (const line of content.split("\n")) {
|
|
@@ -17988,19 +18601,19 @@ function readGitSha(cwd = process.cwd()) {
|
|
|
17988
18601
|
function findGitDir(start) {
|
|
17989
18602
|
let current = start;
|
|
17990
18603
|
while (true) {
|
|
17991
|
-
const candidate =
|
|
18604
|
+
const candidate = path7.join(current, ".git");
|
|
17992
18605
|
if (fs6.existsSync(candidate)) {
|
|
17993
18606
|
const stat = fs6.statSync(candidate);
|
|
17994
18607
|
if (stat.isFile()) {
|
|
17995
18608
|
const content = fs6.readFileSync(candidate, "utf8").trim();
|
|
17996
18609
|
const match = content.match(/^gitdir: (.+)$/);
|
|
17997
18610
|
if (match) {
|
|
17998
|
-
return
|
|
18611
|
+
return path7.resolve(current, match[1]);
|
|
17999
18612
|
}
|
|
18000
18613
|
}
|
|
18001
18614
|
return candidate;
|
|
18002
18615
|
}
|
|
18003
|
-
const parent =
|
|
18616
|
+
const parent = path7.dirname(current);
|
|
18004
18617
|
if (parent === current) return void 0;
|
|
18005
18618
|
current = parent;
|
|
18006
18619
|
}
|
|
@@ -18011,7 +18624,7 @@ function readBranchName(cwd = process.cwd()) {
|
|
|
18011
18624
|
const gitDir = findGitDir(cwd);
|
|
18012
18625
|
if (!gitDir) return void 0;
|
|
18013
18626
|
try {
|
|
18014
|
-
const headPath =
|
|
18627
|
+
const headPath = path7.join(gitDir, "HEAD");
|
|
18015
18628
|
const head = fs6.readFileSync(headPath, "utf8").trim();
|
|
18016
18629
|
if (head.startsWith("ref:")) {
|
|
18017
18630
|
const refPath = head.replace("ref:", "").trim();
|
|
@@ -18050,7 +18663,7 @@ function nanosecondsToMs(ns) {
|
|
|
18050
18663
|
|
|
18051
18664
|
// src/utils/metadata.ts
|
|
18052
18665
|
import * as fs7 from "fs";
|
|
18053
|
-
import * as
|
|
18666
|
+
import * as path8 from "path";
|
|
18054
18667
|
var versionCache = /* @__PURE__ */ new Map();
|
|
18055
18668
|
function readPackageVersion(root) {
|
|
18056
18669
|
if (versionCache.has(root)) {
|
|
@@ -18061,9 +18674,9 @@ function readPackageVersion(root) {
|
|
|
18061
18674
|
return version;
|
|
18062
18675
|
}
|
|
18063
18676
|
function findPackageVersion(startDir) {
|
|
18064
|
-
let current =
|
|
18677
|
+
let current = path8.resolve(startDir);
|
|
18065
18678
|
while (true) {
|
|
18066
|
-
const pkgPath =
|
|
18679
|
+
const pkgPath = path8.join(current, "package.json");
|
|
18067
18680
|
try {
|
|
18068
18681
|
if (fs7.existsSync(pkgPath)) {
|
|
18069
18682
|
const raw = fs7.readFileSync(pkgPath, "utf8");
|
|
@@ -18072,7 +18685,7 @@ function findPackageVersion(startDir) {
|
|
|
18072
18685
|
}
|
|
18073
18686
|
} catch {
|
|
18074
18687
|
}
|
|
18075
|
-
const parent =
|
|
18688
|
+
const parent = path8.dirname(current);
|
|
18076
18689
|
if (parent === current) {
|
|
18077
18690
|
return void 0;
|
|
18078
18691
|
}
|
|
@@ -19030,7 +19643,8 @@ var FORMAT_EXTENSIONS = {
|
|
|
19030
19643
|
junit: ".junit.xml",
|
|
19031
19644
|
"cucumber-json": ".cucumber.json",
|
|
19032
19645
|
"cucumber-messages": ".ndjson",
|
|
19033
|
-
confluence: ".adf.json"
|
|
19646
|
+
confluence: ".adf.json",
|
|
19647
|
+
"story-report-json": ".story-report.json"
|
|
19034
19648
|
};
|
|
19035
19649
|
var TEST_EXTENSIONS = [
|
|
19036
19650
|
".test.ts",
|
|
@@ -19057,11 +19671,11 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
19057
19671
|
const ext = FORMAT_EXTENSIONS[format];
|
|
19058
19672
|
const effectiveName = outputName + (outputNameSuffix ?? "");
|
|
19059
19673
|
if (mode === "aggregated") {
|
|
19060
|
-
return toPosix(
|
|
19674
|
+
return toPosix(path9.join(baseOutputDir, `${effectiveName}${ext}`));
|
|
19061
19675
|
}
|
|
19062
19676
|
const normalizedSource = toPosix(sourceFile);
|
|
19063
|
-
const dirOfSource =
|
|
19064
|
-
let baseName =
|
|
19677
|
+
const dirOfSource = path9.posix.dirname(normalizedSource);
|
|
19678
|
+
let baseName = path9.posix.basename(normalizedSource);
|
|
19065
19679
|
for (const testExt of TEST_EXTENSIONS) {
|
|
19066
19680
|
if (baseName.endsWith(testExt)) {
|
|
19067
19681
|
baseName = baseName.slice(0, -testExt.length);
|
|
@@ -19070,9 +19684,9 @@ function computeOutputPath(sourceFile, format, mode, colocatedStyle, baseOutputD
|
|
|
19070
19684
|
}
|
|
19071
19685
|
const fileName = `${baseName}.${effectiveName}${ext}`;
|
|
19072
19686
|
if (colocatedStyle === "adjacent") {
|
|
19073
|
-
return toPosix(
|
|
19687
|
+
return toPosix(path9.posix.join(dirOfSource, fileName));
|
|
19074
19688
|
}
|
|
19075
|
-
return toPosix(
|
|
19689
|
+
return toPosix(path9.posix.join(baseOutputDir, dirOfSource, fileName));
|
|
19076
19690
|
}
|
|
19077
19691
|
function groupTestCasesByOutput(testCases, format, options, logger, outputNameSuffix) {
|
|
19078
19692
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -19141,7 +19755,7 @@ var ReportGenerator = class {
|
|
|
19141
19755
|
exclude: options.exclude ?? [],
|
|
19142
19756
|
includeTags: options.includeTags ?? [],
|
|
19143
19757
|
excludeTags: options.excludeTags ?? [],
|
|
19144
|
-
formats: options.formats ?? ["
|
|
19758
|
+
formats: options.formats ?? ["html"],
|
|
19145
19759
|
outputDir: options.outputDir ?? "reports",
|
|
19146
19760
|
outputName: options.outputName ?? "index",
|
|
19147
19761
|
outputNameTimestamp: options.outputNameTimestamp ?? false,
|
|
@@ -19155,6 +19769,9 @@ var ReportGenerator = class {
|
|
|
19155
19769
|
cucumberJson: {
|
|
19156
19770
|
pretty: options.cucumberJson?.pretty ?? false
|
|
19157
19771
|
},
|
|
19772
|
+
storyReportJson: {
|
|
19773
|
+
pretty: options.storyReportJson?.pretty ?? true
|
|
19774
|
+
},
|
|
19158
19775
|
cucumberMessages: {
|
|
19159
19776
|
uriStrategy: options.cucumberMessages?.uriStrategy ?? "sourceFile",
|
|
19160
19777
|
includeSynthetics: options.cucumberMessages?.includeSynthetics ?? true,
|
|
@@ -19270,8 +19887,8 @@ var ReportGenerator = class {
|
|
|
19270
19887
|
if (astroPaths) {
|
|
19271
19888
|
for (const mdPath of astroPaths) {
|
|
19272
19889
|
const content = await fsPromises.readFile(mdPath, "utf8");
|
|
19273
|
-
const mdDir =
|
|
19274
|
-
const assetsDir =
|
|
19890
|
+
const mdDir = path9.dirname(mdPath);
|
|
19891
|
+
const assetsDir = path9.resolve(this.options.astro.assetsDir);
|
|
19275
19892
|
const result = copyMarkdownAssets({
|
|
19276
19893
|
markdown: content,
|
|
19277
19894
|
markdownDir: mdDir,
|
|
@@ -19302,9 +19919,9 @@ var ReportGenerator = class {
|
|
|
19302
19919
|
if (groups.size === 0 && this.options.output.mode === "aggregated") {
|
|
19303
19920
|
const ext = FORMAT_EXTENSIONS[format];
|
|
19304
19921
|
const effectiveName = this.options.outputName + (outputNameSuffix ?? "");
|
|
19305
|
-
const outputPath = toPosix(
|
|
19922
|
+
const outputPath = toPosix(path9.join(this.options.outputDir, `${effectiveName}${ext}`));
|
|
19306
19923
|
const content = await this.formatContent(run, format);
|
|
19307
|
-
const dir =
|
|
19924
|
+
const dir = path9.dirname(outputPath);
|
|
19308
19925
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
19309
19926
|
await this.deps.writeFile(outputPath, content);
|
|
19310
19927
|
return [outputPath];
|
|
@@ -19316,7 +19933,7 @@ var ReportGenerator = class {
|
|
|
19316
19933
|
testCases
|
|
19317
19934
|
};
|
|
19318
19935
|
const content = await this.formatContent(groupRun, format);
|
|
19319
|
-
const dir =
|
|
19936
|
+
const dir = path9.dirname(outputPath);
|
|
19320
19937
|
await fsPromises.mkdir(dir, { recursive: true });
|
|
19321
19938
|
await this.deps.writeFile(outputPath, content);
|
|
19322
19939
|
writtenPaths.push(outputPath);
|
|
@@ -19423,6 +20040,12 @@ var ReportGenerator = class {
|
|
|
19423
20040
|
});
|
|
19424
20041
|
return formatter.format(run);
|
|
19425
20042
|
}
|
|
20043
|
+
case "story-report-json": {
|
|
20044
|
+
const formatter = new StoryReportJsonFormatter({
|
|
20045
|
+
pretty: this.options.storyReportJson.pretty
|
|
20046
|
+
});
|
|
20047
|
+
return formatter.format(run);
|
|
20048
|
+
}
|
|
19426
20049
|
default:
|
|
19427
20050
|
throw new Error(`Unknown format: ${format}`);
|
|
19428
20051
|
}
|
|
@@ -19439,7 +20062,7 @@ async function generateRunComparison(args) {
|
|
|
19439
20062
|
await fsPromises.mkdir(outputDir, { recursive: true });
|
|
19440
20063
|
for (const format of args.formats) {
|
|
19441
20064
|
const ext = format === "html" ? ".html" : ".md";
|
|
19442
|
-
const outputPath = toPosix(
|
|
20065
|
+
const outputPath = toPosix(path9.join(outputDir, `${outputName}${ext}`));
|
|
19443
20066
|
const content = format === "html" ? new RunDiffHtmlFormatter({ title: args.title }).format(diff) : new RunDiffMarkdownFormatter({ title: args.title }).format(diff);
|
|
19444
20067
|
await fsPromises.writeFile(outputPath, content, "utf8");
|
|
19445
20068
|
files.push(outputPath);
|
|
@@ -19464,6 +20087,8 @@ export {
|
|
|
19464
20087
|
CucumberHtmlFormatter,
|
|
19465
20088
|
CucumberJsonFormatter,
|
|
19466
20089
|
CucumberMessagesFormatter,
|
|
20090
|
+
ES_THEME_TOKENS_CSS,
|
|
20091
|
+
ES_THEME_TOKEN_VALUES,
|
|
19467
20092
|
HtmlFormatter,
|
|
19468
20093
|
JUnitFormatter,
|
|
19469
20094
|
MIN_FLAKINESS_SAMPLES,
|
|
@@ -19474,6 +20099,9 @@ export {
|
|
|
19474
20099
|
RunDiffHtmlFormatter,
|
|
19475
20100
|
RunDiffMarkdownFormatter,
|
|
19476
20101
|
STORY_META_KEY,
|
|
20102
|
+
STORY_REPORT_SCHEMA_MAJOR,
|
|
20103
|
+
STORY_REPORT_SCHEMA_VERSION,
|
|
20104
|
+
StoryReportJsonFormatter,
|
|
19477
20105
|
adaptJestRun,
|
|
19478
20106
|
adaptPlaywrightRun,
|
|
19479
20107
|
adaptVitestRun,
|
|
@@ -19530,6 +20158,7 @@ export {
|
|
|
19530
20158
|
stripAnsi,
|
|
19531
20159
|
toCIInfo,
|
|
19532
20160
|
toRawCIInfo,
|
|
20161
|
+
toStoryReport,
|
|
19533
20162
|
tryGetActiveOtelContext,
|
|
19534
20163
|
updateHistory,
|
|
19535
20164
|
validateCanonicalRun
|