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/ssr.js
CHANGED
|
@@ -287,23 +287,11 @@ function useReport() {
|
|
|
287
287
|
return ctx.report;
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (ms < 6e4) {
|
|
296
|
-
return `${(ms / 1e3).toFixed(2)} s`;
|
|
297
|
-
}
|
|
298
|
-
const minutes = Math.floor(ms / 6e4);
|
|
299
|
-
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
300
|
-
if (minutes < 60) {
|
|
301
|
-
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
302
|
-
}
|
|
303
|
-
const hours = Math.floor(minutes / 60);
|
|
304
|
-
const remainingMinutes = minutes % 60;
|
|
305
|
-
return `${hours}h ${remainingMinutes}m`;
|
|
306
|
-
}
|
|
290
|
+
// src/components/ReportScenario.tsx
|
|
291
|
+
import { formatDuration as formatDuration3 } from "executable-stories-core/utils/duration";
|
|
292
|
+
|
|
293
|
+
// src/components/ReportSteps.tsx
|
|
294
|
+
import { formatDuration } from "executable-stories-core/utils/duration";
|
|
307
295
|
|
|
308
296
|
// src/components/doc/DocNote.tsx
|
|
309
297
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
@@ -392,36 +380,29 @@ function DocLink({ entry }) {
|
|
|
392
380
|
);
|
|
393
381
|
}
|
|
394
382
|
|
|
395
|
-
// src/
|
|
383
|
+
// src/lib/markdown.ts
|
|
396
384
|
import { marked } from "marked";
|
|
397
385
|
|
|
398
|
-
//
|
|
399
|
-
|
|
400
|
-
if (!value) return void 0;
|
|
401
|
-
const trimmed = value.trim();
|
|
402
|
-
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
|
|
403
|
-
if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
|
|
404
|
-
return trimmed;
|
|
405
|
-
}
|
|
406
|
-
function safeImageUrl(value) {
|
|
407
|
-
if (!value) return void 0;
|
|
408
|
-
const trimmed = value.trim();
|
|
409
|
-
if (/^data:image\//i.test(trimmed)) return trimmed;
|
|
410
|
-
return safeUrl(trimmed);
|
|
411
|
-
}
|
|
412
|
-
function isLocalFsPath(value) {
|
|
413
|
-
return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
|
|
414
|
-
}
|
|
386
|
+
// src/lib/url.ts
|
|
387
|
+
import { safeUrl, safeImageUrl, isLocalFsPath } from "executable-stories-core/utils/url";
|
|
415
388
|
|
|
416
|
-
// src/
|
|
417
|
-
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
418
|
-
function neutralizeUrl(value) {
|
|
419
|
-
return safeUrl(value) ?? "#";
|
|
420
|
-
}
|
|
389
|
+
// src/lib/markdown.ts
|
|
421
390
|
function safeMarkdownHtml(markdown) {
|
|
422
391
|
const raw = marked.parse(markdown, { async: false });
|
|
423
392
|
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)}'`);
|
|
424
393
|
}
|
|
394
|
+
function dedent(text) {
|
|
395
|
+
const lines = text.replace(/\t/g, " ").split("\n");
|
|
396
|
+
const indents = lines.filter((line) => line.trim().length > 0).map((line) => line.length - line.trimStart().length);
|
|
397
|
+
const shortest = indents.length > 0 ? Math.min(...indents) : 0;
|
|
398
|
+
return lines.map((line) => line.slice(shortest)).join("\n").trim();
|
|
399
|
+
}
|
|
400
|
+
function neutralizeUrl(value) {
|
|
401
|
+
return safeUrl(value) ?? "#";
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/components/doc/DocSection.tsx
|
|
405
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
425
406
|
function DocSection({ entry }) {
|
|
426
407
|
const renderers = useBuiltinRenderers();
|
|
427
408
|
if (renderers.section) {
|
|
@@ -704,125 +685,10 @@ function ReportStepItem({ step }) {
|
|
|
704
685
|
);
|
|
705
686
|
}
|
|
706
687
|
|
|
707
|
-
// ../executable-stories-core/dist/state-diff.js
|
|
708
|
-
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
709
|
-
function deepEqual(a, b) {
|
|
710
|
-
if (Object.is(a, b)) return true;
|
|
711
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
712
|
-
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
713
|
-
}
|
|
714
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
715
|
-
const ak = Object.keys(a);
|
|
716
|
-
const bk = Object.keys(b);
|
|
717
|
-
return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
|
|
718
|
-
}
|
|
719
|
-
return false;
|
|
720
|
-
}
|
|
721
|
-
var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
|
|
722
|
-
function walk(path, before, after, out) {
|
|
723
|
-
if (Object.is(before, after)) return;
|
|
724
|
-
if (Array.isArray(before) && Array.isArray(after)) {
|
|
725
|
-
const shared = Math.min(before.length, after.length);
|
|
726
|
-
for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
|
|
727
|
-
for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
|
|
728
|
-
for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
if (isPlainObject(before) && isPlainObject(after)) {
|
|
732
|
-
for (const key of Object.keys(after)) {
|
|
733
|
-
const p = joinKey(path, key);
|
|
734
|
-
if (key in before) walk(p, before[key], after[key], out);
|
|
735
|
-
else out.push({ path: p, kind: "added", after: after[key] });
|
|
736
|
-
}
|
|
737
|
-
for (const key of Object.keys(before)) {
|
|
738
|
-
if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
|
|
739
|
-
}
|
|
740
|
-
return;
|
|
741
|
-
}
|
|
742
|
-
if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
|
|
743
|
-
}
|
|
744
|
-
function diffStateValues(before, after) {
|
|
745
|
-
const out = [];
|
|
746
|
-
walk("", before, after, out);
|
|
747
|
-
return out;
|
|
748
|
-
}
|
|
749
|
-
function formatStateValue(value, maxLen = 60) {
|
|
750
|
-
const s = value === void 0 ? "undefined" : JSON.stringify(value);
|
|
751
|
-
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
752
|
-
}
|
|
753
|
-
function summarizeStateChanges(changes) {
|
|
754
|
-
return changes.map((c) => {
|
|
755
|
-
const at = c.path === "" ? "(root)" : c.path;
|
|
756
|
-
switch (c.kind) {
|
|
757
|
-
case "added":
|
|
758
|
-
return `+ ${at}: ${formatStateValue(c.after)}`;
|
|
759
|
-
case "removed":
|
|
760
|
-
return `\u2212 ${at}`;
|
|
761
|
-
case "changed":
|
|
762
|
-
return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
|
|
763
|
-
}
|
|
764
|
-
});
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
// ../executable-stories-core/dist/storyboard.js
|
|
768
|
-
function findScreenshot(entries) {
|
|
769
|
-
for (const entry of entries) {
|
|
770
|
-
if (entry.kind === "screenshot") return entry;
|
|
771
|
-
if (entry.children) {
|
|
772
|
-
const nested = findScreenshot(entry.children);
|
|
773
|
-
if (nested) return nested;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return void 0;
|
|
777
|
-
}
|
|
778
|
-
function findStates(entries, out = []) {
|
|
779
|
-
for (const entry of entries) {
|
|
780
|
-
if (entry.kind === "state") out.push(entry);
|
|
781
|
-
if (entry.children) findStates(entry.children, out);
|
|
782
|
-
}
|
|
783
|
-
return out;
|
|
784
|
-
}
|
|
785
|
-
var laneOf = (label) => label ?? "";
|
|
786
|
-
function extractStoryboardFrames(scenario) {
|
|
787
|
-
const frames = [];
|
|
788
|
-
const laneOrder = [];
|
|
789
|
-
const previous = /* @__PURE__ */ new Map();
|
|
790
|
-
for (const step of scenario.steps) {
|
|
791
|
-
const shot = findScreenshot(step.docEntries);
|
|
792
|
-
const stateDocs = findStates(step.docEntries);
|
|
793
|
-
if (!shot && stateDocs.length === 0) continue;
|
|
794
|
-
const byLane = /* @__PURE__ */ new Map();
|
|
795
|
-
for (const doc of stateDocs) {
|
|
796
|
-
const lane = laneOf(doc.label);
|
|
797
|
-
if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
|
|
798
|
-
byLane.set(lane, doc);
|
|
799
|
-
}
|
|
800
|
-
const states = [];
|
|
801
|
-
for (const lane of laneOrder) {
|
|
802
|
-
const doc = byLane.get(lane);
|
|
803
|
-
if (!doc) continue;
|
|
804
|
-
const card = {
|
|
805
|
-
...doc.label !== void 0 && { label: doc.label },
|
|
806
|
-
value: doc.value
|
|
807
|
-
};
|
|
808
|
-
if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
|
|
809
|
-
previous.set(lane, doc.value);
|
|
810
|
-
states.push(card);
|
|
811
|
-
}
|
|
812
|
-
frames.push({
|
|
813
|
-
stepId: step.id,
|
|
814
|
-
stepIndex: step.index,
|
|
815
|
-
keyword: step.keyword,
|
|
816
|
-
text: step.text,
|
|
817
|
-
status: step.status,
|
|
818
|
-
...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
|
|
819
|
-
states
|
|
820
|
-
});
|
|
821
|
-
}
|
|
822
|
-
return frames;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
688
|
// src/components/ReportStoryboard.tsx
|
|
689
|
+
import { extractStoryboardFrames } from "executable-stories-core/storyboard";
|
|
690
|
+
import { summarizeStateChanges } from "executable-stories-core/state-diff";
|
|
691
|
+
import { isLocalFsPath as isLocalFsPath2, safeImageUrl as safeImageUrl2 } from "executable-stories-core/utils/url";
|
|
826
692
|
import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
827
693
|
var FRAME_GLYPH = {
|
|
828
694
|
passed: "\u2713",
|
|
@@ -871,7 +737,7 @@ function StoryboardFrameItem({ frame }) {
|
|
|
871
737
|
}
|
|
872
738
|
function FrameScreenshot({ frame, label }) {
|
|
873
739
|
const path = frame.screenshot?.path ?? "";
|
|
874
|
-
const src =
|
|
740
|
+
const src = isLocalFsPath2(path) ? void 0 : safeImageUrl2(path);
|
|
875
741
|
return /* @__PURE__ */ jsx20(
|
|
876
742
|
"a",
|
|
877
743
|
{
|
|
@@ -977,6 +843,9 @@ function ReportAttachments({ attachments }) {
|
|
|
977
843
|
);
|
|
978
844
|
}
|
|
979
845
|
|
|
846
|
+
// src/components/ReportTrace.tsx
|
|
847
|
+
import { formatDuration as formatDuration2 } from "executable-stories-core/utils/duration";
|
|
848
|
+
|
|
980
849
|
// src/lib/trace.ts
|
|
981
850
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
982
851
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -1029,15 +898,15 @@ function toTraceRows(spans) {
|
|
|
1029
898
|
roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
1030
899
|
const rows = [];
|
|
1031
900
|
const seen = /* @__PURE__ */ new Set();
|
|
1032
|
-
const
|
|
901
|
+
const walk = (node, depth) => {
|
|
1033
902
|
if (seen.has(node.span.spanId)) return;
|
|
1034
903
|
seen.add(node.span.spanId);
|
|
1035
904
|
rows.push({ ...node.span, depth });
|
|
1036
|
-
for (const child of node.children)
|
|
905
|
+
for (const child of node.children) walk(child, depth + 1);
|
|
1037
906
|
};
|
|
1038
|
-
for (const root of roots)
|
|
907
|
+
for (const root of roots) walk(root, 0);
|
|
1039
908
|
for (const node of byId.values()) {
|
|
1040
|
-
if (!seen.has(node.span.spanId))
|
|
909
|
+
if (!seen.has(node.span.spanId)) walk(node, 0);
|
|
1041
910
|
}
|
|
1042
911
|
return rows;
|
|
1043
912
|
}
|
|
@@ -1071,7 +940,7 @@ function ReportTrace({ spans }) {
|
|
|
1071
940
|
" span",
|
|
1072
941
|
rows.length === 1 ? "" : "s",
|
|
1073
942
|
", ",
|
|
1074
|
-
|
|
943
|
+
formatDuration2(totalMs),
|
|
1075
944
|
")"
|
|
1076
945
|
] }),
|
|
1077
946
|
/* @__PURE__ */ jsx22("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
|
|
@@ -1094,7 +963,7 @@ function ReportTrace({ spans }) {
|
|
|
1094
963
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
1095
964
|
}
|
|
1096
965
|
) }),
|
|
1097
|
-
/* @__PURE__ */ jsx22("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children:
|
|
966
|
+
/* @__PURE__ */ jsx22("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration2(row.durationMs) })
|
|
1098
967
|
] }, `${row.spanId}-${i}`);
|
|
1099
968
|
}) })
|
|
1100
969
|
] });
|
|
@@ -1205,6 +1074,7 @@ function useCollapse() {
|
|
|
1205
1074
|
|
|
1206
1075
|
// src/interactive/scenario-actions.tsx
|
|
1207
1076
|
import { createContext as createContext4, useContext as useContext5 } from "react";
|
|
1077
|
+
import { scenarioToMarkdown as coreScenarioToMarkdown } from "executable-stories-core/utils/scenario-markdown";
|
|
1208
1078
|
var ScenarioActionsContext = createContext4(null);
|
|
1209
1079
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
1210
1080
|
function useScenarioActions() {
|
|
@@ -1350,7 +1220,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1350
1220
|
] }),
|
|
1351
1221
|
/* @__PURE__ */ jsxs17("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1352
1222
|
/* @__PURE__ */ jsx26(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1353
|
-
scenario.durationMs > 0 ? /* @__PURE__ */ jsx26("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children:
|
|
1223
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ jsx26("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration3(scenario.durationMs) }) : null,
|
|
1354
1224
|
meta.showPill ? /* @__PURE__ */ jsx26(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx26("span", { className: "sr-only", children: `Status: ${label}` }),
|
|
1355
1225
|
actions ? /* @__PURE__ */ jsxs17(DropdownMenu, { children: [
|
|
1356
1226
|
/* @__PURE__ */ jsx26(
|
|
@@ -1409,8 +1279,35 @@ function ReportScenarioList({ feature }) {
|
|
|
1409
1279
|
return /* @__PURE__ */ jsx27(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx27(ReportScenario, { scenario }, scenario.id)) });
|
|
1410
1280
|
}
|
|
1411
1281
|
|
|
1282
|
+
// src/components/ReportFeatureIntro.tsx
|
|
1283
|
+
import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1284
|
+
function ReportFeatureIntro({ feature }) {
|
|
1285
|
+
const hasNarrative = Boolean(feature.narrative?.trim());
|
|
1286
|
+
const hasGlossary = Boolean(feature.glossary?.length);
|
|
1287
|
+
if (!hasNarrative && !hasGlossary) return null;
|
|
1288
|
+
return /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
|
|
1289
|
+
hasNarrative ? /* @__PURE__ */ jsx28(
|
|
1290
|
+
"div",
|
|
1291
|
+
{
|
|
1292
|
+
className: "es-doc-prose prose prose-sm max-w-none text-muted-foreground",
|
|
1293
|
+
dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
|
|
1294
|
+
}
|
|
1295
|
+
) : null,
|
|
1296
|
+
hasGlossary ? /* @__PURE__ */ jsx28("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
|
|
1297
|
+
/* @__PURE__ */ jsx28("dt", { className: "font-medium text-foreground", children: entry.term }),
|
|
1298
|
+
/* @__PURE__ */ jsx28("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
|
|
1299
|
+
] }, entry.term)) }) : null
|
|
1300
|
+
] });
|
|
1301
|
+
}
|
|
1302
|
+
function featureKindLabel(kind) {
|
|
1303
|
+
if (kind === "ability") return "Ability";
|
|
1304
|
+
if (kind === "business-need") return "Business need";
|
|
1305
|
+
if (kind === "feature") return "Feature";
|
|
1306
|
+
return null;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1412
1309
|
// src/components/ReportFeature.tsx
|
|
1413
|
-
import { Fragment as Fragment9, jsx as
|
|
1310
|
+
import { Fragment as Fragment9, jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1414
1311
|
function ReportFeature({ feature }) {
|
|
1415
1312
|
const titleId = `${feature.id}-title`;
|
|
1416
1313
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1418,10 +1315,11 @@ function ReportFeature({ feature }) {
|
|
|
1418
1315
|
const skipped = s.skipped + s.pending;
|
|
1419
1316
|
const collapse = useCollapse();
|
|
1420
1317
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
1318
|
+
const kindLabel = featureKindLabel(feature.kind);
|
|
1421
1319
|
return (
|
|
1422
1320
|
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1423
1321
|
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1424
|
-
/* @__PURE__ */
|
|
1322
|
+
/* @__PURE__ */ jsxs19(
|
|
1425
1323
|
"section",
|
|
1426
1324
|
{
|
|
1427
1325
|
id: feature.id,
|
|
@@ -1429,9 +1327,9 @@ function ReportFeature({ feature }) {
|
|
|
1429
1327
|
"aria-labelledby": titleId,
|
|
1430
1328
|
className: "flex flex-col gap-3",
|
|
1431
1329
|
children: [
|
|
1432
|
-
/* @__PURE__ */
|
|
1433
|
-
/* @__PURE__ */
|
|
1434
|
-
/* @__PURE__ */
|
|
1330
|
+
/* @__PURE__ */ jsxs19("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
|
|
1331
|
+
/* @__PURE__ */ jsxs19("div", { className: "min-w-0", children: [
|
|
1332
|
+
/* @__PURE__ */ jsx29("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs19(
|
|
1435
1333
|
"button",
|
|
1436
1334
|
{
|
|
1437
1335
|
type: "button",
|
|
@@ -1440,31 +1338,34 @@ function ReportFeature({ feature }) {
|
|
|
1440
1338
|
"aria-controls": bodyId,
|
|
1441
1339
|
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1442
1340
|
children: [
|
|
1443
|
-
/* @__PURE__ */
|
|
1444
|
-
/* @__PURE__ */
|
|
1341
|
+
/* @__PURE__ */ jsx29("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
|
|
1342
|
+
/* @__PURE__ */ jsx29("span", { className: "truncate", children: feature.title })
|
|
1445
1343
|
]
|
|
1446
1344
|
}
|
|
1447
1345
|
) : feature.title }),
|
|
1448
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsxs19("p", { className: cn("mt-0.5 flex items-center gap-2 text-xs text-muted-foreground", collapse && "pl-5"), children: [
|
|
1347
|
+
kindLabel ? /* @__PURE__ */ jsx29("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-medium text-foreground", children: kindLabel }) : null,
|
|
1348
|
+
/* @__PURE__ */ jsx29("span", { className: "truncate font-mono", children: feature.sourceFile })
|
|
1349
|
+
] })
|
|
1449
1350
|
] }),
|
|
1450
|
-
/* @__PURE__ */
|
|
1351
|
+
/* @__PURE__ */ jsxs19(
|
|
1451
1352
|
"p",
|
|
1452
1353
|
{
|
|
1453
1354
|
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1454
1355
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1455
1356
|
children: [
|
|
1456
|
-
/* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsxs19("span", { children: [
|
|
1457
1358
|
s.passed,
|
|
1458
1359
|
" passed"
|
|
1459
1360
|
] }),
|
|
1460
|
-
/* @__PURE__ */
|
|
1461
|
-
/* @__PURE__ */
|
|
1361
|
+
/* @__PURE__ */ jsx29("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1362
|
+
/* @__PURE__ */ jsxs19("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
|
|
1462
1363
|
s.failed,
|
|
1463
1364
|
" failed"
|
|
1464
1365
|
] }),
|
|
1465
|
-
skipped > 0 ? /* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
/* @__PURE__ */
|
|
1366
|
+
skipped > 0 ? /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
1367
|
+
/* @__PURE__ */ jsx29("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1368
|
+
/* @__PURE__ */ jsxs19("span", { children: [
|
|
1468
1369
|
skipped,
|
|
1469
1370
|
" skipped"
|
|
1470
1371
|
] })
|
|
@@ -1473,7 +1374,10 @@ function ReportFeature({ feature }) {
|
|
|
1473
1374
|
}
|
|
1474
1375
|
)
|
|
1475
1376
|
] }),
|
|
1476
|
-
/* @__PURE__ */
|
|
1377
|
+
/* @__PURE__ */ jsxs19("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
|
|
1378
|
+
/* @__PURE__ */ jsx29(ReportFeatureIntro, { feature }),
|
|
1379
|
+
/* @__PURE__ */ jsx29(ReportScenarioList, { feature })
|
|
1380
|
+
] })
|
|
1477
1381
|
]
|
|
1478
1382
|
}
|
|
1479
1383
|
)
|
|
@@ -1481,17 +1385,17 @@ function ReportFeature({ feature }) {
|
|
|
1481
1385
|
}
|
|
1482
1386
|
|
|
1483
1387
|
// src/components/ReportFeatureList.tsx
|
|
1484
|
-
import { jsx as
|
|
1388
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
1485
1389
|
function ReportFeatureList() {
|
|
1486
1390
|
const report = useReport();
|
|
1487
|
-
return /* @__PURE__ */
|
|
1391
|
+
return /* @__PURE__ */ jsx30("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx30(ReportFeature, { feature }, feature.id)) });
|
|
1488
1392
|
}
|
|
1489
1393
|
|
|
1490
1394
|
// src/components/ui/empty.tsx
|
|
1491
1395
|
import { cva as cva2 } from "class-variance-authority";
|
|
1492
|
-
import { jsx as
|
|
1396
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1493
1397
|
function Empty({ className, ...props }) {
|
|
1494
|
-
return /* @__PURE__ */
|
|
1398
|
+
return /* @__PURE__ */ jsx31(
|
|
1495
1399
|
"div",
|
|
1496
1400
|
{
|
|
1497
1401
|
"data-slot": "empty",
|
|
@@ -1504,7 +1408,7 @@ function Empty({ className, ...props }) {
|
|
|
1504
1408
|
);
|
|
1505
1409
|
}
|
|
1506
1410
|
function EmptyHeader({ className, ...props }) {
|
|
1507
|
-
return /* @__PURE__ */
|
|
1411
|
+
return /* @__PURE__ */ jsx31(
|
|
1508
1412
|
"div",
|
|
1509
1413
|
{
|
|
1510
1414
|
"data-slot": "empty-header",
|
|
@@ -1531,7 +1435,7 @@ var emptyMediaVariants = cva2(
|
|
|
1531
1435
|
}
|
|
1532
1436
|
);
|
|
1533
1437
|
function EmptyTitle({ className, ...props }) {
|
|
1534
|
-
return /* @__PURE__ */
|
|
1438
|
+
return /* @__PURE__ */ jsx31(
|
|
1535
1439
|
"div",
|
|
1536
1440
|
{
|
|
1537
1441
|
"data-slot": "empty-title",
|
|
@@ -1542,16 +1446,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1542
1446
|
}
|
|
1543
1447
|
|
|
1544
1448
|
// src/components/ReportEmpty.tsx
|
|
1545
|
-
import { jsx as
|
|
1449
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
1546
1450
|
function ReportEmpty({ message }) {
|
|
1547
|
-
return /* @__PURE__ */
|
|
1451
|
+
return /* @__PURE__ */ jsx32(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx32(EmptyHeader, { children: /* @__PURE__ */ jsx32(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1548
1452
|
}
|
|
1549
1453
|
|
|
1550
1454
|
// src/components/ReportSummary.tsx
|
|
1551
|
-
import { jsx as
|
|
1455
|
+
import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1552
1456
|
function ReportSummary({ className }) {
|
|
1553
1457
|
const report = useReport();
|
|
1554
|
-
return /* @__PURE__ */
|
|
1458
|
+
return /* @__PURE__ */ jsx33(
|
|
1555
1459
|
ReportSummaryView,
|
|
1556
1460
|
{
|
|
1557
1461
|
summary: report.summary,
|
|
@@ -1577,7 +1481,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1577
1481
|
if (summary.pending > 0) {
|
|
1578
1482
|
cards.push({ status: "pending", label: "Pending", value: summary.pending });
|
|
1579
1483
|
}
|
|
1580
|
-
return /* @__PURE__ */
|
|
1484
|
+
return /* @__PURE__ */ jsx33(
|
|
1581
1485
|
"dl",
|
|
1582
1486
|
{
|
|
1583
1487
|
className: cn(
|
|
@@ -1590,17 +1494,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1590
1494
|
"aria-label": ariaLabel,
|
|
1591
1495
|
children: cards.map((c) => {
|
|
1592
1496
|
const tone = TONE[c.status];
|
|
1593
|
-
return /* @__PURE__ */
|
|
1497
|
+
return /* @__PURE__ */ jsxs20(
|
|
1594
1498
|
"div",
|
|
1595
1499
|
{
|
|
1596
1500
|
"data-status": c.status,
|
|
1597
1501
|
className: "rounded-lg border bg-card px-4 py-3",
|
|
1598
1502
|
children: [
|
|
1599
|
-
/* @__PURE__ */
|
|
1600
|
-
/* @__PURE__ */
|
|
1503
|
+
/* @__PURE__ */ jsxs20("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
1504
|
+
/* @__PURE__ */ jsx33("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
|
|
1601
1505
|
c.label
|
|
1602
1506
|
] }),
|
|
1603
|
-
/* @__PURE__ */
|
|
1507
|
+
/* @__PURE__ */ jsx33("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
|
|
1604
1508
|
]
|
|
1605
1509
|
},
|
|
1606
1510
|
c.status
|
|
@@ -1610,6 +1514,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1610
1514
|
);
|
|
1611
1515
|
}
|
|
1612
1516
|
|
|
1517
|
+
// src/components/ReportMeta.tsx
|
|
1518
|
+
import { formatDuration as formatDuration4 } from "executable-stories-core/utils/duration";
|
|
1519
|
+
|
|
1613
1520
|
// src/lib/provenance.ts
|
|
1614
1521
|
var CI_DISPLAY_NAMES = {
|
|
1615
1522
|
github: "GitHub Actions",
|
|
@@ -1641,7 +1548,7 @@ function prUrl(ci) {
|
|
|
1641
1548
|
}
|
|
1642
1549
|
|
|
1643
1550
|
// src/components/ReportMeta.tsx
|
|
1644
|
-
import { jsx as
|
|
1551
|
+
import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1645
1552
|
function ReportMeta() {
|
|
1646
1553
|
const report = useReport();
|
|
1647
1554
|
const items = [];
|
|
@@ -1650,7 +1557,7 @@ function ReportMeta() {
|
|
|
1650
1557
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1651
1558
|
}
|
|
1652
1559
|
if (report.durationMs) {
|
|
1653
|
-
items.push({ label: "Duration", value:
|
|
1560
|
+
items.push({ label: "Duration", value: formatDuration4(report.durationMs) });
|
|
1654
1561
|
}
|
|
1655
1562
|
if (report.packageVersion) {
|
|
1656
1563
|
items.push({ label: "Version", value: report.packageVersion });
|
|
@@ -1664,13 +1571,13 @@ function ReportMeta() {
|
|
|
1664
1571
|
const short = sha.slice(0, 8);
|
|
1665
1572
|
items.push({
|
|
1666
1573
|
label: "Commit",
|
|
1667
|
-
value: url ? /* @__PURE__ */
|
|
1574
|
+
value: url ? /* @__PURE__ */ jsx34("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1668
1575
|
});
|
|
1669
1576
|
}
|
|
1670
1577
|
if (report.ci) {
|
|
1671
1578
|
items.push({
|
|
1672
1579
|
label: "CI",
|
|
1673
|
-
value: report.ci.url ? /* @__PURE__ */
|
|
1580
|
+
value: report.ci.url ? /* @__PURE__ */ jsxs21("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1674
1581
|
ciDisplayName(report.ci.name),
|
|
1675
1582
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1676
1583
|
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
@@ -1681,7 +1588,7 @@ function ReportMeta() {
|
|
|
1681
1588
|
const label = `#${report.ci.prNumber}`;
|
|
1682
1589
|
items.push({
|
|
1683
1590
|
label: "PR",
|
|
1684
|
-
value: url ? /* @__PURE__ */
|
|
1591
|
+
value: url ? /* @__PURE__ */ jsx34("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1685
1592
|
});
|
|
1686
1593
|
}
|
|
1687
1594
|
if (items.length === 0) return null;
|
|
@@ -1689,48 +1596,48 @@ function ReportMeta() {
|
|
|
1689
1596
|
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1690
1597
|
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1691
1598
|
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1692
|
-
/* @__PURE__ */
|
|
1693
|
-
/* @__PURE__ */
|
|
1694
|
-
/* @__PURE__ */
|
|
1599
|
+
/* @__PURE__ */ jsxs21("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1600
|
+
/* @__PURE__ */ jsxs21("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: [
|
|
1601
|
+
/* @__PURE__ */ jsx34("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1695
1602
|
"Run details"
|
|
1696
1603
|
] }),
|
|
1697
|
-
/* @__PURE__ */
|
|
1698
|
-
/* @__PURE__ */
|
|
1699
|
-
/* @__PURE__ */
|
|
1604
|
+
/* @__PURE__ */ jsx34("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__ */ jsxs21("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1605
|
+
/* @__PURE__ */ jsx34("dt", { className: "font-medium", children: item.label }),
|
|
1606
|
+
/* @__PURE__ */ jsx34("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1700
1607
|
] }, item.label)) })
|
|
1701
1608
|
] })
|
|
1702
1609
|
);
|
|
1703
1610
|
}
|
|
1704
1611
|
|
|
1705
1612
|
// src/components/ReportSchemaError.tsx
|
|
1706
|
-
import { jsx as
|
|
1613
|
+
import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1707
1614
|
function ReportSchemaError({ error }) {
|
|
1708
|
-
return /* @__PURE__ */
|
|
1709
|
-
/* @__PURE__ */
|
|
1710
|
-
/* @__PURE__ */
|
|
1711
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */
|
|
1615
|
+
return /* @__PURE__ */ jsxs22("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1616
|
+
/* @__PURE__ */ jsx35("p", { children: /* @__PURE__ */ jsx35("strong", { children: "Report could not be displayed." }) }),
|
|
1617
|
+
/* @__PURE__ */ jsx35("p", { children: error.message }),
|
|
1618
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs22("p", { children: [
|
|
1712
1619
|
"The report bundle is newer than this version of ",
|
|
1713
|
-
/* @__PURE__ */
|
|
1620
|
+
/* @__PURE__ */ jsx35("code", { children: "executable-stories-react" }),
|
|
1714
1621
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1715
1622
|
] }) : null,
|
|
1716
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */
|
|
1717
|
-
/* @__PURE__ */
|
|
1623
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs22("details", { children: [
|
|
1624
|
+
/* @__PURE__ */ jsxs22("summary", { children: [
|
|
1718
1625
|
error.issues.length,
|
|
1719
1626
|
" validation issue",
|
|
1720
1627
|
error.issues.length === 1 ? "" : "s"
|
|
1721
1628
|
] }),
|
|
1722
|
-
/* @__PURE__ */
|
|
1629
|
+
/* @__PURE__ */ jsx35("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1723
1630
|
] }) : null
|
|
1724
1631
|
] });
|
|
1725
1632
|
}
|
|
1726
1633
|
|
|
1727
1634
|
// src/components/ReportShell.tsx
|
|
1728
|
-
import { Fragment as Fragment10, jsx as
|
|
1635
|
+
import { Fragment as Fragment10, jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1729
1636
|
function ReportTitleBlock({ title }) {
|
|
1730
|
-
return /* @__PURE__ */
|
|
1731
|
-
/* @__PURE__ */
|
|
1732
|
-
/* @__PURE__ */
|
|
1733
|
-
/* @__PURE__ */
|
|
1637
|
+
return /* @__PURE__ */ jsxs23(Fragment10, { children: [
|
|
1638
|
+
/* @__PURE__ */ jsx36("h1", { children: title ?? "Story Report" }),
|
|
1639
|
+
/* @__PURE__ */ jsx36(ReportSummary, {}),
|
|
1640
|
+
/* @__PURE__ */ jsx36(ReportMeta, {})
|
|
1734
1641
|
] });
|
|
1735
1642
|
}
|
|
1736
1643
|
function ReportErrorShell({
|
|
@@ -1739,22 +1646,22 @@ function ReportErrorShell({
|
|
|
1739
1646
|
title,
|
|
1740
1647
|
dataTheme
|
|
1741
1648
|
}) {
|
|
1742
|
-
return /* @__PURE__ */
|
|
1649
|
+
return /* @__PURE__ */ jsx36("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx36(ReportSchemaError, { error }) });
|
|
1743
1650
|
}
|
|
1744
1651
|
|
|
1745
1652
|
// src/components/Report.tsx
|
|
1746
|
-
import { jsx as
|
|
1653
|
+
import { jsx as jsx37, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1747
1654
|
function Report(props) {
|
|
1748
1655
|
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1749
1656
|
const result = unwrapReport(report);
|
|
1750
1657
|
if (!result.ok) {
|
|
1751
|
-
return /* @__PURE__ */
|
|
1658
|
+
return /* @__PURE__ */ jsx37(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1752
1659
|
}
|
|
1753
1660
|
const data = result.data;
|
|
1754
1661
|
const hasContent = data.features.length > 0;
|
|
1755
|
-
return /* @__PURE__ */
|
|
1756
|
-
/* @__PURE__ */
|
|
1757
|
-
hasContent ? /* @__PURE__ */
|
|
1662
|
+
return /* @__PURE__ */ jsx37(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs24("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
|
|
1663
|
+
/* @__PURE__ */ jsx37("header", { className: "es-report-header", children: /* @__PURE__ */ jsx37(ReportTitleBlock, { title }) }),
|
|
1664
|
+
hasContent ? /* @__PURE__ */ jsx37(ReportFeatureList, {}) : /* @__PURE__ */ jsx37(ReportEmpty, {})
|
|
1758
1665
|
] }) });
|
|
1759
1666
|
}
|
|
1760
1667
|
|