executable-stories-react 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +216 -270
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -235
- package/dist/index.d.ts +6 -235
- package/dist/index.js +208 -262
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +266 -476
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +1 -233
- package/dist/interactive.d.ts +1 -233
- package/dist/interactive.js +258 -468
- package/dist/interactive.js.map +1 -1
- package/dist/parse.cjs +40 -1
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +3 -234
- package/dist/parse.d.ts +3 -234
- package/dist/parse.js +40 -1
- package/dist/parse.js.map +1 -1
- package/dist/report-island.global.js +23 -21
- package/dist/ssr.cjs +143 -236
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.d.cts +1 -232
- package/dist/ssr.d.ts +1 -232
- package/dist/ssr.js +135 -228
- package/dist/ssr.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -377,23 +377,11 @@ function useReport() {
|
|
|
377
377
|
return ctx.report;
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if (ms < 6e4) {
|
|
386
|
-
return `${(ms / 1e3).toFixed(2)} s`;
|
|
387
|
-
}
|
|
388
|
-
const minutes = Math.floor(ms / 6e4);
|
|
389
|
-
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
390
|
-
if (minutes < 60) {
|
|
391
|
-
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
392
|
-
}
|
|
393
|
-
const hours = Math.floor(minutes / 60);
|
|
394
|
-
const remainingMinutes = minutes % 60;
|
|
395
|
-
return `${hours}h ${remainingMinutes}m`;
|
|
396
|
-
}
|
|
380
|
+
// src/components/ReportScenario.tsx
|
|
381
|
+
var import_duration3 = require("executable-stories-core/utils/duration");
|
|
382
|
+
|
|
383
|
+
// src/components/ReportSteps.tsx
|
|
384
|
+
var import_duration = require("executable-stories-core/utils/duration");
|
|
397
385
|
|
|
398
386
|
// src/components/doc/DocNote.tsx
|
|
399
387
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -482,36 +470,29 @@ function DocLink({ entry }) {
|
|
|
482
470
|
);
|
|
483
471
|
}
|
|
484
472
|
|
|
485
|
-
// src/
|
|
473
|
+
// src/lib/markdown.ts
|
|
486
474
|
var import_marked = require("marked");
|
|
487
475
|
|
|
488
|
-
//
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
return
|
|
476
|
+
// src/lib/url.ts
|
|
477
|
+
var import_url = require("executable-stories-core/utils/url");
|
|
478
|
+
|
|
479
|
+
// src/lib/markdown.ts
|
|
480
|
+
function safeMarkdownHtml(markdown) {
|
|
481
|
+
const raw = import_marked.marked.parse(markdown, { async: false });
|
|
482
|
+
return raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<\/?(?:iframe|object|embed|form|base)\b[^>]*>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, "").replace(/(href|src)\s*=\s*"([^"]*)"/gi, (_m, attr, val) => `${attr}="${neutralizeUrl(val)}"`).replace(/(href|src)\s*=\s*'([^']*)'/gi, (_m, attr, val) => `${attr}='${neutralizeUrl(val)}'`);
|
|
495
483
|
}
|
|
496
|
-
function
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
return
|
|
484
|
+
function dedent(text) {
|
|
485
|
+
const lines = text.replace(/\t/g, " ").split("\n");
|
|
486
|
+
const indents = lines.filter((line) => line.trim().length > 0).map((line) => line.length - line.trimStart().length);
|
|
487
|
+
const shortest = indents.length > 0 ? Math.min(...indents) : 0;
|
|
488
|
+
return lines.map((line) => line.slice(shortest)).join("\n").trim();
|
|
501
489
|
}
|
|
502
|
-
function
|
|
503
|
-
return
|
|
490
|
+
function neutralizeUrl(value) {
|
|
491
|
+
return (0, import_url.safeUrl)(value) ?? "#";
|
|
504
492
|
}
|
|
505
493
|
|
|
506
494
|
// src/components/doc/DocSection.tsx
|
|
507
495
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
508
|
-
function neutralizeUrl(value) {
|
|
509
|
-
return safeUrl(value) ?? "#";
|
|
510
|
-
}
|
|
511
|
-
function safeMarkdownHtml(markdown) {
|
|
512
|
-
const raw = import_marked.marked.parse(markdown, { async: false });
|
|
513
|
-
return raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<\/?(?:iframe|object|embed|form|base)\b[^>]*>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, "").replace(/(href|src)\s*=\s*"([^"]*)"/gi, (_m, attr, val) => `${attr}="${neutralizeUrl(val)}"`).replace(/(href|src)\s*=\s*'([^']*)'/gi, (_m, attr, val) => `${attr}='${neutralizeUrl(val)}'`);
|
|
514
|
-
}
|
|
515
496
|
function DocSection({ entry }) {
|
|
516
497
|
const renderers = useBuiltinRenderers();
|
|
517
498
|
if (renderers.section) {
|
|
@@ -557,7 +538,7 @@ function DocMermaid({ entry }) {
|
|
|
557
538
|
// src/components/doc/DocScreenshot.tsx
|
|
558
539
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
559
540
|
function DocScreenshot({ entry }) {
|
|
560
|
-
const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
|
|
541
|
+
const src = (0, import_url.isLocalFsPath)(entry.path) ? void 0 : (0, import_url.safeImageUrl)(entry.path);
|
|
561
542
|
if (!src) {
|
|
562
543
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
563
544
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
|
|
@@ -582,15 +563,15 @@ function DocScreenshot({ entry }) {
|
|
|
582
563
|
// src/components/doc/DocVideo.tsx
|
|
583
564
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
584
565
|
function DocVideo({ entry }) {
|
|
585
|
-
if (isLocalFsPath(entry.path)) {
|
|
566
|
+
if ((0, import_url.isLocalFsPath)(entry.path)) {
|
|
586
567
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
587
568
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
|
|
588
569
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
589
570
|
entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
|
|
590
571
|
] });
|
|
591
572
|
}
|
|
592
|
-
const src = safeUrl(entry.path);
|
|
593
|
-
const poster = safeUrl(entry.poster);
|
|
573
|
+
const src = (0, import_url.safeUrl)(entry.path);
|
|
574
|
+
const poster = (0, import_url.safeUrl)(entry.poster);
|
|
594
575
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
|
|
595
576
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
596
577
|
"video",
|
|
@@ -611,7 +592,7 @@ var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
|
611
592
|
function DocHtml({ entry }) {
|
|
612
593
|
const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
|
|
613
594
|
const title = entry.title ?? "Embedded HTML";
|
|
614
|
-
const safeHref = safeUrl(entry.url ?? entry.path);
|
|
595
|
+
const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
|
|
615
596
|
const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
|
|
616
597
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
|
|
617
598
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
|
|
@@ -778,7 +759,7 @@ function ReportStepItem({ step }) {
|
|
|
778
759
|
}
|
|
779
760
|
),
|
|
780
761
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
781
|
-
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
|
|
762
|
+
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration.formatDuration)(step.durationMs) }) : null
|
|
782
763
|
] }),
|
|
783
764
|
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
784
765
|
"pre",
|
|
@@ -794,125 +775,10 @@ function ReportStepItem({ step }) {
|
|
|
794
775
|
);
|
|
795
776
|
}
|
|
796
777
|
|
|
797
|
-
// ../executable-stories-core/dist/state-diff.js
|
|
798
|
-
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
799
|
-
function deepEqual(a, b) {
|
|
800
|
-
if (Object.is(a, b)) return true;
|
|
801
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
802
|
-
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
803
|
-
}
|
|
804
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
805
|
-
const ak = Object.keys(a);
|
|
806
|
-
const bk = Object.keys(b);
|
|
807
|
-
return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
|
|
808
|
-
}
|
|
809
|
-
return false;
|
|
810
|
-
}
|
|
811
|
-
var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
|
|
812
|
-
function walk(path, before, after, out) {
|
|
813
|
-
if (Object.is(before, after)) return;
|
|
814
|
-
if (Array.isArray(before) && Array.isArray(after)) {
|
|
815
|
-
const shared = Math.min(before.length, after.length);
|
|
816
|
-
for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
|
|
817
|
-
for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
|
|
818
|
-
for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
|
|
819
|
-
return;
|
|
820
|
-
}
|
|
821
|
-
if (isPlainObject(before) && isPlainObject(after)) {
|
|
822
|
-
for (const key of Object.keys(after)) {
|
|
823
|
-
const p = joinKey(path, key);
|
|
824
|
-
if (key in before) walk(p, before[key], after[key], out);
|
|
825
|
-
else out.push({ path: p, kind: "added", after: after[key] });
|
|
826
|
-
}
|
|
827
|
-
for (const key of Object.keys(before)) {
|
|
828
|
-
if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
|
|
829
|
-
}
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
|
|
833
|
-
}
|
|
834
|
-
function diffStateValues(before, after) {
|
|
835
|
-
const out = [];
|
|
836
|
-
walk("", before, after, out);
|
|
837
|
-
return out;
|
|
838
|
-
}
|
|
839
|
-
function formatStateValue(value, maxLen = 60) {
|
|
840
|
-
const s = value === void 0 ? "undefined" : JSON.stringify(value);
|
|
841
|
-
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
842
|
-
}
|
|
843
|
-
function summarizeStateChanges(changes) {
|
|
844
|
-
return changes.map((c) => {
|
|
845
|
-
const at = c.path === "" ? "(root)" : c.path;
|
|
846
|
-
switch (c.kind) {
|
|
847
|
-
case "added":
|
|
848
|
-
return `+ ${at}: ${formatStateValue(c.after)}`;
|
|
849
|
-
case "removed":
|
|
850
|
-
return `\u2212 ${at}`;
|
|
851
|
-
case "changed":
|
|
852
|
-
return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
|
|
853
|
-
}
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// ../executable-stories-core/dist/storyboard.js
|
|
858
|
-
function findScreenshot(entries) {
|
|
859
|
-
for (const entry of entries) {
|
|
860
|
-
if (entry.kind === "screenshot") return entry;
|
|
861
|
-
if (entry.children) {
|
|
862
|
-
const nested = findScreenshot(entry.children);
|
|
863
|
-
if (nested) return nested;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
return void 0;
|
|
867
|
-
}
|
|
868
|
-
function findStates(entries, out = []) {
|
|
869
|
-
for (const entry of entries) {
|
|
870
|
-
if (entry.kind === "state") out.push(entry);
|
|
871
|
-
if (entry.children) findStates(entry.children, out);
|
|
872
|
-
}
|
|
873
|
-
return out;
|
|
874
|
-
}
|
|
875
|
-
var laneOf = (label) => label ?? "";
|
|
876
|
-
function extractStoryboardFrames(scenario) {
|
|
877
|
-
const frames = [];
|
|
878
|
-
const laneOrder = [];
|
|
879
|
-
const previous = /* @__PURE__ */ new Map();
|
|
880
|
-
for (const step of scenario.steps) {
|
|
881
|
-
const shot = findScreenshot(step.docEntries);
|
|
882
|
-
const stateDocs = findStates(step.docEntries);
|
|
883
|
-
if (!shot && stateDocs.length === 0) continue;
|
|
884
|
-
const byLane = /* @__PURE__ */ new Map();
|
|
885
|
-
for (const doc of stateDocs) {
|
|
886
|
-
const lane = laneOf(doc.label);
|
|
887
|
-
if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
|
|
888
|
-
byLane.set(lane, doc);
|
|
889
|
-
}
|
|
890
|
-
const states = [];
|
|
891
|
-
for (const lane of laneOrder) {
|
|
892
|
-
const doc = byLane.get(lane);
|
|
893
|
-
if (!doc) continue;
|
|
894
|
-
const card = {
|
|
895
|
-
...doc.label !== void 0 && { label: doc.label },
|
|
896
|
-
value: doc.value
|
|
897
|
-
};
|
|
898
|
-
if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
|
|
899
|
-
previous.set(lane, doc.value);
|
|
900
|
-
states.push(card);
|
|
901
|
-
}
|
|
902
|
-
frames.push({
|
|
903
|
-
stepId: step.id,
|
|
904
|
-
stepIndex: step.index,
|
|
905
|
-
keyword: step.keyword,
|
|
906
|
-
text: step.text,
|
|
907
|
-
status: step.status,
|
|
908
|
-
...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
|
|
909
|
-
states
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
return frames;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
778
|
// src/components/ReportStoryboard.tsx
|
|
779
|
+
var import_storyboard = require("executable-stories-core/storyboard");
|
|
780
|
+
var import_state_diff = require("executable-stories-core/state-diff");
|
|
781
|
+
var import_url6 = require("executable-stories-core/utils/url");
|
|
916
782
|
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
917
783
|
var FRAME_GLYPH = {
|
|
918
784
|
passed: "\u2713",
|
|
@@ -927,7 +793,7 @@ var FRAME_GLYPH_COLOR = {
|
|
|
927
793
|
pending: "text-pend"
|
|
928
794
|
};
|
|
929
795
|
function ReportStoryboard({ scenario }) {
|
|
930
|
-
const frames = extractStoryboardFrames(scenario);
|
|
796
|
+
const frames = (0, import_storyboard.extractStoryboardFrames)(scenario);
|
|
931
797
|
if (frames.length < 2) return null;
|
|
932
798
|
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
|
|
933
799
|
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
|
|
@@ -961,7 +827,7 @@ function StoryboardFrameItem({ frame }) {
|
|
|
961
827
|
}
|
|
962
828
|
function FrameScreenshot({ frame, label }) {
|
|
963
829
|
const path = frame.screenshot?.path ?? "";
|
|
964
|
-
const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
|
|
830
|
+
const src = (0, import_url6.isLocalFsPath)(path) ? void 0 : (0, import_url6.safeImageUrl)(path);
|
|
965
831
|
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
966
832
|
"a",
|
|
967
833
|
{
|
|
@@ -1007,7 +873,7 @@ function StateCardView({ card }) {
|
|
|
1007
873
|
"rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
|
|
1008
874
|
CHANGE_TONE[change.kind]
|
|
1009
875
|
),
|
|
1010
|
-
children: summarizeStateChanges([change])[0]
|
|
876
|
+
children: (0, import_state_diff.summarizeStateChanges)([change])[0]
|
|
1011
877
|
},
|
|
1012
878
|
i
|
|
1013
879
|
)) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
|
|
@@ -1067,6 +933,9 @@ function ReportAttachments({ attachments }) {
|
|
|
1067
933
|
);
|
|
1068
934
|
}
|
|
1069
935
|
|
|
936
|
+
// src/components/ReportTrace.tsx
|
|
937
|
+
var import_duration2 = require("executable-stories-core/utils/duration");
|
|
938
|
+
|
|
1070
939
|
// src/lib/trace.ts
|
|
1071
940
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
1072
941
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -1119,15 +988,15 @@ function toTraceRows(spans) {
|
|
|
1119
988
|
roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
1120
989
|
const rows = [];
|
|
1121
990
|
const seen = /* @__PURE__ */ new Set();
|
|
1122
|
-
const
|
|
991
|
+
const walk = (node, depth) => {
|
|
1123
992
|
if (seen.has(node.span.spanId)) return;
|
|
1124
993
|
seen.add(node.span.spanId);
|
|
1125
994
|
rows.push({ ...node.span, depth });
|
|
1126
|
-
for (const child of node.children)
|
|
995
|
+
for (const child of node.children) walk(child, depth + 1);
|
|
1127
996
|
};
|
|
1128
|
-
for (const root of roots)
|
|
997
|
+
for (const root of roots) walk(root, 0);
|
|
1129
998
|
for (const node of byId.values()) {
|
|
1130
|
-
if (!seen.has(node.span.spanId))
|
|
999
|
+
if (!seen.has(node.span.spanId)) walk(node, 0);
|
|
1131
1000
|
}
|
|
1132
1001
|
return rows;
|
|
1133
1002
|
}
|
|
@@ -1161,7 +1030,7 @@ function ReportTrace({ spans }) {
|
|
|
1161
1030
|
" span",
|
|
1162
1031
|
rows.length === 1 ? "" : "s",
|
|
1163
1032
|
", ",
|
|
1164
|
-
formatDuration(totalMs),
|
|
1033
|
+
(0, import_duration2.formatDuration)(totalMs),
|
|
1165
1034
|
")"
|
|
1166
1035
|
] }),
|
|
1167
1036
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
|
|
@@ -1184,7 +1053,7 @@ function ReportTrace({ spans }) {
|
|
|
1184
1053
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
1185
1054
|
}
|
|
1186
1055
|
) }),
|
|
1187
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
|
|
1056
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: (0, import_duration2.formatDuration)(row.durationMs) })
|
|
1188
1057
|
] }, `${row.spanId}-${i}`);
|
|
1189
1058
|
}) })
|
|
1190
1059
|
] });
|
|
@@ -1295,6 +1164,7 @@ function useCollapse() {
|
|
|
1295
1164
|
|
|
1296
1165
|
// src/interactive/scenario-actions.tsx
|
|
1297
1166
|
var import_react7 = require("react");
|
|
1167
|
+
var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
|
|
1298
1168
|
var ScenarioActionsContext = (0, import_react7.createContext)(null);
|
|
1299
1169
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
1300
1170
|
function useScenarioActions() {
|
|
@@ -1440,7 +1310,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1440
1310
|
] }),
|
|
1441
1311
|
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1442
1312
|
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1443
|
-
scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
|
|
1313
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration3.formatDuration)(scenario.durationMs) }) : null,
|
|
1444
1314
|
meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
|
|
1445
1315
|
actions ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenu, { children: [
|
|
1446
1316
|
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
@@ -1499,8 +1369,35 @@ function ReportScenarioList({ feature }) {
|
|
|
1499
1369
|
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportScenario, { scenario }, scenario.id)) });
|
|
1500
1370
|
}
|
|
1501
1371
|
|
|
1502
|
-
// src/components/
|
|
1372
|
+
// src/components/ReportFeatureIntro.tsx
|
|
1503
1373
|
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1374
|
+
function ReportFeatureIntro({ feature }) {
|
|
1375
|
+
const hasNarrative = Boolean(feature.narrative?.trim());
|
|
1376
|
+
const hasGlossary = Boolean(feature.glossary?.length);
|
|
1377
|
+
if (!hasNarrative && !hasGlossary) return null;
|
|
1378
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
|
|
1379
|
+
hasNarrative ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1380
|
+
"div",
|
|
1381
|
+
{
|
|
1382
|
+
className: "es-doc-prose prose prose-sm max-w-none text-muted-foreground",
|
|
1383
|
+
dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
|
|
1384
|
+
}
|
|
1385
|
+
) : null,
|
|
1386
|
+
hasGlossary ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
|
|
1387
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dt", { className: "font-medium text-foreground", children: entry.term }),
|
|
1388
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
|
|
1389
|
+
] }, entry.term)) }) : null
|
|
1390
|
+
] });
|
|
1391
|
+
}
|
|
1392
|
+
function featureKindLabel(kind) {
|
|
1393
|
+
if (kind === "ability") return "Ability";
|
|
1394
|
+
if (kind === "business-need") return "Business need";
|
|
1395
|
+
if (kind === "feature") return "Feature";
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
// src/components/ReportFeature.tsx
|
|
1400
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1504
1401
|
function ReportFeature({ feature }) {
|
|
1505
1402
|
const titleId = `${feature.id}-title`;
|
|
1506
1403
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1508,10 +1405,11 @@ function ReportFeature({ feature }) {
|
|
|
1508
1405
|
const skipped = s.skipped + s.pending;
|
|
1509
1406
|
const collapse = useCollapse();
|
|
1510
1407
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
1408
|
+
const kindLabel = featureKindLabel(feature.kind);
|
|
1511
1409
|
return (
|
|
1512
1410
|
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1513
1411
|
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1514
|
-
/* @__PURE__ */ (0,
|
|
1412
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1515
1413
|
"section",
|
|
1516
1414
|
{
|
|
1517
1415
|
id: feature.id,
|
|
@@ -1519,9 +1417,9 @@ function ReportFeature({ feature }) {
|
|
|
1519
1417
|
"aria-labelledby": titleId,
|
|
1520
1418
|
className: "flex flex-col gap-3",
|
|
1521
1419
|
children: [
|
|
1522
|
-
/* @__PURE__ */ (0,
|
|
1523
|
-
/* @__PURE__ */ (0,
|
|
1524
|
-
/* @__PURE__ */ (0,
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "min-w-0", children: [
|
|
1422
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1525
1423
|
"button",
|
|
1526
1424
|
{
|
|
1527
1425
|
type: "button",
|
|
@@ -1530,31 +1428,34 @@ function ReportFeature({ feature }) {
|
|
|
1530
1428
|
"aria-controls": bodyId,
|
|
1531
1429
|
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1532
1430
|
children: [
|
|
1533
|
-
/* @__PURE__ */ (0,
|
|
1534
|
-
/* @__PURE__ */ (0,
|
|
1431
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate", children: feature.title })
|
|
1535
1433
|
]
|
|
1536
1434
|
}
|
|
1537
1435
|
) : feature.title }),
|
|
1538
|
-
/* @__PURE__ */ (0,
|
|
1436
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { className: cn("mt-0.5 flex items-center gap-2 text-xs text-muted-foreground", collapse && "pl-5"), children: [
|
|
1437
|
+
kindLabel ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-medium text-foreground", children: kindLabel }) : null,
|
|
1438
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate font-mono", children: feature.sourceFile })
|
|
1439
|
+
] })
|
|
1539
1440
|
] }),
|
|
1540
|
-
/* @__PURE__ */ (0,
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1541
1442
|
"p",
|
|
1542
1443
|
{
|
|
1543
1444
|
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1544
1445
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1545
1446
|
children: [
|
|
1546
|
-
/* @__PURE__ */ (0,
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { children: [
|
|
1547
1448
|
s.passed,
|
|
1548
1449
|
" passed"
|
|
1549
1450
|
] }),
|
|
1550
|
-
/* @__PURE__ */ (0,
|
|
1551
|
-
/* @__PURE__ */ (0,
|
|
1451
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1452
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
|
|
1552
1453
|
s.failed,
|
|
1553
1454
|
" failed"
|
|
1554
1455
|
] }),
|
|
1555
|
-
skipped > 0 ? /* @__PURE__ */ (0,
|
|
1556
|
-
/* @__PURE__ */ (0,
|
|
1557
|
-
/* @__PURE__ */ (0,
|
|
1456
|
+
skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1457
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { children: [
|
|
1558
1459
|
skipped,
|
|
1559
1460
|
" skipped"
|
|
1560
1461
|
] })
|
|
@@ -1563,7 +1464,10 @@ function ReportFeature({ feature }) {
|
|
|
1563
1464
|
}
|
|
1564
1465
|
)
|
|
1565
1466
|
] }),
|
|
1566
|
-
/* @__PURE__ */ (0,
|
|
1467
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
|
|
1468
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportFeatureIntro, { feature }),
|
|
1469
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportScenarioList, { feature })
|
|
1470
|
+
] })
|
|
1567
1471
|
]
|
|
1568
1472
|
}
|
|
1569
1473
|
)
|
|
@@ -1571,17 +1475,17 @@ function ReportFeature({ feature }) {
|
|
|
1571
1475
|
}
|
|
1572
1476
|
|
|
1573
1477
|
// src/components/ReportFeatureList.tsx
|
|
1574
|
-
var
|
|
1478
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1575
1479
|
function ReportFeatureList() {
|
|
1576
1480
|
const report = useReport();
|
|
1577
|
-
return /* @__PURE__ */ (0,
|
|
1481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportFeature, { feature }, feature.id)) });
|
|
1578
1482
|
}
|
|
1579
1483
|
|
|
1580
1484
|
// src/components/ui/empty.tsx
|
|
1581
1485
|
var import_class_variance_authority2 = require("class-variance-authority");
|
|
1582
|
-
var
|
|
1486
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1583
1487
|
function Empty({ className, ...props }) {
|
|
1584
|
-
return /* @__PURE__ */ (0,
|
|
1488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1585
1489
|
"div",
|
|
1586
1490
|
{
|
|
1587
1491
|
"data-slot": "empty",
|
|
@@ -1594,7 +1498,7 @@ function Empty({ className, ...props }) {
|
|
|
1594
1498
|
);
|
|
1595
1499
|
}
|
|
1596
1500
|
function EmptyHeader({ className, ...props }) {
|
|
1597
|
-
return /* @__PURE__ */ (0,
|
|
1501
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1598
1502
|
"div",
|
|
1599
1503
|
{
|
|
1600
1504
|
"data-slot": "empty-header",
|
|
@@ -1621,7 +1525,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
|
1621
1525
|
}
|
|
1622
1526
|
);
|
|
1623
1527
|
function EmptyTitle({ className, ...props }) {
|
|
1624
|
-
return /* @__PURE__ */ (0,
|
|
1528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1625
1529
|
"div",
|
|
1626
1530
|
{
|
|
1627
1531
|
"data-slot": "empty-title",
|
|
@@ -1632,16 +1536,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1632
1536
|
}
|
|
1633
1537
|
|
|
1634
1538
|
// src/components/ReportEmpty.tsx
|
|
1635
|
-
var
|
|
1539
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1636
1540
|
function ReportEmpty({ message }) {
|
|
1637
|
-
return /* @__PURE__ */ (0,
|
|
1541
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1638
1542
|
}
|
|
1639
1543
|
|
|
1640
1544
|
// src/components/ReportSummary.tsx
|
|
1641
|
-
var
|
|
1545
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1642
1546
|
function ReportSummary({ className }) {
|
|
1643
1547
|
const report = useReport();
|
|
1644
|
-
return /* @__PURE__ */ (0,
|
|
1548
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1645
1549
|
ReportSummaryView,
|
|
1646
1550
|
{
|
|
1647
1551
|
summary: report.summary,
|
|
@@ -1667,7 +1571,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1667
1571
|
if (summary.pending > 0) {
|
|
1668
1572
|
cards.push({ status: "pending", label: "Pending", value: summary.pending });
|
|
1669
1573
|
}
|
|
1670
|
-
return /* @__PURE__ */ (0,
|
|
1574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1671
1575
|
"dl",
|
|
1672
1576
|
{
|
|
1673
1577
|
className: cn(
|
|
@@ -1680,17 +1584,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1680
1584
|
"aria-label": ariaLabel,
|
|
1681
1585
|
children: cards.map((c) => {
|
|
1682
1586
|
const tone = TONE[c.status];
|
|
1683
|
-
return /* @__PURE__ */ (0,
|
|
1587
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
1684
1588
|
"div",
|
|
1685
1589
|
{
|
|
1686
1590
|
"data-status": c.status,
|
|
1687
1591
|
className: "rounded-lg border bg-card px-4 py-3",
|
|
1688
1592
|
children: [
|
|
1689
|
-
/* @__PURE__ */ (0,
|
|
1690
|
-
/* @__PURE__ */ (0,
|
|
1593
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
1594
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
|
|
1691
1595
|
c.label
|
|
1692
1596
|
] }),
|
|
1693
|
-
/* @__PURE__ */ (0,
|
|
1597
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
|
|
1694
1598
|
]
|
|
1695
1599
|
},
|
|
1696
1600
|
c.status
|
|
@@ -1700,6 +1604,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1700
1604
|
);
|
|
1701
1605
|
}
|
|
1702
1606
|
|
|
1607
|
+
// src/components/ReportMeta.tsx
|
|
1608
|
+
var import_duration4 = require("executable-stories-core/utils/duration");
|
|
1609
|
+
|
|
1703
1610
|
// src/lib/provenance.ts
|
|
1704
1611
|
function reportLastRunMs(report) {
|
|
1705
1612
|
if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
|
|
@@ -1750,7 +1657,7 @@ function prUrl(ci) {
|
|
|
1750
1657
|
}
|
|
1751
1658
|
|
|
1752
1659
|
// src/components/ReportMeta.tsx
|
|
1753
|
-
var
|
|
1660
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1754
1661
|
function ReportMeta() {
|
|
1755
1662
|
const report = useReport();
|
|
1756
1663
|
const items = [];
|
|
@@ -1759,7 +1666,7 @@ function ReportMeta() {
|
|
|
1759
1666
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1760
1667
|
}
|
|
1761
1668
|
if (report.durationMs) {
|
|
1762
|
-
items.push({ label: "Duration", value: formatDuration(report.durationMs) });
|
|
1669
|
+
items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
|
|
1763
1670
|
}
|
|
1764
1671
|
if (report.packageVersion) {
|
|
1765
1672
|
items.push({ label: "Version", value: report.packageVersion });
|
|
@@ -1773,13 +1680,13 @@ function ReportMeta() {
|
|
|
1773
1680
|
const short = sha.slice(0, 8);
|
|
1774
1681
|
items.push({
|
|
1775
1682
|
label: "Commit",
|
|
1776
|
-
value: url ? /* @__PURE__ */ (0,
|
|
1683
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1777
1684
|
});
|
|
1778
1685
|
}
|
|
1779
1686
|
if (report.ci) {
|
|
1780
1687
|
items.push({
|
|
1781
1688
|
label: "CI",
|
|
1782
|
-
value: report.ci.url ? /* @__PURE__ */ (0,
|
|
1689
|
+
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1783
1690
|
ciDisplayName(report.ci.name),
|
|
1784
1691
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1785
1692
|
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
@@ -1790,7 +1697,7 @@ function ReportMeta() {
|
|
|
1790
1697
|
const label = `#${report.ci.prNumber}`;
|
|
1791
1698
|
items.push({
|
|
1792
1699
|
label: "PR",
|
|
1793
|
-
value: url ? /* @__PURE__ */ (0,
|
|
1700
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1794
1701
|
});
|
|
1795
1702
|
}
|
|
1796
1703
|
if (items.length === 0) return null;
|
|
@@ -1798,48 +1705,48 @@ function ReportMeta() {
|
|
|
1798
1705
|
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1799
1706
|
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1800
1707
|
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1801
|
-
/* @__PURE__ */ (0,
|
|
1802
|
-
/* @__PURE__ */ (0,
|
|
1803
|
-
/* @__PURE__ */ (0,
|
|
1708
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1709
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1710
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1804
1711
|
"Run details"
|
|
1805
1712
|
] }),
|
|
1806
|
-
/* @__PURE__ */ (0,
|
|
1807
|
-
/* @__PURE__ */ (0,
|
|
1808
|
-
/* @__PURE__ */ (0,
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1714
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1809
1716
|
] }, item.label)) })
|
|
1810
1717
|
] })
|
|
1811
1718
|
);
|
|
1812
1719
|
}
|
|
1813
1720
|
|
|
1814
1721
|
// src/components/ReportSchemaError.tsx
|
|
1815
|
-
var
|
|
1722
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1816
1723
|
function ReportSchemaError({ error }) {
|
|
1817
|
-
return /* @__PURE__ */ (0,
|
|
1818
|
-
/* @__PURE__ */ (0,
|
|
1819
|
-
/* @__PURE__ */ (0,
|
|
1820
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0,
|
|
1724
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1725
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: error.message }),
|
|
1727
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { children: [
|
|
1821
1728
|
"The report bundle is newer than this version of ",
|
|
1822
|
-
/* @__PURE__ */ (0,
|
|
1729
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: "executable-stories-react" }),
|
|
1823
1730
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1824
1731
|
] }) : null,
|
|
1825
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0,
|
|
1826
|
-
/* @__PURE__ */ (0,
|
|
1732
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("details", { children: [
|
|
1733
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("summary", { children: [
|
|
1827
1734
|
error.issues.length,
|
|
1828
1735
|
" validation issue",
|
|
1829
1736
|
error.issues.length === 1 ? "" : "s"
|
|
1830
1737
|
] }),
|
|
1831
|
-
/* @__PURE__ */ (0,
|
|
1738
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1832
1739
|
] }) : null
|
|
1833
1740
|
] });
|
|
1834
1741
|
}
|
|
1835
1742
|
|
|
1836
1743
|
// src/components/ReportShell.tsx
|
|
1837
|
-
var
|
|
1744
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1838
1745
|
function ReportTitleBlock({ title }) {
|
|
1839
|
-
return /* @__PURE__ */ (0,
|
|
1840
|
-
/* @__PURE__ */ (0,
|
|
1841
|
-
/* @__PURE__ */ (0,
|
|
1842
|
-
/* @__PURE__ */ (0,
|
|
1746
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h1", { children: title ?? "Story Report" }),
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ReportSummary, {}),
|
|
1749
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ReportMeta, {})
|
|
1843
1750
|
] });
|
|
1844
1751
|
}
|
|
1845
1752
|
function ReportErrorShell({
|
|
@@ -1848,28 +1755,28 @@ function ReportErrorShell({
|
|
|
1848
1755
|
title,
|
|
1849
1756
|
dataTheme
|
|
1850
1757
|
}) {
|
|
1851
|
-
return /* @__PURE__ */ (0,
|
|
1758
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ReportSchemaError, { error }) });
|
|
1852
1759
|
}
|
|
1853
1760
|
|
|
1854
1761
|
// src/components/Report.tsx
|
|
1855
|
-
var
|
|
1762
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1856
1763
|
function Report(props) {
|
|
1857
1764
|
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1858
1765
|
const result = unwrapReport(report);
|
|
1859
1766
|
if (!result.ok) {
|
|
1860
|
-
return /* @__PURE__ */ (0,
|
|
1767
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1861
1768
|
}
|
|
1862
1769
|
const data = result.data;
|
|
1863
1770
|
const hasContent = data.features.length > 0;
|
|
1864
|
-
return /* @__PURE__ */ (0,
|
|
1865
|
-
/* @__PURE__ */ (0,
|
|
1866
|
-
hasContent ? /* @__PURE__ */ (0,
|
|
1771
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
|
|
1772
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("header", { className: "es-report-header", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportTitleBlock, { title }) }),
|
|
1773
|
+
hasContent ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportFeatureList, {}) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportEmpty, {})
|
|
1867
1774
|
] }) });
|
|
1868
1775
|
}
|
|
1869
1776
|
|
|
1870
1777
|
// src/components/doc/MermaidView.tsx
|
|
1871
1778
|
var import_react8 = require("react");
|
|
1872
|
-
var
|
|
1779
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1873
1780
|
function prefersDark(el) {
|
|
1874
1781
|
const themed = el?.closest("[data-theme]");
|
|
1875
1782
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -1923,9 +1830,9 @@ function MermaidView({ entry, load }) {
|
|
|
1923
1830
|
};
|
|
1924
1831
|
}, [entry.code, load]);
|
|
1925
1832
|
if (svg) {
|
|
1926
|
-
return /* @__PURE__ */ (0,
|
|
1927
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
1928
|
-
/* @__PURE__ */ (0,
|
|
1833
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("figure", { ref, className: "my-3", children: [
|
|
1834
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
1835
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1929
1836
|
"div",
|
|
1930
1837
|
{
|
|
1931
1838
|
role: "img",
|
|
@@ -1936,8 +1843,8 @@ function MermaidView({ entry, load }) {
|
|
|
1936
1843
|
)
|
|
1937
1844
|
] });
|
|
1938
1845
|
}
|
|
1939
|
-
return /* @__PURE__ */ (0,
|
|
1940
|
-
error ? /* @__PURE__ */ (0,
|
|
1846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
1847
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
1941
1848
|
"p",
|
|
1942
1849
|
{
|
|
1943
1850
|
role: "alert",
|
|
@@ -1949,19 +1856,19 @@ function MermaidView({ entry, load }) {
|
|
|
1949
1856
|
]
|
|
1950
1857
|
}
|
|
1951
1858
|
) : null,
|
|
1952
|
-
/* @__PURE__ */ (0,
|
|
1859
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MermaidSource, { entry, ref })
|
|
1953
1860
|
] });
|
|
1954
1861
|
}
|
|
1955
1862
|
|
|
1956
1863
|
// src/components/doc/MermaidDiagram.tsx
|
|
1957
|
-
var
|
|
1864
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
1958
1865
|
var mermaidLoader;
|
|
1959
1866
|
function loadMermaid() {
|
|
1960
1867
|
mermaidLoader ??= import("mermaid").then((m) => m.default);
|
|
1961
1868
|
return mermaidLoader;
|
|
1962
1869
|
}
|
|
1963
1870
|
function MermaidDiagram({ entry, load = loadMermaid }) {
|
|
1964
|
-
return /* @__PURE__ */ (0,
|
|
1871
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MermaidView, { entry, load });
|
|
1965
1872
|
}
|
|
1966
1873
|
|
|
1967
1874
|
// src/components/ScenarioExplorer.tsx
|
|
@@ -1971,12 +1878,12 @@ var import_react9 = require("react");
|
|
|
1971
1878
|
var React4 = require("react");
|
|
1972
1879
|
var import_cmdk = require("cmdk");
|
|
1973
1880
|
var import_lucide_react3 = require("lucide-react");
|
|
1974
|
-
var
|
|
1881
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1975
1882
|
function Command({
|
|
1976
1883
|
className,
|
|
1977
1884
|
...props
|
|
1978
1885
|
}) {
|
|
1979
|
-
return /* @__PURE__ */ (0,
|
|
1886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
1980
1887
|
import_cmdk.Command,
|
|
1981
1888
|
{
|
|
1982
1889
|
"data-slot": "command",
|
|
@@ -1992,14 +1899,14 @@ function CommandInput({
|
|
|
1992
1899
|
className,
|
|
1993
1900
|
...props
|
|
1994
1901
|
}) {
|
|
1995
|
-
return /* @__PURE__ */ (0,
|
|
1902
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
1996
1903
|
"div",
|
|
1997
1904
|
{
|
|
1998
1905
|
"data-slot": "command-input-wrapper",
|
|
1999
1906
|
className: "flex h-9 items-center gap-2 border-b px-3",
|
|
2000
1907
|
children: [
|
|
2001
|
-
/* @__PURE__ */ (0,
|
|
2002
|
-
/* @__PURE__ */ (0,
|
|
1908
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react3.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
|
|
1909
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2003
1910
|
import_cmdk.Command.Input,
|
|
2004
1911
|
{
|
|
2005
1912
|
"data-slot": "command-input",
|
|
@@ -2018,7 +1925,7 @@ function CommandList({
|
|
|
2018
1925
|
className,
|
|
2019
1926
|
...props
|
|
2020
1927
|
}) {
|
|
2021
|
-
return /* @__PURE__ */ (0,
|
|
1928
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2022
1929
|
import_cmdk.Command.List,
|
|
2023
1930
|
{
|
|
2024
1931
|
"data-slot": "command-list",
|
|
@@ -2033,7 +1940,7 @@ function CommandList({
|
|
|
2033
1940
|
function CommandEmpty({
|
|
2034
1941
|
...props
|
|
2035
1942
|
}) {
|
|
2036
|
-
return /* @__PURE__ */ (0,
|
|
1943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2037
1944
|
import_cmdk.Command.Empty,
|
|
2038
1945
|
{
|
|
2039
1946
|
"data-slot": "command-empty",
|
|
@@ -2046,7 +1953,7 @@ function CommandGroup({
|
|
|
2046
1953
|
className,
|
|
2047
1954
|
...props
|
|
2048
1955
|
}) {
|
|
2049
|
-
return /* @__PURE__ */ (0,
|
|
1956
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2050
1957
|
import_cmdk.Command.Group,
|
|
2051
1958
|
{
|
|
2052
1959
|
"data-slot": "command-group",
|
|
@@ -2062,7 +1969,7 @@ function CommandItem({
|
|
|
2062
1969
|
className,
|
|
2063
1970
|
...props
|
|
2064
1971
|
}) {
|
|
2065
|
-
return /* @__PURE__ */ (0,
|
|
1972
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2066
1973
|
import_cmdk.Command.Item,
|
|
2067
1974
|
{
|
|
2068
1975
|
"data-slot": "command-item",
|
|
@@ -2076,7 +1983,7 @@ function CommandItem({
|
|
|
2076
1983
|
}
|
|
2077
1984
|
|
|
2078
1985
|
// src/components/ScenarioExplorer.tsx
|
|
2079
|
-
var
|
|
1986
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2080
1987
|
var STATUS_FILTERS = [
|
|
2081
1988
|
{ value: "all", label: "All" },
|
|
2082
1989
|
{ value: "passed", label: "Passed" },
|
|
@@ -2087,22 +1994,22 @@ var STATUS_FILTERS = [
|
|
|
2087
1994
|
function ScenarioExplorer({ scenarios, summary }) {
|
|
2088
1995
|
const [status, setStatus] = (0, import_react9.useState)("all");
|
|
2089
1996
|
const visible = status === "all" ? scenarios : scenarios.filter((s) => s.status === status);
|
|
2090
|
-
return /* @__PURE__ */ (0,
|
|
2091
|
-
summary ? /* @__PURE__ */ (0,
|
|
1997
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "font-sans text-foreground", children: [
|
|
1998
|
+
summary ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "mb-3 text-sm text-muted-foreground", children: [
|
|
2092
1999
|
summary.total,
|
|
2093
2000
|
" scenarios \xB7 ",
|
|
2094
|
-
/* @__PURE__ */ (0,
|
|
2001
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-pass", children: [
|
|
2095
2002
|
summary.passed,
|
|
2096
2003
|
" passed"
|
|
2097
2004
|
] }),
|
|
2098
2005
|
" \xB7",
|
|
2099
2006
|
" ",
|
|
2100
|
-
/* @__PURE__ */ (0,
|
|
2007
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-fail", children: [
|
|
2101
2008
|
summary.failed,
|
|
2102
2009
|
" failed"
|
|
2103
2010
|
] })
|
|
2104
2011
|
] }) : null,
|
|
2105
|
-
/* @__PURE__ */ (0,
|
|
2012
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "mb-2 flex flex-wrap gap-1.5", role: "group", "aria-label": "Filter by status", children: STATUS_FILTERS.map((f) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2106
2013
|
"button",
|
|
2107
2014
|
{
|
|
2108
2015
|
type: "button",
|
|
@@ -2117,26 +2024,26 @@ function ScenarioExplorer({ scenarios, summary }) {
|
|
|
2117
2024
|
},
|
|
2118
2025
|
f.value
|
|
2119
2026
|
)) }),
|
|
2120
|
-
/* @__PURE__ */ (0,
|
|
2027
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2121
2028
|
Command,
|
|
2122
2029
|
{
|
|
2123
2030
|
className: "rounded-lg border border-border bg-card",
|
|
2124
2031
|
filter: (value, search) => value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0,
|
|
2125
2032
|
children: [
|
|
2126
|
-
/* @__PURE__ */ (0,
|
|
2127
|
-
/* @__PURE__ */ (0,
|
|
2128
|
-
/* @__PURE__ */ (0,
|
|
2129
|
-
/* @__PURE__ */ (0,
|
|
2033
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CommandInput, { placeholder: "Search scenarios\u2026" }),
|
|
2034
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(CommandList, { className: "max-h-none", children: [
|
|
2035
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CommandEmpty, { children: "No scenarios match your filters." }),
|
|
2036
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CommandGroup, { children: visible.map((s) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2130
2037
|
CommandItem,
|
|
2131
2038
|
{
|
|
2132
2039
|
value: `${s.title} ${s.feature} ${s.tags.join(" ")}`,
|
|
2133
2040
|
asChild: true,
|
|
2134
2041
|
className: "cursor-pointer",
|
|
2135
|
-
children: /* @__PURE__ */ (0,
|
|
2136
|
-
/* @__PURE__ */ (0,
|
|
2137
|
-
/* @__PURE__ */ (0,
|
|
2138
|
-
s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0,
|
|
2139
|
-
/* @__PURE__ */ (0,
|
|
2042
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("a", { href: s.href, className: "flex items-center gap-3", children: [
|
|
2043
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Badge, { variant: s.status, children: s.status }),
|
|
2044
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "min-w-0 flex-1 truncate font-medium", children: s.title }),
|
|
2045
|
+
s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Badge, { variant: "tag", className: "hidden sm:inline-flex", children: t }, t)),
|
|
2046
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { className: "hidden font-mono text-xs text-muted-foreground md:inline", children: s.feature })
|
|
2140
2047
|
] })
|
|
2141
2048
|
},
|
|
2142
2049
|
s.id
|
|
@@ -2283,7 +2190,7 @@ var story_report_v1_default = {
|
|
|
2283
2190
|
title: {
|
|
2284
2191
|
type: "string",
|
|
2285
2192
|
minLength: 1,
|
|
2286
|
-
description: "Display title.
|
|
2193
|
+
description: "Display title. Comes from story.feature(...) when declared, otherwise the describe block or file basename."
|
|
2287
2194
|
},
|
|
2288
2195
|
sourceFile: {
|
|
2289
2196
|
type: "string",
|
|
@@ -2299,6 +2206,26 @@ var story_report_v1_default = {
|
|
|
2299
2206
|
$ref: "#/$defs/Scenario"
|
|
2300
2207
|
},
|
|
2301
2208
|
description: "Scenarios in this feature, in declaration order."
|
|
2209
|
+
},
|
|
2210
|
+
kind: {
|
|
2211
|
+
type: "string",
|
|
2212
|
+
enum: [
|
|
2213
|
+
"feature",
|
|
2214
|
+
"ability",
|
|
2215
|
+
"business-need"
|
|
2216
|
+
],
|
|
2217
|
+
description: "How the feature was declared. Absent when the title was derived rather than declared."
|
|
2218
|
+
},
|
|
2219
|
+
narrative: {
|
|
2220
|
+
type: "string",
|
|
2221
|
+
description: "Markdown explaining why the feature exists and who it serves."
|
|
2222
|
+
},
|
|
2223
|
+
glossary: {
|
|
2224
|
+
type: "array",
|
|
2225
|
+
items: {
|
|
2226
|
+
$ref: "#/$defs/GlossaryTerm"
|
|
2227
|
+
},
|
|
2228
|
+
description: "Terms this feature defines, linked wherever they appear in scenario text."
|
|
2302
2229
|
}
|
|
2303
2230
|
},
|
|
2304
2231
|
required: [
|
|
@@ -3110,6 +3037,25 @@ var story_report_v1_default = {
|
|
|
3110
3037
|
}
|
|
3111
3038
|
},
|
|
3112
3039
|
additionalProperties: false
|
|
3040
|
+
},
|
|
3041
|
+
GlossaryTerm: {
|
|
3042
|
+
type: "object",
|
|
3043
|
+
description: "One glossary entry: a term the scenarios use and what it means.",
|
|
3044
|
+
properties: {
|
|
3045
|
+
term: {
|
|
3046
|
+
type: "string",
|
|
3047
|
+
minLength: 1
|
|
3048
|
+
},
|
|
3049
|
+
definition: {
|
|
3050
|
+
type: "string",
|
|
3051
|
+
minLength: 1
|
|
3052
|
+
}
|
|
3053
|
+
},
|
|
3054
|
+
required: [
|
|
3055
|
+
"term",
|
|
3056
|
+
"definition"
|
|
3057
|
+
],
|
|
3058
|
+
additionalProperties: false
|
|
3113
3059
|
}
|
|
3114
3060
|
}
|
|
3115
3061
|
};
|