executable-stories-react 0.2.0 → 0.3.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 +369 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +307 -9
- package/dist/index.d.ts +307 -9
- package/dist/index.js +353 -145
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +557 -239
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +269 -2
- package/dist/interactive.d.ts +269 -2
- package/dist/interactive.js +518 -201
- package/dist/interactive.js.map +1 -1
- package/dist/parse.cjs +4 -0
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +225 -3
- package/dist/parse.d.ts +225 -3
- package/dist/parse.js +4 -0
- package/dist/parse.js.map +1 -1
- package/dist/report-island.global.js +32 -32
- package/dist/ssr.cjs +299 -118
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.d.cts +250 -2
- package/dist/ssr.d.ts +250 -2
- package/dist/ssr.js +296 -115
- package/dist/ssr.js.map +1 -1
- package/dist/styles.css +26 -0
- package/dist/tailwind.css +1 -1
- package/package.json +4 -4
package/dist/interactive.cjs
CHANGED
|
@@ -22,6 +22,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var interactive_exports = {};
|
|
23
23
|
__export(interactive_exports, {
|
|
24
24
|
ReportFailureBanner: () => ReportFailureBanner,
|
|
25
|
+
ReportFreshness: () => ReportFreshness,
|
|
25
26
|
ReportInteractive: () => ReportInteractive,
|
|
26
27
|
ReportInteractiveIsland: () => ReportInteractiveIsland,
|
|
27
28
|
ReportSearch: () => ReportSearch,
|
|
@@ -36,7 +37,7 @@ __export(interactive_exports, {
|
|
|
36
37
|
module.exports = __toCommonJS(interactive_exports);
|
|
37
38
|
|
|
38
39
|
// src/interactive/ReportInteractive.tsx
|
|
39
|
-
var
|
|
40
|
+
var import_react16 = require("react");
|
|
40
41
|
|
|
41
42
|
// src/result.ts
|
|
42
43
|
var ok = (data) => ({ ok: true, data });
|
|
@@ -87,11 +88,23 @@ function useReport() {
|
|
|
87
88
|
return ctx.report;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
// ../executable-stories-core/dist/utils/duration.js
|
|
92
|
+
function formatDuration(ms) {
|
|
93
|
+
if (ms < 1e3) {
|
|
94
|
+
return `${Math.round(ms)} ms`;
|
|
95
|
+
}
|
|
96
|
+
if (ms < 6e4) {
|
|
97
|
+
return `${(ms / 1e3).toFixed(2)} s`;
|
|
98
|
+
}
|
|
99
|
+
const minutes = Math.floor(ms / 6e4);
|
|
100
|
+
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
101
|
+
if (minutes < 60) {
|
|
102
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
103
|
+
}
|
|
104
|
+
const hours = Math.floor(minutes / 60);
|
|
105
|
+
const remainingMinutes = minutes % 60;
|
|
106
|
+
return `${hours}h ${remainingMinutes}m`;
|
|
107
|
+
}
|
|
95
108
|
|
|
96
109
|
// src/components/doc/DocNote.tsx
|
|
97
110
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -245,6 +258,15 @@ function safeUrl(value) {
|
|
|
245
258
|
if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
|
|
246
259
|
return trimmed;
|
|
247
260
|
}
|
|
261
|
+
function safeImageUrl(value) {
|
|
262
|
+
if (!value) return void 0;
|
|
263
|
+
const trimmed = value.trim();
|
|
264
|
+
if (/^data:image\//i.test(trimmed)) return trimmed;
|
|
265
|
+
return safeUrl(trimmed);
|
|
266
|
+
}
|
|
267
|
+
function isLocalFsPath(value) {
|
|
268
|
+
return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
|
|
269
|
+
}
|
|
248
270
|
|
|
249
271
|
// src/components/doc/DocSection.tsx
|
|
250
272
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
@@ -300,11 +322,19 @@ function DocMermaid({ entry }) {
|
|
|
300
322
|
// src/components/doc/DocScreenshot.tsx
|
|
301
323
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
302
324
|
function DocScreenshot({ entry }) {
|
|
325
|
+
const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
|
|
326
|
+
if (!src) {
|
|
327
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
328
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
330
|
+
entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
|
|
331
|
+
] });
|
|
332
|
+
}
|
|
303
333
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3", children: [
|
|
304
334
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
305
335
|
"img",
|
|
306
336
|
{
|
|
307
|
-
src
|
|
337
|
+
src,
|
|
308
338
|
alt: entry.alt ?? "",
|
|
309
339
|
loading: "lazy",
|
|
310
340
|
className: "max-w-full rounded-md border border-border"
|
|
@@ -317,6 +347,13 @@ function DocScreenshot({ entry }) {
|
|
|
317
347
|
// src/components/doc/DocVideo.tsx
|
|
318
348
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
319
349
|
function DocVideo({ entry }) {
|
|
350
|
+
if (isLocalFsPath(entry.path)) {
|
|
351
|
+
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: [
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
|
|
353
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
354
|
+
entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
|
|
355
|
+
] });
|
|
356
|
+
}
|
|
320
357
|
const src = safeUrl(entry.path);
|
|
321
358
|
const poster = safeUrl(entry.poster);
|
|
322
359
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3", children: [
|
|
@@ -492,7 +529,7 @@ function ReportStepItem({ step }) {
|
|
|
492
529
|
}
|
|
493
530
|
),
|
|
494
531
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
495
|
-
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children:
|
|
532
|
+
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
|
|
496
533
|
] }),
|
|
497
534
|
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
498
535
|
"pre",
|
|
@@ -550,9 +587,6 @@ function ReportAttachments({ attachments }) {
|
|
|
550
587
|
);
|
|
551
588
|
}
|
|
552
589
|
|
|
553
|
-
// src/components/ReportTrace.tsx
|
|
554
|
-
var import_duration2 = require("executable-stories-core/utils/duration");
|
|
555
|
-
|
|
556
590
|
// src/lib/trace.ts
|
|
557
591
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
558
592
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -647,7 +681,7 @@ function ReportTrace({ spans }) {
|
|
|
647
681
|
" span",
|
|
648
682
|
rows.length === 1 ? "" : "s",
|
|
649
683
|
", ",
|
|
650
|
-
|
|
684
|
+
formatDuration(totalMs),
|
|
651
685
|
")"
|
|
652
686
|
] }),
|
|
653
687
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
|
|
@@ -670,18 +704,131 @@ function ReportTrace({ spans }) {
|
|
|
670
704
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
671
705
|
}
|
|
672
706
|
) }),
|
|
673
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children:
|
|
707
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
|
|
674
708
|
] }, `${row.spanId}-${i}`);
|
|
675
709
|
}) })
|
|
676
710
|
] });
|
|
677
711
|
}
|
|
678
712
|
|
|
679
|
-
// src/interactive/
|
|
713
|
+
// src/interactive/scenario-history-context.ts
|
|
680
714
|
var import_react5 = require("react");
|
|
681
|
-
var
|
|
715
|
+
var ScenarioHistoryContext = (0, import_react5.createContext)(null);
|
|
716
|
+
var ScenarioHistoryProvider = ScenarioHistoryContext.Provider;
|
|
717
|
+
function useScenarioHistory() {
|
|
718
|
+
return (0, import_react5.useContext)(ScenarioHistoryContext);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/lib/run-history.ts
|
|
722
|
+
function currentStreak(entries) {
|
|
723
|
+
const last = entries[entries.length - 1];
|
|
724
|
+
if (!last) return void 0;
|
|
725
|
+
let count = 0;
|
|
726
|
+
for (let i = entries.length - 1; i >= 0 && entries[i].status === last.status; i--) count++;
|
|
727
|
+
return { status: last.status, count };
|
|
728
|
+
}
|
|
729
|
+
var STREAK_VERB = {
|
|
730
|
+
passed: "Passing",
|
|
731
|
+
failed: "Failing",
|
|
732
|
+
skipped: "Skipped",
|
|
733
|
+
pending: "Pending"
|
|
734
|
+
};
|
|
735
|
+
function describeRunHistory(entries) {
|
|
736
|
+
const passed = entries.filter((e) => e.status === "passed").length;
|
|
737
|
+
const base = `${passed}/${entries.length} runs passed`;
|
|
738
|
+
const streak = currentStreak(entries);
|
|
739
|
+
if (!streak || entries.length < 2) return base;
|
|
740
|
+
return `${base} \xB7 ${STREAK_VERB[streak.status]} for the last ${streak.count === 1 ? "run" : `${streak.count} runs`}`;
|
|
741
|
+
}
|
|
742
|
+
var MIN_FLAKINESS_SAMPLES = 3;
|
|
743
|
+
function flakinessOf(entries) {
|
|
744
|
+
const countable = entries.filter((e) => e.status === "passed" || e.status === "failed");
|
|
745
|
+
if (countable.length < MIN_FLAKINESS_SAMPLES) return "stable";
|
|
746
|
+
let transitions = 0;
|
|
747
|
+
for (let i = 1; i < countable.length; i++) {
|
|
748
|
+
if (countable[i].status !== countable[i - 1].status) transitions++;
|
|
749
|
+
}
|
|
750
|
+
const transitionScore = transitions / (countable.length - 1);
|
|
751
|
+
const failureRate = countable.filter((e) => e.status === "failed").length / countable.length;
|
|
752
|
+
if (transitionScore > 0.5 || transitionScore > 0.3 && failureRate > 0.2) return "flaky";
|
|
753
|
+
if (transitionScore > 0.2 || failureRate > 0.3) return "unstable";
|
|
754
|
+
return "stable";
|
|
755
|
+
}
|
|
756
|
+
function diffSinceLastRun(history2, scenarios) {
|
|
757
|
+
let hasPreviousRun = false;
|
|
758
|
+
const delta = { newlyFailing: [], newlyPassing: [], added: [] };
|
|
759
|
+
for (const scenario of scenarios) {
|
|
760
|
+
const entries = history2[scenario.id];
|
|
761
|
+
if (!entries || entries.length === 0) continue;
|
|
762
|
+
const last = entries[entries.length - 1];
|
|
763
|
+
const prev = entries[entries.length - 2];
|
|
764
|
+
if (!prev) {
|
|
765
|
+
delta.added.push(scenario);
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
hasPreviousRun = true;
|
|
769
|
+
if (last.status === "failed" && prev.status !== "failed") delta.newlyFailing.push(scenario);
|
|
770
|
+
else if (last.status === "passed" && prev.status === "failed") delta.newlyPassing.push(scenario);
|
|
771
|
+
}
|
|
772
|
+
return hasPreviousRun ? delta : void 0;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// src/components/ScenarioRunHistory.tsx
|
|
776
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
777
|
+
var MAX_DOTS = 10;
|
|
778
|
+
var DOT_COLOR = {
|
|
779
|
+
passed: "bg-pass",
|
|
780
|
+
failed: "bg-fail",
|
|
781
|
+
skipped: "bg-skip",
|
|
782
|
+
pending: "bg-pend"
|
|
783
|
+
};
|
|
784
|
+
function dotTitle(e) {
|
|
785
|
+
const parts = [new Date(e.timestamp).toLocaleString(), e.status];
|
|
786
|
+
if (e.commitSha) parts.push(e.commitSha.slice(0, 8));
|
|
787
|
+
return parts.join(" \xB7 ");
|
|
788
|
+
}
|
|
789
|
+
function ScenarioRunHistory({ scenarioId }) {
|
|
790
|
+
const history2 = useScenarioHistory();
|
|
791
|
+
const entries = history2?.[scenarioId];
|
|
792
|
+
if (!entries || entries.length < 2) return null;
|
|
793
|
+
const recent = entries.slice(-MAX_DOTS);
|
|
794
|
+
const flaky = flakinessOf(recent) === "flaky";
|
|
795
|
+
const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
|
|
796
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
797
|
+
flaky ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
|
|
798
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
799
|
+
"span",
|
|
800
|
+
{
|
|
801
|
+
role: "img",
|
|
802
|
+
"aria-label": `Run history: ${summary}`,
|
|
803
|
+
title: summary,
|
|
804
|
+
className: "flex shrink-0 items-center gap-[3px]",
|
|
805
|
+
children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
806
|
+
"span",
|
|
807
|
+
{
|
|
808
|
+
"aria-hidden": true,
|
|
809
|
+
title: dotTitle(e),
|
|
810
|
+
className: cn(
|
|
811
|
+
"inline-block size-1.5 rounded-full",
|
|
812
|
+
DOT_COLOR[e.status],
|
|
813
|
+
// The newest run (rightmost dot) is the one the card's status
|
|
814
|
+
// badge already describes; ring it so the timeline reads as
|
|
815
|
+
// "…leading up to now".
|
|
816
|
+
i === recent.length - 1 && "ring-1 ring-border ring-offset-1 ring-offset-card"
|
|
817
|
+
)
|
|
818
|
+
},
|
|
819
|
+
`${e.timestamp}-${i}`
|
|
820
|
+
))
|
|
821
|
+
}
|
|
822
|
+
)
|
|
823
|
+
] });
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/interactive/collapse-context.ts
|
|
827
|
+
var import_react6 = require("react");
|
|
828
|
+
var CollapseContext = (0, import_react6.createContext)(null);
|
|
682
829
|
var CollapseProvider = CollapseContext.Provider;
|
|
683
830
|
function useCollapse() {
|
|
684
|
-
return (0,
|
|
831
|
+
return (0, import_react6.useContext)(CollapseContext);
|
|
685
832
|
}
|
|
686
833
|
var STORAGE_KEY = "es-collapsed-ids";
|
|
687
834
|
function readPersisted() {
|
|
@@ -701,9 +848,9 @@ function persist(ids) {
|
|
|
701
848
|
}
|
|
702
849
|
}
|
|
703
850
|
function useCollapseState() {
|
|
704
|
-
const [collapsed, setCollapsed] = (0,
|
|
705
|
-
const isCollapsed = (0,
|
|
706
|
-
const toggle = (0,
|
|
851
|
+
const [collapsed, setCollapsed] = (0, import_react6.useState)(readPersisted);
|
|
852
|
+
const isCollapsed = (0, import_react6.useCallback)((id) => collapsed.has(id), [collapsed]);
|
|
853
|
+
const toggle = (0, import_react6.useCallback)((id) => {
|
|
707
854
|
setCollapsed((prev) => {
|
|
708
855
|
const next = new Set(prev);
|
|
709
856
|
if (next.has(id)) next.delete(id);
|
|
@@ -712,14 +859,14 @@ function useCollapseState() {
|
|
|
712
859
|
return next;
|
|
713
860
|
});
|
|
714
861
|
}, []);
|
|
715
|
-
const collapseAll = (0,
|
|
862
|
+
const collapseAll = (0, import_react6.useCallback)((ids) => {
|
|
716
863
|
setCollapsed(() => {
|
|
717
864
|
const next = new Set(ids);
|
|
718
865
|
persist(next);
|
|
719
866
|
return next;
|
|
720
867
|
});
|
|
721
868
|
}, []);
|
|
722
|
-
const expandAll = (0,
|
|
869
|
+
const expandAll = (0, import_react6.useCallback)(() => {
|
|
723
870
|
setCollapsed(() => {
|
|
724
871
|
const next = /* @__PURE__ */ new Set();
|
|
725
872
|
persist(next);
|
|
@@ -730,11 +877,11 @@ function useCollapseState() {
|
|
|
730
877
|
}
|
|
731
878
|
|
|
732
879
|
// src/interactive/scenario-actions.tsx
|
|
733
|
-
var
|
|
734
|
-
var ScenarioActionsContext = (0,
|
|
880
|
+
var import_react7 = require("react");
|
|
881
|
+
var ScenarioActionsContext = (0, import_react7.createContext)(null);
|
|
735
882
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
736
883
|
function useScenarioActions() {
|
|
737
|
-
return (0,
|
|
884
|
+
return (0, import_react7.useContext)(ScenarioActionsContext);
|
|
738
885
|
}
|
|
739
886
|
function scenarioToMarkdown(scenario) {
|
|
740
887
|
const lines = [`## ${scenario.title} _(${scenario.status})_`, ""];
|
|
@@ -772,9 +919,9 @@ function scenarioPermalink(scenario) {
|
|
|
772
919
|
|
|
773
920
|
// src/components/ui/card.tsx
|
|
774
921
|
var React2 = require("react");
|
|
775
|
-
var
|
|
922
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
776
923
|
function Card({ className, ...props }) {
|
|
777
|
-
return /* @__PURE__ */ (0,
|
|
924
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
778
925
|
"div",
|
|
779
926
|
{
|
|
780
927
|
"data-slot": "card",
|
|
@@ -787,7 +934,7 @@ function Card({ className, ...props }) {
|
|
|
787
934
|
);
|
|
788
935
|
}
|
|
789
936
|
function CardHeader({ className, ...props }) {
|
|
790
|
-
return /* @__PURE__ */ (0,
|
|
937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
791
938
|
"div",
|
|
792
939
|
{
|
|
793
940
|
"data-slot": "card-header",
|
|
@@ -800,7 +947,7 @@ function CardHeader({ className, ...props }) {
|
|
|
800
947
|
);
|
|
801
948
|
}
|
|
802
949
|
function CardContent({ className, ...props }) {
|
|
803
|
-
return /* @__PURE__ */ (0,
|
|
950
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
804
951
|
"div",
|
|
805
952
|
{
|
|
806
953
|
"data-slot": "card-content",
|
|
@@ -811,7 +958,7 @@ function CardContent({ className, ...props }) {
|
|
|
811
958
|
}
|
|
812
959
|
|
|
813
960
|
// src/components/ReportScenario.tsx
|
|
814
|
-
var
|
|
961
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
815
962
|
var STATUS_LABEL = {
|
|
816
963
|
passed: "Passed",
|
|
817
964
|
failed: "Failed",
|
|
@@ -839,12 +986,12 @@ var STATUS_BADGE = {
|
|
|
839
986
|
function ReportScenario({ scenario, hideTitle = false }) {
|
|
840
987
|
const titleId = `${scenario.id}-title`;
|
|
841
988
|
const bodyId = `${scenario.id}-body`;
|
|
842
|
-
const label = STATUS_LABEL[scenario.status];
|
|
989
|
+
const label = scenario.planned ? "Planned" : STATUS_LABEL[scenario.status];
|
|
843
990
|
const collapse = useCollapse();
|
|
844
991
|
const collapsible = collapse !== null && !hideTitle;
|
|
845
992
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
846
993
|
const actions = useScenarioActions();
|
|
847
|
-
return /* @__PURE__ */ (0,
|
|
994
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
848
995
|
Card,
|
|
849
996
|
{
|
|
850
997
|
id: scenario.id,
|
|
@@ -852,10 +999,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
852
999
|
...hideTitle ? { "aria-label": `${scenario.title} \u2014 ${label}` } : { "aria-labelledby": titleId },
|
|
853
1000
|
className: "gap-0 py-0",
|
|
854
1001
|
children: [
|
|
855
|
-
/* @__PURE__ */ (0,
|
|
856
|
-
/* @__PURE__ */ (0,
|
|
857
|
-
hideTitle ? null : /* @__PURE__ */ (0,
|
|
858
|
-
collapsible ? /* @__PURE__ */ (0,
|
|
1002
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CardHeader, { className: "px-4 py-3", children: [
|
|
1003
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
1004
|
+
hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1005
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
859
1006
|
"button",
|
|
860
1007
|
{
|
|
861
1008
|
type: "button",
|
|
@@ -864,17 +1011,18 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
864
1011
|
"aria-controls": bodyId,
|
|
865
1012
|
"aria-label": `Toggle ${scenario.title}`,
|
|
866
1013
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
867
|
-
children: /* @__PURE__ */ (0,
|
|
1014
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
868
1015
|
}
|
|
869
1016
|
) : null,
|
|
870
|
-
/* @__PURE__ */ (0,
|
|
871
|
-
/* @__PURE__ */ (0,
|
|
1017
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", STATUS_GLYPH_COLOR[scenario.status]), children: STATUS_GLYPH[scenario.status] }),
|
|
1018
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: scenario.title })
|
|
872
1019
|
] }),
|
|
873
|
-
/* @__PURE__ */ (0,
|
|
874
|
-
|
|
875
|
-
/* @__PURE__ */ (0,
|
|
876
|
-
|
|
877
|
-
|
|
1020
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1021
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1022
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
|
|
1023
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: STATUS_BADGE[scenario.status], "aria-label": `Status: ${label}`, children: label }),
|
|
1024
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-0.5", children: [
|
|
1025
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
878
1026
|
"button",
|
|
879
1027
|
{
|
|
880
1028
|
type: "button",
|
|
@@ -885,7 +1033,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
885
1033
|
children: "link"
|
|
886
1034
|
}
|
|
887
1035
|
),
|
|
888
|
-
/* @__PURE__ */ (0,
|
|
1036
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
889
1037
|
"button",
|
|
890
1038
|
{
|
|
891
1039
|
type: "button",
|
|
@@ -896,7 +1044,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
896
1044
|
children: "md"
|
|
897
1045
|
}
|
|
898
1046
|
),
|
|
899
|
-
/* @__PURE__ */ (0,
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
900
1048
|
"button",
|
|
901
1049
|
{
|
|
902
1050
|
type: "button",
|
|
@@ -910,13 +1058,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
910
1058
|
] }) : null
|
|
911
1059
|
] })
|
|
912
1060
|
] }),
|
|
913
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0,
|
|
914
|
-
scenario.tags.map((t) => /* @__PURE__ */ (0,
|
|
915
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0,
|
|
1061
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
1062
|
+
scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
1063
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
916
1064
|
] }) : null
|
|
917
1065
|
] }),
|
|
918
|
-
/* @__PURE__ */ (0,
|
|
919
|
-
scenario.errorMessage ? /* @__PURE__ */ (0,
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1067
|
+
scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
920
1068
|
"pre",
|
|
921
1069
|
{
|
|
922
1070
|
role: "alert",
|
|
@@ -924,10 +1072,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
924
1072
|
children: scenario.errorMessage
|
|
925
1073
|
}
|
|
926
1074
|
) : null,
|
|
927
|
-
/* @__PURE__ */ (0,
|
|
928
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
929
|
-
/* @__PURE__ */ (0,
|
|
930
|
-
/* @__PURE__ */ (0,
|
|
1075
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportSteps, { scenario }),
|
|
1076
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportAttachments, { attachments: scenario.attachments }),
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportTrace, { spans: scenario.otelSpans })
|
|
931
1079
|
] })
|
|
932
1080
|
]
|
|
933
1081
|
}
|
|
@@ -935,13 +1083,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
935
1083
|
}
|
|
936
1084
|
|
|
937
1085
|
// src/components/ReportScenarioList.tsx
|
|
938
|
-
var
|
|
1086
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
939
1087
|
function ReportScenarioList({ feature }) {
|
|
940
|
-
return /* @__PURE__ */ (0,
|
|
1088
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportScenario, { scenario }, scenario.id)) });
|
|
941
1089
|
}
|
|
942
1090
|
|
|
943
1091
|
// src/components/ReportFeature.tsx
|
|
944
|
-
var
|
|
1092
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
945
1093
|
function ReportFeature({ feature }) {
|
|
946
1094
|
const titleId = `${feature.id}-title`;
|
|
947
1095
|
const bodyId = `${feature.id}-body`;
|
|
@@ -949,7 +1097,7 @@ function ReportFeature({ feature }) {
|
|
|
949
1097
|
const skipped = s.skipped + s.pending;
|
|
950
1098
|
const collapse = useCollapse();
|
|
951
1099
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
952
|
-
return /* @__PURE__ */ (0,
|
|
1100
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
953
1101
|
"section",
|
|
954
1102
|
{
|
|
955
1103
|
id: feature.id,
|
|
@@ -957,9 +1105,9 @@ function ReportFeature({ feature }) {
|
|
|
957
1105
|
"aria-labelledby": titleId,
|
|
958
1106
|
className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm",
|
|
959
1107
|
children: [
|
|
960
|
-
/* @__PURE__ */ (0,
|
|
961
|
-
/* @__PURE__ */ (0,
|
|
962
|
-
collapse ? /* @__PURE__ */ (0,
|
|
1108
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center justify-between gap-4 border-b border-border bg-muted/40 px-4 py-3", children: [
|
|
1109
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1110
|
+
collapse ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
963
1111
|
"button",
|
|
964
1112
|
{
|
|
965
1113
|
type: "button",
|
|
@@ -968,30 +1116,30 @@ function ReportFeature({ feature }) {
|
|
|
968
1116
|
"aria-controls": bodyId,
|
|
969
1117
|
"aria-label": `Toggle ${feature.title}`,
|
|
970
1118
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
971
|
-
children: /* @__PURE__ */ (0,
|
|
1119
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
972
1120
|
}
|
|
973
1121
|
) : null,
|
|
974
|
-
/* @__PURE__ */ (0,
|
|
975
|
-
/* @__PURE__ */ (0,
|
|
976
|
-
/* @__PURE__ */ (0,
|
|
1122
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "min-w-0", children: [
|
|
1123
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { id: titleId, className: "text-sm font-semibold tracking-tight text-foreground", children: feature.title }),
|
|
1124
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mt-0.5 truncate font-mono text-xs text-muted-foreground", children: feature.sourceFile })
|
|
977
1125
|
] })
|
|
978
1126
|
] }),
|
|
979
|
-
/* @__PURE__ */ (0,
|
|
1127
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
980
1128
|
"div",
|
|
981
1129
|
{
|
|
982
1130
|
role: "img",
|
|
983
1131
|
className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
|
|
984
1132
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
985
1133
|
children: [
|
|
986
|
-
/* @__PURE__ */ (0,
|
|
1134
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-pass", children: [
|
|
987
1135
|
"\u2713",
|
|
988
1136
|
s.passed
|
|
989
1137
|
] }),
|
|
990
|
-
/* @__PURE__ */ (0,
|
|
1138
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-fail", children: [
|
|
991
1139
|
"\u2717",
|
|
992
1140
|
s.failed
|
|
993
1141
|
] }),
|
|
994
|
-
/* @__PURE__ */ (0,
|
|
1142
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-skip", children: [
|
|
995
1143
|
"\u25CB",
|
|
996
1144
|
skipped
|
|
997
1145
|
] })
|
|
@@ -999,24 +1147,24 @@ function ReportFeature({ feature }) {
|
|
|
999
1147
|
}
|
|
1000
1148
|
)
|
|
1001
1149
|
] }),
|
|
1002
|
-
/* @__PURE__ */ (0,
|
|
1150
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-2 p-3", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportScenarioList, { feature }) })
|
|
1003
1151
|
]
|
|
1004
1152
|
}
|
|
1005
1153
|
);
|
|
1006
1154
|
}
|
|
1007
1155
|
|
|
1008
1156
|
// src/components/ReportFeatureList.tsx
|
|
1009
|
-
var
|
|
1157
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1010
1158
|
function ReportFeatureList() {
|
|
1011
1159
|
const report = useReport();
|
|
1012
|
-
return /* @__PURE__ */ (0,
|
|
1160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportFeature, { feature }, feature.id)) });
|
|
1013
1161
|
}
|
|
1014
1162
|
|
|
1015
1163
|
// src/components/ui/empty.tsx
|
|
1016
1164
|
var import_class_variance_authority2 = require("class-variance-authority");
|
|
1017
|
-
var
|
|
1165
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1018
1166
|
function Empty({ className, ...props }) {
|
|
1019
|
-
return /* @__PURE__ */ (0,
|
|
1167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1020
1168
|
"div",
|
|
1021
1169
|
{
|
|
1022
1170
|
"data-slot": "empty",
|
|
@@ -1029,7 +1177,7 @@ function Empty({ className, ...props }) {
|
|
|
1029
1177
|
);
|
|
1030
1178
|
}
|
|
1031
1179
|
function EmptyHeader({ className, ...props }) {
|
|
1032
|
-
return /* @__PURE__ */ (0,
|
|
1180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1033
1181
|
"div",
|
|
1034
1182
|
{
|
|
1035
1183
|
"data-slot": "empty-header",
|
|
@@ -1056,7 +1204,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
|
1056
1204
|
}
|
|
1057
1205
|
);
|
|
1058
1206
|
function EmptyTitle({ className, ...props }) {
|
|
1059
|
-
return /* @__PURE__ */ (0,
|
|
1207
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1060
1208
|
"div",
|
|
1061
1209
|
{
|
|
1062
1210
|
"data-slot": "empty-title",
|
|
@@ -1067,16 +1215,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1067
1215
|
}
|
|
1068
1216
|
|
|
1069
1217
|
// src/components/ReportEmpty.tsx
|
|
1070
|
-
var
|
|
1218
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1071
1219
|
function ReportEmpty({ message }) {
|
|
1072
|
-
return /* @__PURE__ */ (0,
|
|
1220
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1073
1221
|
}
|
|
1074
1222
|
|
|
1075
1223
|
// src/components/ReportSummary.tsx
|
|
1076
|
-
var
|
|
1224
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1077
1225
|
function ReportSummary({ className }) {
|
|
1078
1226
|
const report = useReport();
|
|
1079
|
-
return /* @__PURE__ */ (0,
|
|
1227
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1080
1228
|
ReportSummaryView,
|
|
1081
1229
|
{
|
|
1082
1230
|
summary: report.summary,
|
|
@@ -1086,32 +1234,32 @@ function ReportSummary({ className }) {
|
|
|
1086
1234
|
);
|
|
1087
1235
|
}
|
|
1088
1236
|
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
1089
|
-
return /* @__PURE__ */ (0,
|
|
1090
|
-
/* @__PURE__ */ (0,
|
|
1091
|
-
/* @__PURE__ */ (0,
|
|
1237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: cn("text-sm text-muted-foreground", className), "aria-label": ariaLabel, children: [
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
|
|
1239
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("strong", { className: "font-semibold text-foreground", children: summary.total }),
|
|
1092
1240
|
" scenario",
|
|
1093
1241
|
summary.total === 1 ? "" : "s"
|
|
1094
1242
|
] }),
|
|
1095
1243
|
" \xB7 ",
|
|
1096
|
-
/* @__PURE__ */ (0,
|
|
1244
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "passed", className: "text-pass", children: [
|
|
1097
1245
|
summary.passed,
|
|
1098
1246
|
" passed"
|
|
1099
1247
|
] }),
|
|
1100
1248
|
" \xB7 ",
|
|
1101
|
-
/* @__PURE__ */ (0,
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "failed", className: "text-fail", children: [
|
|
1102
1250
|
summary.failed,
|
|
1103
1251
|
" failed"
|
|
1104
1252
|
] }),
|
|
1105
|
-
summary.skipped > 0 ? /* @__PURE__ */ (0,
|
|
1253
|
+
summary.skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
1106
1254
|
" \xB7 ",
|
|
1107
|
-
/* @__PURE__ */ (0,
|
|
1255
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "skipped", className: "text-skip", children: [
|
|
1108
1256
|
summary.skipped,
|
|
1109
1257
|
" skipped"
|
|
1110
1258
|
] })
|
|
1111
1259
|
] }) : null,
|
|
1112
|
-
summary.pending > 0 ? /* @__PURE__ */ (0,
|
|
1260
|
+
summary.pending > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
1113
1261
|
" \xB7 ",
|
|
1114
|
-
/* @__PURE__ */ (0,
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "pending", className: "text-pend", children: [
|
|
1115
1263
|
summary.pending,
|
|
1116
1264
|
" pending"
|
|
1117
1265
|
] })
|
|
@@ -1119,69 +1267,135 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1119
1267
|
] });
|
|
1120
1268
|
}
|
|
1121
1269
|
|
|
1270
|
+
// src/lib/provenance.ts
|
|
1271
|
+
function reportLastRunMs(report) {
|
|
1272
|
+
if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
|
|
1273
|
+
if (report.startedAtMs && report.startedAtMs > 0) return report.startedAtMs;
|
|
1274
|
+
return void 0;
|
|
1275
|
+
}
|
|
1276
|
+
function formatRelativeAge(thenMs, nowMs) {
|
|
1277
|
+
const deltaMs = Math.max(0, nowMs - thenMs);
|
|
1278
|
+
const minutes = Math.floor(deltaMs / 6e4);
|
|
1279
|
+
if (minutes < 1) return "just now";
|
|
1280
|
+
if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
|
|
1281
|
+
const hours = Math.floor(minutes / 60);
|
|
1282
|
+
if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
|
|
1283
|
+
const days = Math.floor(hours / 24);
|
|
1284
|
+
return `${days} day${days === 1 ? "" : "s"} ago`;
|
|
1285
|
+
}
|
|
1286
|
+
function isReportStale(lastRunMs, staleAfterDays, nowMs) {
|
|
1287
|
+
if (lastRunMs === void 0 || staleAfterDays <= 0) return false;
|
|
1288
|
+
return nowMs - lastRunMs >= staleAfterDays * 864e5;
|
|
1289
|
+
}
|
|
1290
|
+
var CI_DISPLAY_NAMES = {
|
|
1291
|
+
github: "GitHub Actions",
|
|
1292
|
+
gitlab: "GitLab CI",
|
|
1293
|
+
circleci: "CircleCI",
|
|
1294
|
+
jenkins: "Jenkins",
|
|
1295
|
+
azure: "Azure DevOps",
|
|
1296
|
+
buildkite: "Buildkite",
|
|
1297
|
+
travis: "Travis CI",
|
|
1298
|
+
ci: "CI"
|
|
1299
|
+
};
|
|
1300
|
+
function ciDisplayName(name) {
|
|
1301
|
+
return CI_DISPLAY_NAMES[name] ?? name;
|
|
1302
|
+
}
|
|
1303
|
+
function commitUrl(ci, sha) {
|
|
1304
|
+
const commitSha = ci?.commitSha ?? sha;
|
|
1305
|
+
if (!ci?.url || !commitSha) return void 0;
|
|
1306
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1307
|
+
if (github) return `${github[1]}/commit/${commitSha}`;
|
|
1308
|
+
const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
|
|
1309
|
+
if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
|
|
1310
|
+
return void 0;
|
|
1311
|
+
}
|
|
1312
|
+
function prUrl(ci) {
|
|
1313
|
+
if (!ci?.url || !ci.prNumber) return void 0;
|
|
1314
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1315
|
+
if (github) return `${github[1]}/pull/${ci.prNumber}`;
|
|
1316
|
+
return void 0;
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1122
1319
|
// src/components/ReportMeta.tsx
|
|
1123
|
-
var
|
|
1124
|
-
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1320
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1125
1321
|
function ReportMeta() {
|
|
1126
1322
|
const report = useReport();
|
|
1127
1323
|
const items = [];
|
|
1324
|
+
const linkClass = "text-primary underline underline-offset-2";
|
|
1128
1325
|
if (report.startedAtMs) {
|
|
1129
1326
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1130
1327
|
}
|
|
1131
1328
|
if (report.durationMs) {
|
|
1132
|
-
items.push({ label: "Duration", value:
|
|
1329
|
+
items.push({ label: "Duration", value: formatDuration(report.durationMs) });
|
|
1133
1330
|
}
|
|
1134
1331
|
if (report.packageVersion) {
|
|
1135
1332
|
items.push({ label: "Version", value: report.packageVersion });
|
|
1136
1333
|
}
|
|
1137
|
-
if (report.
|
|
1138
|
-
items.push({ label: "
|
|
1334
|
+
if (report.ci?.branch) {
|
|
1335
|
+
items.push({ label: "Branch", value: report.ci.branch });
|
|
1336
|
+
}
|
|
1337
|
+
const sha = report.ci?.commitSha ?? report.gitSha;
|
|
1338
|
+
if (sha) {
|
|
1339
|
+
const url = commitUrl(report.ci, report.gitSha);
|
|
1340
|
+
const short = sha.slice(0, 8);
|
|
1341
|
+
items.push({
|
|
1342
|
+
label: "Commit",
|
|
1343
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1344
|
+
});
|
|
1139
1345
|
}
|
|
1140
1346
|
if (report.ci) {
|
|
1141
1347
|
items.push({
|
|
1142
1348
|
label: "CI",
|
|
1143
|
-
value: report.ci.url ? /* @__PURE__ */ (0,
|
|
1144
|
-
report.ci.name,
|
|
1349
|
+
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1350
|
+
ciDisplayName(report.ci.name),
|
|
1145
1351
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1146
|
-
] }) : `${report.ci.name}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1352
|
+
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
if (report.ci?.prNumber) {
|
|
1356
|
+
const url = prUrl(report.ci);
|
|
1357
|
+
const label = `#${report.ci.prNumber}`;
|
|
1358
|
+
items.push({
|
|
1359
|
+
label: "PR",
|
|
1360
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1147
1361
|
});
|
|
1148
1362
|
}
|
|
1149
1363
|
if (items.length === 0) return null;
|
|
1150
|
-
return /* @__PURE__ */ (0,
|
|
1151
|
-
/* @__PURE__ */ (0,
|
|
1152
|
-
/* @__PURE__ */ (0,
|
|
1364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dl", { "aria-label": "Run metadata", className: "flex flex-wrap gap-x-6 gap-y-1 text-xs text-muted-foreground", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex gap-1.5", children: [
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1153
1367
|
] }, item.label)) });
|
|
1154
1368
|
}
|
|
1155
1369
|
|
|
1156
1370
|
// src/components/ReportSchemaError.tsx
|
|
1157
|
-
var
|
|
1371
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1158
1372
|
function ReportSchemaError({ error }) {
|
|
1159
|
-
return /* @__PURE__ */ (0,
|
|
1160
|
-
/* @__PURE__ */ (0,
|
|
1161
|
-
/* @__PURE__ */ (0,
|
|
1162
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0,
|
|
1373
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1374
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: error.message }),
|
|
1376
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { children: [
|
|
1163
1377
|
"The report bundle is newer than this version of ",
|
|
1164
|
-
/* @__PURE__ */ (0,
|
|
1378
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("code", { children: "executable-stories-react" }),
|
|
1165
1379
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1166
1380
|
] }) : null,
|
|
1167
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0,
|
|
1168
|
-
/* @__PURE__ */ (0,
|
|
1381
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("details", { children: [
|
|
1382
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("summary", { children: [
|
|
1169
1383
|
error.issues.length,
|
|
1170
1384
|
" validation issue",
|
|
1171
1385
|
error.issues.length === 1 ? "" : "s"
|
|
1172
1386
|
] }),
|
|
1173
|
-
/* @__PURE__ */ (0,
|
|
1387
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1174
1388
|
] }) : null
|
|
1175
1389
|
] });
|
|
1176
1390
|
}
|
|
1177
1391
|
|
|
1178
1392
|
// src/components/ReportShell.tsx
|
|
1179
|
-
var
|
|
1393
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1180
1394
|
function ReportTitleBlock({ title }) {
|
|
1181
|
-
return /* @__PURE__ */ (0,
|
|
1182
|
-
/* @__PURE__ */ (0,
|
|
1183
|
-
/* @__PURE__ */ (0,
|
|
1184
|
-
/* @__PURE__ */ (0,
|
|
1395
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1396
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h1", { children: title ?? "Story Report" }),
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportSummary, {}),
|
|
1398
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportMeta, {})
|
|
1185
1399
|
] });
|
|
1186
1400
|
}
|
|
1187
1401
|
function ReportErrorShell({
|
|
@@ -1190,13 +1404,13 @@ function ReportErrorShell({
|
|
|
1190
1404
|
title,
|
|
1191
1405
|
dataTheme
|
|
1192
1406
|
}) {
|
|
1193
|
-
return /* @__PURE__ */ (0,
|
|
1407
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportSchemaError, { error }) });
|
|
1194
1408
|
}
|
|
1195
1409
|
|
|
1196
1410
|
// src/interactive/ReportSearch.tsx
|
|
1197
|
-
var
|
|
1198
|
-
var
|
|
1199
|
-
var ReportSearch = (0,
|
|
1411
|
+
var import_react8 = require("react");
|
|
1412
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1413
|
+
var ReportSearch = (0, import_react8.forwardRef)(
|
|
1200
1414
|
function ReportSearch2(props, ref) {
|
|
1201
1415
|
const {
|
|
1202
1416
|
value,
|
|
@@ -1206,7 +1420,7 @@ var ReportSearch = (0, import_react7.forwardRef)(
|
|
|
1206
1420
|
placeholder = "Search scenarios, tags, or step text\u2026",
|
|
1207
1421
|
className
|
|
1208
1422
|
} = props;
|
|
1209
|
-
const inputId = (0,
|
|
1423
|
+
const inputId = (0, import_react8.useId)();
|
|
1210
1424
|
const showCounts = typeof matchedCount === "number" && typeof totalCount === "number";
|
|
1211
1425
|
function handleChange(e) {
|
|
1212
1426
|
onChange(e.target.value);
|
|
@@ -1217,9 +1431,9 @@ var ReportSearch = (0, import_react7.forwardRef)(
|
|
|
1217
1431
|
e.preventDefault();
|
|
1218
1432
|
}
|
|
1219
1433
|
}
|
|
1220
|
-
return /* @__PURE__ */ (0,
|
|
1221
|
-
/* @__PURE__ */ (0,
|
|
1222
|
-
/* @__PURE__ */ (0,
|
|
1434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: ["es-search", className].filter(Boolean).join(" "), children: [
|
|
1435
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: inputId, className: "es-search-label", children: "Search" }),
|
|
1436
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1223
1437
|
"input",
|
|
1224
1438
|
{
|
|
1225
1439
|
ref,
|
|
@@ -1234,13 +1448,13 @@ var ReportSearch = (0, import_react7.forwardRef)(
|
|
|
1234
1448
|
"aria-keyshortcuts": "/"
|
|
1235
1449
|
}
|
|
1236
1450
|
),
|
|
1237
|
-
showCounts ? /* @__PURE__ */ (0,
|
|
1451
|
+
showCounts ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "es-search-counts", "aria-live": "polite", children: value ? `${matchedCount} of ${totalCount}` : `${totalCount} total` }) : null
|
|
1238
1452
|
] });
|
|
1239
1453
|
}
|
|
1240
1454
|
);
|
|
1241
1455
|
|
|
1242
1456
|
// src/interactive/ReportFailureBanner.tsx
|
|
1243
|
-
var
|
|
1457
|
+
var import_react9 = require("react");
|
|
1244
1458
|
|
|
1245
1459
|
// src/lib/scroll.ts
|
|
1246
1460
|
function prefersReducedMotion() {
|
|
@@ -1263,15 +1477,15 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
1263
1477
|
}
|
|
1264
1478
|
|
|
1265
1479
|
// src/interactive/ReportFailureBanner.tsx
|
|
1266
|
-
var
|
|
1480
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1267
1481
|
function ReportFailureBanner({ failures }) {
|
|
1268
1482
|
const first = failures[0];
|
|
1269
|
-
const jumpToFirst = (0,
|
|
1483
|
+
const jumpToFirst = (0, import_react9.useCallback)(() => {
|
|
1270
1484
|
if (!first) return;
|
|
1271
1485
|
scrollToScenarioId(first.scenarioId);
|
|
1272
1486
|
}, [first]);
|
|
1273
1487
|
if (failures.length === 0) return null;
|
|
1274
|
-
return /* @__PURE__ */ (0,
|
|
1488
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
1275
1489
|
"div",
|
|
1276
1490
|
{
|
|
1277
1491
|
className: "es-failure-banner",
|
|
@@ -1279,13 +1493,13 @@ function ReportFailureBanner({ failures }) {
|
|
|
1279
1493
|
"aria-live": "polite",
|
|
1280
1494
|
"aria-label": "Failure summary",
|
|
1281
1495
|
children: [
|
|
1282
|
-
/* @__PURE__ */ (0,
|
|
1283
|
-
/* @__PURE__ */ (0,
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "es-failure-banner-text", children: [
|
|
1497
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: failures.length }),
|
|
1284
1498
|
" ",
|
|
1285
1499
|
"failure",
|
|
1286
1500
|
failures.length === 1 ? "" : "s"
|
|
1287
1501
|
] }),
|
|
1288
|
-
/* @__PURE__ */ (0,
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1289
1503
|
"button",
|
|
1290
1504
|
{
|
|
1291
1505
|
type: "button",
|
|
@@ -1300,9 +1514,101 @@ function ReportFailureBanner({ failures }) {
|
|
|
1300
1514
|
);
|
|
1301
1515
|
}
|
|
1302
1516
|
|
|
1517
|
+
// src/interactive/ReportFreshness.tsx
|
|
1518
|
+
var import_react10 = require("react");
|
|
1519
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1520
|
+
function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
1521
|
+
const [now, setNow] = (0, import_react10.useState)(nowMs);
|
|
1522
|
+
(0, import_react10.useEffect)(() => {
|
|
1523
|
+
if (nowMs === void 0) setNow(Date.now());
|
|
1524
|
+
}, [nowMs]);
|
|
1525
|
+
if (lastRunMs === void 0 || now === void 0) return null;
|
|
1526
|
+
const age = formatRelativeAge(lastRunMs, now);
|
|
1527
|
+
const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
1528
|
+
"a",
|
|
1529
|
+
{
|
|
1530
|
+
href: ciUrl,
|
|
1531
|
+
target: "_blank",
|
|
1532
|
+
rel: "noreferrer noopener",
|
|
1533
|
+
className: "underline underline-offset-2",
|
|
1534
|
+
children: "View CI run"
|
|
1535
|
+
}
|
|
1536
|
+
) : null;
|
|
1537
|
+
if (isReportStale(lastRunMs, staleAfterDays, now)) {
|
|
1538
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
|
|
1539
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { children: [
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("strong", { children: [
|
|
1541
|
+
"Last verified ",
|
|
1542
|
+
age,
|
|
1543
|
+
"."
|
|
1544
|
+
] }),
|
|
1545
|
+
" This report may no longer match the current behavior \u2014 look for a newer run."
|
|
1546
|
+
] }),
|
|
1547
|
+
runLink
|
|
1548
|
+
] });
|
|
1549
|
+
}
|
|
1550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
|
|
1551
|
+
"Verified ",
|
|
1552
|
+
age,
|
|
1553
|
+
runLink ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
1554
|
+
" \xB7 ",
|
|
1555
|
+
runLink
|
|
1556
|
+
] }) : null
|
|
1557
|
+
] });
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// src/interactive/ReportLastRunDelta.tsx
|
|
1561
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1562
|
+
var MAX_LINKS = 5;
|
|
1563
|
+
function ScenarioLinks({ scenarios, tone }) {
|
|
1564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
1565
|
+
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
|
|
1566
|
+
i > 0 ? ", " : "",
|
|
1567
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
|
|
1568
|
+
] }, s.id)),
|
|
1569
|
+
scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
|
|
1570
|
+
" and ",
|
|
1571
|
+
scenarios.length - MAX_LINKS,
|
|
1572
|
+
" more"
|
|
1573
|
+
] }) : null
|
|
1574
|
+
] });
|
|
1575
|
+
}
|
|
1576
|
+
function ReportLastRunDelta({
|
|
1577
|
+
history: history2,
|
|
1578
|
+
report
|
|
1579
|
+
}) {
|
|
1580
|
+
if (!history2) return null;
|
|
1581
|
+
const scenarios = report.features.flatMap((f) => f.scenarios.map((s) => ({ id: s.id, title: s.title })));
|
|
1582
|
+
const delta = diffSinceLastRun(history2, scenarios);
|
|
1583
|
+
if (!delta) return null;
|
|
1584
|
+
const { newlyFailing, newlyPassing, added } = delta;
|
|
1585
|
+
if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
|
|
1586
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
|
|
1587
|
+
}
|
|
1588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
|
|
1589
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
|
|
1590
|
+
newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ml-2 text-fail", children: [
|
|
1591
|
+
newlyFailing.length,
|
|
1592
|
+
" newly failing (",
|
|
1593
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
|
|
1594
|
+
")"
|
|
1595
|
+
] }) : null,
|
|
1596
|
+
newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ml-2 text-pass", children: [
|
|
1597
|
+
newlyPassing.length,
|
|
1598
|
+
" fixed (",
|
|
1599
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
|
|
1600
|
+
")"
|
|
1601
|
+
] }) : null,
|
|
1602
|
+
added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ml-2", children: [
|
|
1603
|
+
added.length,
|
|
1604
|
+
" new"
|
|
1605
|
+
] }) : null
|
|
1606
|
+
] });
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1303
1609
|
// src/interactive/ReportShortcutsHelp.tsx
|
|
1304
|
-
var
|
|
1305
|
-
var
|
|
1610
|
+
var import_react11 = require("react");
|
|
1611
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1306
1612
|
var SHORTCUTS = [
|
|
1307
1613
|
{ keys: "/", description: "Focus search" },
|
|
1308
1614
|
{ keys: "f", description: "Jump to next failure" },
|
|
@@ -1311,14 +1617,14 @@ var SHORTCUTS = [
|
|
|
1311
1617
|
{ keys: "?", description: "Toggle this help" }
|
|
1312
1618
|
];
|
|
1313
1619
|
function ReportShortcutsHelp({ open, onClose }) {
|
|
1314
|
-
const dialogRef = (0,
|
|
1315
|
-
(0,
|
|
1620
|
+
const dialogRef = (0, import_react11.useRef)(null);
|
|
1621
|
+
(0, import_react11.useEffect)(() => {
|
|
1316
1622
|
const dialog = dialogRef.current;
|
|
1317
1623
|
if (!dialog) return;
|
|
1318
1624
|
if (open && !dialog.open) dialog.showModal();
|
|
1319
1625
|
else if (!open && dialog.open) dialog.close();
|
|
1320
1626
|
}, [open]);
|
|
1321
|
-
return /* @__PURE__ */ (0,
|
|
1627
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
1322
1628
|
"dialog",
|
|
1323
1629
|
{
|
|
1324
1630
|
ref: dialogRef,
|
|
@@ -1326,19 +1632,19 @@ function ReportShortcutsHelp({ open, onClose }) {
|
|
|
1326
1632
|
"aria-label": "Keyboard shortcuts",
|
|
1327
1633
|
onClose,
|
|
1328
1634
|
children: [
|
|
1329
|
-
/* @__PURE__ */ (0,
|
|
1330
|
-
/* @__PURE__ */ (0,
|
|
1331
|
-
/* @__PURE__ */ (0,
|
|
1332
|
-
/* @__PURE__ */ (0,
|
|
1635
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h2", { children: "Keyboard shortcuts" }),
|
|
1636
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("dl", { children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { children: [
|
|
1637
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("dt", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("kbd", { children: s.keys }) }),
|
|
1638
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("dd", { children: s.description })
|
|
1333
1639
|
] }, s.keys)) }),
|
|
1334
|
-
/* @__PURE__ */ (0,
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("form", { method: "dialog", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "submit", className: "es-shortcuts-close", children: "Close" }) })
|
|
1335
1641
|
]
|
|
1336
1642
|
}
|
|
1337
1643
|
);
|
|
1338
1644
|
}
|
|
1339
1645
|
|
|
1340
1646
|
// src/interactive/use-keyboard-shortcuts.ts
|
|
1341
|
-
var
|
|
1647
|
+
var import_react12 = require("react");
|
|
1342
1648
|
function isEditableTarget(target) {
|
|
1343
1649
|
if (!(target instanceof HTMLElement)) return false;
|
|
1344
1650
|
const tag = target.tagName;
|
|
@@ -1347,9 +1653,9 @@ function isEditableTarget(target) {
|
|
|
1347
1653
|
return false;
|
|
1348
1654
|
}
|
|
1349
1655
|
function useKeyboardShortcuts(handlers) {
|
|
1350
|
-
const ref = (0,
|
|
1656
|
+
const ref = (0, import_react12.useRef)(handlers);
|
|
1351
1657
|
ref.current = handlers;
|
|
1352
|
-
(0,
|
|
1658
|
+
(0, import_react12.useEffect)(() => {
|
|
1353
1659
|
function onKeyDown(e) {
|
|
1354
1660
|
const h = ref.current;
|
|
1355
1661
|
if (e.key === "Escape") {
|
|
@@ -1393,9 +1699,9 @@ function useKeyboardShortcuts(handlers) {
|
|
|
1393
1699
|
}
|
|
1394
1700
|
|
|
1395
1701
|
// src/interactive/use-deep-link-scroll.ts
|
|
1396
|
-
var
|
|
1702
|
+
var import_react13 = require("react");
|
|
1397
1703
|
function useDeepLinkScroll() {
|
|
1398
|
-
(0,
|
|
1704
|
+
(0, import_react13.useEffect)(() => {
|
|
1399
1705
|
function scrollToHash() {
|
|
1400
1706
|
const hash = window.location.hash.replace(/^#/, "");
|
|
1401
1707
|
if (!hash) return;
|
|
@@ -1504,7 +1810,7 @@ function listFailures(report) {
|
|
|
1504
1810
|
}
|
|
1505
1811
|
|
|
1506
1812
|
// src/interactive/ReportFilters.tsx
|
|
1507
|
-
var
|
|
1813
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1508
1814
|
function ReportFilters({
|
|
1509
1815
|
statuses,
|
|
1510
1816
|
status,
|
|
@@ -1514,10 +1820,10 @@ function ReportFilters({
|
|
|
1514
1820
|
onToggleTag
|
|
1515
1821
|
}) {
|
|
1516
1822
|
if (statuses.length === 0 && tags.length === 0) return null;
|
|
1517
|
-
return /* @__PURE__ */ (0,
|
|
1518
|
-
statuses.length > 0 ? /* @__PURE__ */ (0,
|
|
1823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1824
|
+
statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
|
|
1519
1825
|
const active = status === opt.key;
|
|
1520
|
-
return /* @__PURE__ */ (0,
|
|
1826
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
1521
1827
|
"button",
|
|
1522
1828
|
{
|
|
1523
1829
|
type: "button",
|
|
@@ -1529,22 +1835,22 @@ function ReportFilters({
|
|
|
1529
1835
|
),
|
|
1530
1836
|
children: [
|
|
1531
1837
|
opt.label,
|
|
1532
|
-
/* @__PURE__ */ (0,
|
|
1838
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
|
|
1533
1839
|
]
|
|
1534
1840
|
},
|
|
1535
1841
|
opt.key
|
|
1536
1842
|
);
|
|
1537
1843
|
}) }) : null,
|
|
1538
|
-
tags.length > 0 ? /* @__PURE__ */ (0,
|
|
1844
|
+
tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap gap-1.5", children: tags.map((tag) => {
|
|
1539
1845
|
const active = activeTags.includes(tag);
|
|
1540
|
-
return /* @__PURE__ */ (0,
|
|
1846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1541
1847
|
"button",
|
|
1542
1848
|
{
|
|
1543
1849
|
type: "button",
|
|
1544
1850
|
"aria-pressed": active,
|
|
1545
1851
|
onClick: () => onToggleTag(tag),
|
|
1546
1852
|
className: "cursor-pointer",
|
|
1547
|
-
children: /* @__PURE__ */ (0,
|
|
1853
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Badge, { variant: active ? "default" : "tag", children: tag })
|
|
1548
1854
|
}
|
|
1549
1855
|
) }, tag);
|
|
1550
1856
|
}) }) : null
|
|
@@ -1552,8 +1858,8 @@ function ReportFilters({
|
|
|
1552
1858
|
}
|
|
1553
1859
|
|
|
1554
1860
|
// src/interactive/ReportToc.tsx
|
|
1555
|
-
var
|
|
1556
|
-
var
|
|
1861
|
+
var import_react14 = require("react");
|
|
1862
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1557
1863
|
var DOT = {
|
|
1558
1864
|
passed: "text-pass",
|
|
1559
1865
|
failed: "text-fail",
|
|
@@ -1562,12 +1868,12 @@ var DOT = {
|
|
|
1562
1868
|
};
|
|
1563
1869
|
function ReportToc() {
|
|
1564
1870
|
const report = useReport();
|
|
1565
|
-
const [activeId, setActiveId] = (0,
|
|
1566
|
-
const scenarioIds = (0,
|
|
1871
|
+
const [activeId, setActiveId] = (0, import_react14.useState)(null);
|
|
1872
|
+
const scenarioIds = (0, import_react14.useMemo)(
|
|
1567
1873
|
() => report.features.flatMap((f) => f.scenarios.map((s) => s.id)),
|
|
1568
1874
|
[report]
|
|
1569
1875
|
);
|
|
1570
|
-
(0,
|
|
1876
|
+
(0, import_react14.useEffect)(() => {
|
|
1571
1877
|
if (typeof IntersectionObserver === "undefined") return;
|
|
1572
1878
|
const els = scenarioIds.map((id) => document.getElementById(id)).filter((el) => el !== null);
|
|
1573
1879
|
if (els.length === 0) return;
|
|
@@ -1582,16 +1888,16 @@ function ReportToc() {
|
|
|
1582
1888
|
return () => observer.disconnect();
|
|
1583
1889
|
}, [scenarioIds]);
|
|
1584
1890
|
if (report.features.length === 0) return null;
|
|
1585
|
-
return /* @__PURE__ */ (0,
|
|
1891
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1586
1892
|
"nav",
|
|
1587
1893
|
{
|
|
1588
1894
|
"aria-label": "Table of contents",
|
|
1589
1895
|
className: "sticky top-4 hidden h-fit w-56 shrink-0 self-start text-xs lg:block",
|
|
1590
|
-
children: /* @__PURE__ */ (0,
|
|
1591
|
-
/* @__PURE__ */ (0,
|
|
1592
|
-
/* @__PURE__ */ (0,
|
|
1896
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: "flex flex-col gap-3", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { children: [
|
|
1897
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "mb-1 truncate font-semibold text-foreground", children: feature.title }),
|
|
1898
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: "flex flex-col", children: feature.scenarios.map((scenario) => {
|
|
1593
1899
|
const active = activeId === scenario.id;
|
|
1594
|
-
return /* @__PURE__ */ (0,
|
|
1900
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
1595
1901
|
"a",
|
|
1596
1902
|
{
|
|
1597
1903
|
href: `#${scenario.id}`,
|
|
@@ -1605,8 +1911,8 @@ function ReportToc() {
|
|
|
1605
1911
|
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
1606
1912
|
),
|
|
1607
1913
|
children: [
|
|
1608
|
-
/* @__PURE__ */ (0,
|
|
1609
|
-
/* @__PURE__ */ (0,
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", DOT[scenario.status]), children: "\u2022" }),
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "truncate", children: scenario.title })
|
|
1610
1916
|
]
|
|
1611
1917
|
}
|
|
1612
1918
|
) }, scenario.id);
|
|
@@ -1617,7 +1923,7 @@ function ReportToc() {
|
|
|
1617
1923
|
}
|
|
1618
1924
|
|
|
1619
1925
|
// src/interactive/use-theme.ts
|
|
1620
|
-
var
|
|
1926
|
+
var import_react15 = require("react");
|
|
1621
1927
|
var KEY = "es-theme";
|
|
1622
1928
|
function applyTheme(theme) {
|
|
1623
1929
|
if (typeof document !== "undefined") {
|
|
@@ -1638,13 +1944,13 @@ function initialTheme() {
|
|
|
1638
1944
|
return "light";
|
|
1639
1945
|
}
|
|
1640
1946
|
function useTheme() {
|
|
1641
|
-
const [theme, setTheme] = (0,
|
|
1642
|
-
(0,
|
|
1947
|
+
const [theme, setTheme] = (0, import_react15.useState)("light");
|
|
1948
|
+
(0, import_react15.useEffect)(() => {
|
|
1643
1949
|
const t = initialTheme();
|
|
1644
1950
|
setTheme(t);
|
|
1645
1951
|
applyTheme(t);
|
|
1646
1952
|
}, []);
|
|
1647
|
-
const toggle = (0,
|
|
1953
|
+
const toggle = (0, import_react15.useCallback)(() => {
|
|
1648
1954
|
setTheme((prev) => {
|
|
1649
1955
|
const next = prev === "dark" ? "light" : "dark";
|
|
1650
1956
|
applyTheme(next);
|
|
@@ -1659,14 +1965,14 @@ function useTheme() {
|
|
|
1659
1965
|
}
|
|
1660
1966
|
|
|
1661
1967
|
// src/interactive/ReportInteractive.tsx
|
|
1662
|
-
var
|
|
1968
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
1663
1969
|
function ReportInteractive(props) {
|
|
1664
1970
|
const { report, className, title, dataTheme } = props;
|
|
1665
1971
|
const result = unwrapReport(report);
|
|
1666
1972
|
if (!result.ok) {
|
|
1667
|
-
return /* @__PURE__ */ (0,
|
|
1973
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1668
1974
|
}
|
|
1669
|
-
return /* @__PURE__ */ (0,
|
|
1975
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportInteractiveView, { ...props, report: result.data });
|
|
1670
1976
|
}
|
|
1671
1977
|
function ReportInteractiveView({
|
|
1672
1978
|
report,
|
|
@@ -1675,23 +1981,25 @@ function ReportInteractiveView({
|
|
|
1675
1981
|
className,
|
|
1676
1982
|
title,
|
|
1677
1983
|
dataTheme,
|
|
1678
|
-
hideHeader = false
|
|
1984
|
+
hideHeader = false,
|
|
1985
|
+
staleAfterDays = 7,
|
|
1986
|
+
scenarioHistory
|
|
1679
1987
|
}) {
|
|
1680
|
-
const [query, setQuery] = (0,
|
|
1681
|
-
const [statusFilter, setStatusFilter] = (0,
|
|
1682
|
-
const [activeTags, setActiveTags] = (0,
|
|
1683
|
-
const [detail, setDetail] = (0,
|
|
1684
|
-
const [helpOpen, setHelpOpen] = (0,
|
|
1988
|
+
const [query, setQuery] = (0, import_react16.useState)("");
|
|
1989
|
+
const [statusFilter, setStatusFilter] = (0, import_react16.useState)("all");
|
|
1990
|
+
const [activeTags, setActiveTags] = (0, import_react16.useState)([]);
|
|
1991
|
+
const [detail, setDetail] = (0, import_react16.useState)("full");
|
|
1992
|
+
const [helpOpen, setHelpOpen] = (0, import_react16.useState)(false);
|
|
1685
1993
|
const { theme, toggle: toggleTheme } = useTheme();
|
|
1686
|
-
const searchRef = (0,
|
|
1687
|
-
const deferredQuery = (0,
|
|
1688
|
-
const failures = (0,
|
|
1689
|
-
const filtered = (0,
|
|
1994
|
+
const searchRef = (0, import_react16.useRef)(null);
|
|
1995
|
+
const deferredQuery = (0, import_react16.useDeferredValue)(query);
|
|
1996
|
+
const failures = (0, import_react16.useMemo)(() => listFailures(report), [report]);
|
|
1997
|
+
const filtered = (0, import_react16.useMemo)(
|
|
1690
1998
|
() => filterReport(report, { query: deferredQuery, status: statusFilter, tags: activeTags }),
|
|
1691
1999
|
[report, deferredQuery, statusFilter, activeTags]
|
|
1692
2000
|
);
|
|
1693
2001
|
const isFiltering = query !== deferredQuery;
|
|
1694
|
-
const statusOptions = (0,
|
|
2002
|
+
const statusOptions = (0, import_react16.useMemo)(() => {
|
|
1695
2003
|
const s = report.summary;
|
|
1696
2004
|
const opts = [
|
|
1697
2005
|
{ key: "all", label: "All", count: s.total }
|
|
@@ -1702,19 +2010,19 @@ function ReportInteractiveView({
|
|
|
1702
2010
|
if (s.pending) opts.push({ key: "pending", label: "Pending", count: s.pending });
|
|
1703
2011
|
return opts;
|
|
1704
2012
|
}, [report]);
|
|
1705
|
-
const tagOptions = (0,
|
|
1706
|
-
const toggleTag = (0,
|
|
2013
|
+
const tagOptions = (0, import_react16.useMemo)(() => allTags(report), [report]);
|
|
2014
|
+
const toggleTag = (0, import_react16.useCallback)(
|
|
1707
2015
|
(tag) => setActiveTags((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]),
|
|
1708
2016
|
[]
|
|
1709
2017
|
);
|
|
1710
|
-
const [toast, setToast] = (0,
|
|
1711
|
-
const toastTimer = (0,
|
|
1712
|
-
const showToast = (0,
|
|
2018
|
+
const [toast, setToast] = (0, import_react16.useState)(null);
|
|
2019
|
+
const toastTimer = (0, import_react16.useRef)(null);
|
|
2020
|
+
const showToast = (0, import_react16.useCallback)((msg) => {
|
|
1713
2021
|
setToast(msg);
|
|
1714
2022
|
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
1715
2023
|
toastTimer.current = setTimeout(() => setToast(null), 1800);
|
|
1716
2024
|
}, []);
|
|
1717
|
-
const copy = (0,
|
|
2025
|
+
const copy = (0, import_react16.useCallback)(
|
|
1718
2026
|
(text, msg) => {
|
|
1719
2027
|
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
1720
2028
|
navigator.clipboard.writeText(text).then(() => showToast(msg), () => showToast("Copy failed"));
|
|
@@ -1722,7 +2030,7 @@ function ReportInteractiveView({
|
|
|
1722
2030
|
},
|
|
1723
2031
|
[showToast]
|
|
1724
2032
|
);
|
|
1725
|
-
const scenarioActions = (0,
|
|
2033
|
+
const scenarioActions = (0, import_react16.useMemo)(
|
|
1726
2034
|
() => ({
|
|
1727
2035
|
copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
|
|
1728
2036
|
copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
|
|
@@ -1730,17 +2038,17 @@ function ReportInteractiveView({
|
|
|
1730
2038
|
}),
|
|
1731
2039
|
[copy]
|
|
1732
2040
|
);
|
|
1733
|
-
const failureIndexRef = (0,
|
|
2041
|
+
const failureIndexRef = (0, import_react16.useRef)(0);
|
|
1734
2042
|
const collapse = useCollapseState();
|
|
1735
|
-
const allCollapsibleIds = (0,
|
|
2043
|
+
const allCollapsibleIds = (0, import_react16.useMemo)(
|
|
1736
2044
|
() => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
|
|
1737
2045
|
[report]
|
|
1738
2046
|
);
|
|
1739
|
-
const collapseAll = (0,
|
|
1740
|
-
const focusSearch = (0,
|
|
2047
|
+
const collapseAll = (0, import_react16.useCallback)(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
|
|
2048
|
+
const focusSearch = (0, import_react16.useCallback)(() => {
|
|
1741
2049
|
searchRef.current?.focus();
|
|
1742
2050
|
}, []);
|
|
1743
|
-
const stepFailure = (0,
|
|
2051
|
+
const stepFailure = (0, import_react16.useCallback)(
|
|
1744
2052
|
(direction) => {
|
|
1745
2053
|
if (failures.length === 0) return;
|
|
1746
2054
|
failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
|
|
@@ -1749,10 +2057,10 @@ function ReportInteractiveView({
|
|
|
1749
2057
|
},
|
|
1750
2058
|
[failures]
|
|
1751
2059
|
);
|
|
1752
|
-
const toggleHelp = (0,
|
|
2060
|
+
const toggleHelp = (0, import_react16.useCallback)(() => {
|
|
1753
2061
|
setHelpOpen((v) => !v);
|
|
1754
2062
|
}, []);
|
|
1755
|
-
const escape = (0,
|
|
2063
|
+
const escape = (0, import_react16.useCallback)(() => {
|
|
1756
2064
|
if (helpOpen) setHelpOpen(false);
|
|
1757
2065
|
else if (query !== "") setQuery("");
|
|
1758
2066
|
}, [helpOpen, query]);
|
|
@@ -1769,13 +2077,13 @@ function ReportInteractiveView({
|
|
|
1769
2077
|
const hasContent = filtered.features.length > 0;
|
|
1770
2078
|
const totalScenarios = report.summary.total;
|
|
1771
2079
|
const matchedScenarios = filtered.summary.total;
|
|
1772
|
-
return /* @__PURE__ */ (0,
|
|
2080
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1773
2081
|
ReportRoot,
|
|
1774
2082
|
{
|
|
1775
2083
|
report: filtered,
|
|
1776
2084
|
customRenderers,
|
|
1777
2085
|
renderers,
|
|
1778
|
-
children: /* @__PURE__ */ (0,
|
|
2086
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
1779
2087
|
"main",
|
|
1780
2088
|
{
|
|
1781
2089
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -1784,10 +2092,19 @@ function ReportInteractiveView({
|
|
|
1784
2092
|
"data-theme": dataTheme,
|
|
1785
2093
|
"data-detail-level": detail,
|
|
1786
2094
|
children: [
|
|
1787
|
-
/* @__PURE__ */ (0,
|
|
1788
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
1789
|
-
/* @__PURE__ */ (0,
|
|
1790
|
-
|
|
2095
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("header", { className: "es-report-header", children: [
|
|
2096
|
+
hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportTitleBlock, { title }),
|
|
2097
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2098
|
+
ReportFreshness,
|
|
2099
|
+
{
|
|
2100
|
+
lastRunMs: reportLastRunMs(report),
|
|
2101
|
+
ciUrl: report.ci?.url,
|
|
2102
|
+
staleAfterDays
|
|
2103
|
+
}
|
|
2104
|
+
),
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1791
2108
|
ReportSearch,
|
|
1792
2109
|
{
|
|
1793
2110
|
ref: searchRef,
|
|
@@ -1797,8 +2114,8 @@ function ReportInteractiveView({
|
|
|
1797
2114
|
totalCount: totalScenarios
|
|
1798
2115
|
}
|
|
1799
2116
|
),
|
|
1800
|
-
/* @__PURE__ */ (0,
|
|
1801
|
-
/* @__PURE__ */ (0,
|
|
2117
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex shrink-0 gap-1", children: [
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1802
2119
|
"button",
|
|
1803
2120
|
{
|
|
1804
2121
|
type: "button",
|
|
@@ -1808,7 +2125,7 @@ function ReportInteractiveView({
|
|
|
1808
2125
|
children: "Expand all"
|
|
1809
2126
|
}
|
|
1810
2127
|
),
|
|
1811
|
-
/* @__PURE__ */ (0,
|
|
2128
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1812
2129
|
"button",
|
|
1813
2130
|
{
|
|
1814
2131
|
type: "button",
|
|
@@ -1818,7 +2135,7 @@ function ReportInteractiveView({
|
|
|
1818
2135
|
children: "Collapse all"
|
|
1819
2136
|
}
|
|
1820
2137
|
),
|
|
1821
|
-
/* @__PURE__ */ (0,
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1822
2139
|
"button",
|
|
1823
2140
|
{
|
|
1824
2141
|
type: "button",
|
|
@@ -1828,7 +2145,7 @@ function ReportInteractiveView({
|
|
|
1828
2145
|
children: detail === "full" ? "Hide docs" : "Show docs"
|
|
1829
2146
|
}
|
|
1830
2147
|
),
|
|
1831
|
-
/* @__PURE__ */ (0,
|
|
2148
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1832
2149
|
"button",
|
|
1833
2150
|
{
|
|
1834
2151
|
type: "button",
|
|
@@ -1840,7 +2157,7 @@ function ReportInteractiveView({
|
|
|
1840
2157
|
)
|
|
1841
2158
|
] })
|
|
1842
2159
|
] }),
|
|
1843
|
-
/* @__PURE__ */ (0,
|
|
2160
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1844
2161
|
ReportFilters,
|
|
1845
2162
|
{
|
|
1846
2163
|
statuses: statusOptions,
|
|
@@ -1852,12 +2169,12 @@ function ReportInteractiveView({
|
|
|
1852
2169
|
}
|
|
1853
2170
|
)
|
|
1854
2171
|
] }),
|
|
1855
|
-
/* @__PURE__ */ (0,
|
|
1856
|
-
hasContent ? /* @__PURE__ */ (0,
|
|
1857
|
-
/* @__PURE__ */ (0,
|
|
1858
|
-
/* @__PURE__ */ (0,
|
|
1859
|
-
] }) : /* @__PURE__ */ (0,
|
|
1860
|
-
/* @__PURE__ */ (0,
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportFailureBanner, { failures }),
|
|
2173
|
+
hasContent ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex gap-6", children: [
|
|
2174
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportToc, {}),
|
|
2175
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportFeatureList, {}) })
|
|
2176
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1861
2178
|
"button",
|
|
1862
2179
|
{
|
|
1863
2180
|
type: "button",
|
|
@@ -1868,8 +2185,8 @@ function ReportInteractiveView({
|
|
|
1868
2185
|
children: "?"
|
|
1869
2186
|
}
|
|
1870
2187
|
),
|
|
1871
|
-
/* @__PURE__ */ (0,
|
|
1872
|
-
toast ? /* @__PURE__ */ (0,
|
|
2188
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
|
|
2189
|
+
toast ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1873
2190
|
"div",
|
|
1874
2191
|
{
|
|
1875
2192
|
role: "status",
|
|
@@ -1882,18 +2199,18 @@ function ReportInteractiveView({
|
|
|
1882
2199
|
}
|
|
1883
2200
|
)
|
|
1884
2201
|
}
|
|
1885
|
-
) }) });
|
|
2202
|
+
) }) }) });
|
|
1886
2203
|
}
|
|
1887
2204
|
|
|
1888
2205
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
1889
|
-
var
|
|
2206
|
+
var import_react19 = require("react");
|
|
1890
2207
|
|
|
1891
2208
|
// src/interactive/island-renderers.tsx
|
|
1892
|
-
var
|
|
2209
|
+
var import_react18 = require("react");
|
|
1893
2210
|
|
|
1894
2211
|
// src/components/doc/MermaidView.tsx
|
|
1895
|
-
var
|
|
1896
|
-
var
|
|
2212
|
+
var import_react17 = require("react");
|
|
2213
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1897
2214
|
function prefersDark(el) {
|
|
1898
2215
|
const themed = el?.closest("[data-theme]");
|
|
1899
2216
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -1901,9 +2218,9 @@ function prefersDark(el) {
|
|
|
1901
2218
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
1902
2219
|
}
|
|
1903
2220
|
function MermaidView({ entry, load }) {
|
|
1904
|
-
const ref = (0,
|
|
1905
|
-
const [svg, setSvg] = (0,
|
|
1906
|
-
(0,
|
|
2221
|
+
const ref = (0, import_react17.useRef)(null);
|
|
2222
|
+
const [svg, setSvg] = (0, import_react17.useState)(null);
|
|
2223
|
+
(0, import_react17.useEffect)(() => {
|
|
1907
2224
|
let cancelled = false;
|
|
1908
2225
|
const draw = async () => {
|
|
1909
2226
|
try {
|
|
@@ -1930,9 +2247,9 @@ function MermaidView({ entry, load }) {
|
|
|
1930
2247
|
};
|
|
1931
2248
|
}, [entry.code, load]);
|
|
1932
2249
|
if (svg) {
|
|
1933
|
-
return /* @__PURE__ */ (0,
|
|
1934
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
1935
|
-
/* @__PURE__ */ (0,
|
|
2250
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("figure", { ref, className: "my-3", children: [
|
|
2251
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
2252
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
1936
2253
|
"div",
|
|
1937
2254
|
{
|
|
1938
2255
|
role: "img",
|
|
@@ -1943,11 +2260,11 @@ function MermaidView({ entry, load }) {
|
|
|
1943
2260
|
)
|
|
1944
2261
|
] });
|
|
1945
2262
|
}
|
|
1946
|
-
return /* @__PURE__ */ (0,
|
|
2263
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MermaidSource, { entry, ref });
|
|
1947
2264
|
}
|
|
1948
2265
|
|
|
1949
2266
|
// src/interactive/island-renderers.tsx
|
|
1950
|
-
var
|
|
2267
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
1951
2268
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
1952
2269
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
1953
2270
|
function cachedCdnModule(url) {
|
|
@@ -1964,8 +2281,8 @@ function cachedCdnModule(url) {
|
|
|
1964
2281
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
1965
2282
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
1966
2283
|
function CdnHighlightedCode({ entry }) {
|
|
1967
|
-
const [html, setHtml] = (0,
|
|
1968
|
-
(0,
|
|
2284
|
+
const [html, setHtml] = (0, import_react18.useState)(null);
|
|
2285
|
+
(0, import_react18.useEffect)(() => {
|
|
1969
2286
|
let cancelled = false;
|
|
1970
2287
|
void (async () => {
|
|
1971
2288
|
try {
|
|
@@ -1981,27 +2298,27 @@ function CdnHighlightedCode({ entry }) {
|
|
|
1981
2298
|
cancelled = true;
|
|
1982
2299
|
};
|
|
1983
2300
|
}, [entry.content, entry.lang]);
|
|
1984
|
-
return /* @__PURE__ */ (0,
|
|
2301
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
1985
2302
|
"code",
|
|
1986
2303
|
{
|
|
1987
2304
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
1988
2305
|
dangerouslySetInnerHTML: { __html: html }
|
|
1989
2306
|
}
|
|
1990
|
-
) : /* @__PURE__ */ (0,
|
|
2307
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
1991
2308
|
}
|
|
1992
2309
|
function buildIslandRenderers(opts) {
|
|
1993
2310
|
const renderers = {};
|
|
1994
2311
|
if (opts.mermaid) {
|
|
1995
|
-
renderers.mermaid = (entry) => (0,
|
|
2312
|
+
renderers.mermaid = (entry) => (0, import_react18.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
|
|
1996
2313
|
}
|
|
1997
2314
|
if (opts.syntaxHighlighting) {
|
|
1998
|
-
renderers.code = (entry) => (0,
|
|
2315
|
+
renderers.code = (entry) => (0, import_react18.createElement)(CdnHighlightedCode, { entry });
|
|
1999
2316
|
}
|
|
2000
2317
|
return renderers;
|
|
2001
2318
|
}
|
|
2002
2319
|
|
|
2003
2320
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
2004
|
-
var
|
|
2321
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2005
2322
|
function ReportInteractiveIsland({
|
|
2006
2323
|
report,
|
|
2007
2324
|
title,
|
|
@@ -2010,11 +2327,11 @@ function ReportInteractiveIsland({
|
|
|
2010
2327
|
syntaxHighlighting = true,
|
|
2011
2328
|
mermaid = true
|
|
2012
2329
|
}) {
|
|
2013
|
-
const renderers = (0,
|
|
2330
|
+
const renderers = (0, import_react19.useMemo)(
|
|
2014
2331
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
2015
2332
|
[syntaxHighlighting, mermaid]
|
|
2016
2333
|
);
|
|
2017
|
-
return /* @__PURE__ */ (0,
|
|
2334
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2018
2335
|
ReportInteractive,
|
|
2019
2336
|
{
|
|
2020
2337
|
report,
|
|
@@ -2028,6 +2345,7 @@ function ReportInteractiveIsland({
|
|
|
2028
2345
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2029
2346
|
0 && (module.exports = {
|
|
2030
2347
|
ReportFailureBanner,
|
|
2348
|
+
ReportFreshness,
|
|
2031
2349
|
ReportInteractive,
|
|
2032
2350
|
ReportInteractiveIsland,
|
|
2033
2351
|
ReportSearch,
|