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.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
useDeferredValue,
|
|
7
7
|
useMemo as useMemo3,
|
|
8
8
|
useRef as useRef3,
|
|
9
|
-
useState as
|
|
9
|
+
useState as useState5
|
|
10
10
|
} from "react";
|
|
11
11
|
|
|
12
12
|
// src/result.ts
|
|
@@ -58,11 +58,23 @@ function useReport() {
|
|
|
58
58
|
return ctx.report;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
// ../executable-stories-core/dist/utils/duration.js
|
|
62
|
+
function formatDuration(ms) {
|
|
63
|
+
if (ms < 1e3) {
|
|
64
|
+
return `${Math.round(ms)} ms`;
|
|
65
|
+
}
|
|
66
|
+
if (ms < 6e4) {
|
|
67
|
+
return `${(ms / 1e3).toFixed(2)} s`;
|
|
68
|
+
}
|
|
69
|
+
const minutes = Math.floor(ms / 6e4);
|
|
70
|
+
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
71
|
+
if (minutes < 60) {
|
|
72
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
73
|
+
}
|
|
74
|
+
const hours = Math.floor(minutes / 60);
|
|
75
|
+
const remainingMinutes = minutes % 60;
|
|
76
|
+
return `${hours}h ${remainingMinutes}m`;
|
|
77
|
+
}
|
|
66
78
|
|
|
67
79
|
// src/components/doc/DocNote.tsx
|
|
68
80
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
@@ -216,6 +228,15 @@ function safeUrl(value) {
|
|
|
216
228
|
if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
|
|
217
229
|
return trimmed;
|
|
218
230
|
}
|
|
231
|
+
function safeImageUrl(value) {
|
|
232
|
+
if (!value) return void 0;
|
|
233
|
+
const trimmed = value.trim();
|
|
234
|
+
if (/^data:image\//i.test(trimmed)) return trimmed;
|
|
235
|
+
return safeUrl(trimmed);
|
|
236
|
+
}
|
|
237
|
+
function isLocalFsPath(value) {
|
|
238
|
+
return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
|
|
239
|
+
}
|
|
219
240
|
|
|
220
241
|
// src/components/doc/DocSection.tsx
|
|
221
242
|
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
@@ -271,11 +292,19 @@ function DocMermaid({ entry }) {
|
|
|
271
292
|
// src/components/doc/DocScreenshot.tsx
|
|
272
293
|
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
273
294
|
function DocScreenshot({ entry }) {
|
|
295
|
+
const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
|
|
296
|
+
if (!src) {
|
|
297
|
+
return /* @__PURE__ */ jsxs6("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
298
|
+
/* @__PURE__ */ jsx10("div", { className: "font-medium", children: "Screenshot unavailable" }),
|
|
299
|
+
/* @__PURE__ */ jsx10("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
300
|
+
entry.alt ? /* @__PURE__ */ jsx10("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
|
|
301
|
+
] });
|
|
302
|
+
}
|
|
274
303
|
return /* @__PURE__ */ jsxs6("figure", { className: "my-3", children: [
|
|
275
304
|
/* @__PURE__ */ jsx10(
|
|
276
305
|
"img",
|
|
277
306
|
{
|
|
278
|
-
src
|
|
307
|
+
src,
|
|
279
308
|
alt: entry.alt ?? "",
|
|
280
309
|
loading: "lazy",
|
|
281
310
|
className: "max-w-full rounded-md border border-border"
|
|
@@ -288,6 +317,13 @@ function DocScreenshot({ entry }) {
|
|
|
288
317
|
// src/components/doc/DocVideo.tsx
|
|
289
318
|
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
290
319
|
function DocVideo({ entry }) {
|
|
320
|
+
if (isLocalFsPath(entry.path)) {
|
|
321
|
+
return /* @__PURE__ */ jsxs7("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
322
|
+
/* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Video unavailable" }),
|
|
323
|
+
/* @__PURE__ */ jsx11("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
324
|
+
entry.caption ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
|
|
325
|
+
] });
|
|
326
|
+
}
|
|
291
327
|
const src = safeUrl(entry.path);
|
|
292
328
|
const poster = safeUrl(entry.poster);
|
|
293
329
|
return /* @__PURE__ */ jsxs7("figure", { className: "my-3", children: [
|
|
@@ -521,9 +557,6 @@ function ReportAttachments({ attachments }) {
|
|
|
521
557
|
);
|
|
522
558
|
}
|
|
523
559
|
|
|
524
|
-
// src/components/ReportTrace.tsx
|
|
525
|
-
import { formatDuration as formatDuration2 } from "executable-stories-core/utils/duration";
|
|
526
|
-
|
|
527
560
|
// src/lib/trace.ts
|
|
528
561
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
529
562
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -618,7 +651,7 @@ function ReportTrace({ spans }) {
|
|
|
618
651
|
" span",
|
|
619
652
|
rows.length === 1 ? "" : "s",
|
|
620
653
|
", ",
|
|
621
|
-
|
|
654
|
+
formatDuration(totalMs),
|
|
622
655
|
")"
|
|
623
656
|
] }),
|
|
624
657
|
/* @__PURE__ */ jsx19("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
|
|
@@ -641,18 +674,131 @@ function ReportTrace({ spans }) {
|
|
|
641
674
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
642
675
|
}
|
|
643
676
|
) }),
|
|
644
|
-
/* @__PURE__ */ jsx19("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children:
|
|
677
|
+
/* @__PURE__ */ jsx19("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
|
|
645
678
|
] }, `${row.spanId}-${i}`);
|
|
646
679
|
}) })
|
|
647
680
|
] });
|
|
648
681
|
}
|
|
649
682
|
|
|
683
|
+
// src/interactive/scenario-history-context.ts
|
|
684
|
+
import { createContext as createContext2, useContext as useContext3 } from "react";
|
|
685
|
+
var ScenarioHistoryContext = createContext2(null);
|
|
686
|
+
var ScenarioHistoryProvider = ScenarioHistoryContext.Provider;
|
|
687
|
+
function useScenarioHistory() {
|
|
688
|
+
return useContext3(ScenarioHistoryContext);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// src/lib/run-history.ts
|
|
692
|
+
function currentStreak(entries) {
|
|
693
|
+
const last = entries[entries.length - 1];
|
|
694
|
+
if (!last) return void 0;
|
|
695
|
+
let count = 0;
|
|
696
|
+
for (let i = entries.length - 1; i >= 0 && entries[i].status === last.status; i--) count++;
|
|
697
|
+
return { status: last.status, count };
|
|
698
|
+
}
|
|
699
|
+
var STREAK_VERB = {
|
|
700
|
+
passed: "Passing",
|
|
701
|
+
failed: "Failing",
|
|
702
|
+
skipped: "Skipped",
|
|
703
|
+
pending: "Pending"
|
|
704
|
+
};
|
|
705
|
+
function describeRunHistory(entries) {
|
|
706
|
+
const passed = entries.filter((e) => e.status === "passed").length;
|
|
707
|
+
const base = `${passed}/${entries.length} runs passed`;
|
|
708
|
+
const streak = currentStreak(entries);
|
|
709
|
+
if (!streak || entries.length < 2) return base;
|
|
710
|
+
return `${base} \xB7 ${STREAK_VERB[streak.status]} for the last ${streak.count === 1 ? "run" : `${streak.count} runs`}`;
|
|
711
|
+
}
|
|
712
|
+
var MIN_FLAKINESS_SAMPLES = 3;
|
|
713
|
+
function flakinessOf(entries) {
|
|
714
|
+
const countable = entries.filter((e) => e.status === "passed" || e.status === "failed");
|
|
715
|
+
if (countable.length < MIN_FLAKINESS_SAMPLES) return "stable";
|
|
716
|
+
let transitions = 0;
|
|
717
|
+
for (let i = 1; i < countable.length; i++) {
|
|
718
|
+
if (countable[i].status !== countable[i - 1].status) transitions++;
|
|
719
|
+
}
|
|
720
|
+
const transitionScore = transitions / (countable.length - 1);
|
|
721
|
+
const failureRate = countable.filter((e) => e.status === "failed").length / countable.length;
|
|
722
|
+
if (transitionScore > 0.5 || transitionScore > 0.3 && failureRate > 0.2) return "flaky";
|
|
723
|
+
if (transitionScore > 0.2 || failureRate > 0.3) return "unstable";
|
|
724
|
+
return "stable";
|
|
725
|
+
}
|
|
726
|
+
function diffSinceLastRun(history2, scenarios) {
|
|
727
|
+
let hasPreviousRun = false;
|
|
728
|
+
const delta = { newlyFailing: [], newlyPassing: [], added: [] };
|
|
729
|
+
for (const scenario of scenarios) {
|
|
730
|
+
const entries = history2[scenario.id];
|
|
731
|
+
if (!entries || entries.length === 0) continue;
|
|
732
|
+
const last = entries[entries.length - 1];
|
|
733
|
+
const prev = entries[entries.length - 2];
|
|
734
|
+
if (!prev) {
|
|
735
|
+
delta.added.push(scenario);
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
hasPreviousRun = true;
|
|
739
|
+
if (last.status === "failed" && prev.status !== "failed") delta.newlyFailing.push(scenario);
|
|
740
|
+
else if (last.status === "passed" && prev.status === "failed") delta.newlyPassing.push(scenario);
|
|
741
|
+
}
|
|
742
|
+
return hasPreviousRun ? delta : void 0;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// src/components/ScenarioRunHistory.tsx
|
|
746
|
+
import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
747
|
+
var MAX_DOTS = 10;
|
|
748
|
+
var DOT_COLOR = {
|
|
749
|
+
passed: "bg-pass",
|
|
750
|
+
failed: "bg-fail",
|
|
751
|
+
skipped: "bg-skip",
|
|
752
|
+
pending: "bg-pend"
|
|
753
|
+
};
|
|
754
|
+
function dotTitle(e) {
|
|
755
|
+
const parts = [new Date(e.timestamp).toLocaleString(), e.status];
|
|
756
|
+
if (e.commitSha) parts.push(e.commitSha.slice(0, 8));
|
|
757
|
+
return parts.join(" \xB7 ");
|
|
758
|
+
}
|
|
759
|
+
function ScenarioRunHistory({ scenarioId }) {
|
|
760
|
+
const history2 = useScenarioHistory();
|
|
761
|
+
const entries = history2?.[scenarioId];
|
|
762
|
+
if (!entries || entries.length < 2) return null;
|
|
763
|
+
const recent = entries.slice(-MAX_DOTS);
|
|
764
|
+
const flaky = flakinessOf(recent) === "flaky";
|
|
765
|
+
const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
|
|
766
|
+
return /* @__PURE__ */ jsxs13(Fragment7, { children: [
|
|
767
|
+
flaky ? /* @__PURE__ */ jsx20(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
|
|
768
|
+
/* @__PURE__ */ jsx20(
|
|
769
|
+
"span",
|
|
770
|
+
{
|
|
771
|
+
role: "img",
|
|
772
|
+
"aria-label": `Run history: ${summary}`,
|
|
773
|
+
title: summary,
|
|
774
|
+
className: "flex shrink-0 items-center gap-[3px]",
|
|
775
|
+
children: recent.map((e, i) => /* @__PURE__ */ jsx20(
|
|
776
|
+
"span",
|
|
777
|
+
{
|
|
778
|
+
"aria-hidden": true,
|
|
779
|
+
title: dotTitle(e),
|
|
780
|
+
className: cn(
|
|
781
|
+
"inline-block size-1.5 rounded-full",
|
|
782
|
+
DOT_COLOR[e.status],
|
|
783
|
+
// The newest run (rightmost dot) is the one the card's status
|
|
784
|
+
// badge already describes; ring it so the timeline reads as
|
|
785
|
+
// "…leading up to now".
|
|
786
|
+
i === recent.length - 1 && "ring-1 ring-border ring-offset-1 ring-offset-card"
|
|
787
|
+
)
|
|
788
|
+
},
|
|
789
|
+
`${e.timestamp}-${i}`
|
|
790
|
+
))
|
|
791
|
+
}
|
|
792
|
+
)
|
|
793
|
+
] });
|
|
794
|
+
}
|
|
795
|
+
|
|
650
796
|
// src/interactive/collapse-context.ts
|
|
651
|
-
import { createContext as
|
|
652
|
-
var CollapseContext =
|
|
797
|
+
import { createContext as createContext3, useCallback, useContext as useContext4, useState } from "react";
|
|
798
|
+
var CollapseContext = createContext3(null);
|
|
653
799
|
var CollapseProvider = CollapseContext.Provider;
|
|
654
800
|
function useCollapse() {
|
|
655
|
-
return
|
|
801
|
+
return useContext4(CollapseContext);
|
|
656
802
|
}
|
|
657
803
|
var STORAGE_KEY = "es-collapsed-ids";
|
|
658
804
|
function readPersisted() {
|
|
@@ -701,11 +847,11 @@ function useCollapseState() {
|
|
|
701
847
|
}
|
|
702
848
|
|
|
703
849
|
// src/interactive/scenario-actions.tsx
|
|
704
|
-
import { createContext as
|
|
705
|
-
var ScenarioActionsContext =
|
|
850
|
+
import { createContext as createContext4, useContext as useContext5 } from "react";
|
|
851
|
+
var ScenarioActionsContext = createContext4(null);
|
|
706
852
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
707
853
|
function useScenarioActions() {
|
|
708
|
-
return
|
|
854
|
+
return useContext5(ScenarioActionsContext);
|
|
709
855
|
}
|
|
710
856
|
function scenarioToMarkdown(scenario) {
|
|
711
857
|
const lines = [`## ${scenario.title} _(${scenario.status})_`, ""];
|
|
@@ -743,9 +889,9 @@ function scenarioPermalink(scenario) {
|
|
|
743
889
|
|
|
744
890
|
// src/components/ui/card.tsx
|
|
745
891
|
import "react";
|
|
746
|
-
import { jsx as
|
|
892
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
747
893
|
function Card({ className, ...props }) {
|
|
748
|
-
return /* @__PURE__ */
|
|
894
|
+
return /* @__PURE__ */ jsx21(
|
|
749
895
|
"div",
|
|
750
896
|
{
|
|
751
897
|
"data-slot": "card",
|
|
@@ -758,7 +904,7 @@ function Card({ className, ...props }) {
|
|
|
758
904
|
);
|
|
759
905
|
}
|
|
760
906
|
function CardHeader({ className, ...props }) {
|
|
761
|
-
return /* @__PURE__ */
|
|
907
|
+
return /* @__PURE__ */ jsx21(
|
|
762
908
|
"div",
|
|
763
909
|
{
|
|
764
910
|
"data-slot": "card-header",
|
|
@@ -771,7 +917,7 @@ function CardHeader({ className, ...props }) {
|
|
|
771
917
|
);
|
|
772
918
|
}
|
|
773
919
|
function CardContent({ className, ...props }) {
|
|
774
|
-
return /* @__PURE__ */
|
|
920
|
+
return /* @__PURE__ */ jsx21(
|
|
775
921
|
"div",
|
|
776
922
|
{
|
|
777
923
|
"data-slot": "card-content",
|
|
@@ -782,7 +928,7 @@ function CardContent({ className, ...props }) {
|
|
|
782
928
|
}
|
|
783
929
|
|
|
784
930
|
// src/components/ReportScenario.tsx
|
|
785
|
-
import { jsx as
|
|
931
|
+
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
786
932
|
var STATUS_LABEL = {
|
|
787
933
|
passed: "Passed",
|
|
788
934
|
failed: "Failed",
|
|
@@ -810,12 +956,12 @@ var STATUS_BADGE = {
|
|
|
810
956
|
function ReportScenario({ scenario, hideTitle = false }) {
|
|
811
957
|
const titleId = `${scenario.id}-title`;
|
|
812
958
|
const bodyId = `${scenario.id}-body`;
|
|
813
|
-
const label = STATUS_LABEL[scenario.status];
|
|
959
|
+
const label = scenario.planned ? "Planned" : STATUS_LABEL[scenario.status];
|
|
814
960
|
const collapse = useCollapse();
|
|
815
961
|
const collapsible = collapse !== null && !hideTitle;
|
|
816
962
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
817
963
|
const actions = useScenarioActions();
|
|
818
|
-
return /* @__PURE__ */
|
|
964
|
+
return /* @__PURE__ */ jsxs14(
|
|
819
965
|
Card,
|
|
820
966
|
{
|
|
821
967
|
id: scenario.id,
|
|
@@ -823,10 +969,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
823
969
|
...hideTitle ? { "aria-label": `${scenario.title} \u2014 ${label}` } : { "aria-labelledby": titleId },
|
|
824
970
|
className: "gap-0 py-0",
|
|
825
971
|
children: [
|
|
826
|
-
/* @__PURE__ */
|
|
827
|
-
/* @__PURE__ */
|
|
828
|
-
hideTitle ? null : /* @__PURE__ */
|
|
829
|
-
collapsible ? /* @__PURE__ */
|
|
972
|
+
/* @__PURE__ */ jsxs14(CardHeader, { className: "px-4 py-3", children: [
|
|
973
|
+
/* @__PURE__ */ jsxs14("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
974
|
+
hideTitle ? null : /* @__PURE__ */ jsxs14("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
975
|
+
collapsible ? /* @__PURE__ */ jsx22(
|
|
830
976
|
"button",
|
|
831
977
|
{
|
|
832
978
|
type: "button",
|
|
@@ -835,17 +981,18 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
835
981
|
"aria-controls": bodyId,
|
|
836
982
|
"aria-label": `Toggle ${scenario.title}`,
|
|
837
983
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
838
|
-
children: /* @__PURE__ */
|
|
984
|
+
children: /* @__PURE__ */ jsx22("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
839
985
|
}
|
|
840
986
|
) : null,
|
|
841
|
-
/* @__PURE__ */
|
|
842
|
-
/* @__PURE__ */
|
|
987
|
+
/* @__PURE__ */ jsx22("span", { "aria-hidden": true, className: cn("text-sm leading-none", STATUS_GLYPH_COLOR[scenario.status]), children: STATUS_GLYPH[scenario.status] }),
|
|
988
|
+
/* @__PURE__ */ jsx22("span", { children: scenario.title })
|
|
843
989
|
] }),
|
|
844
|
-
/* @__PURE__ */
|
|
845
|
-
|
|
846
|
-
/* @__PURE__ */
|
|
847
|
-
|
|
848
|
-
|
|
990
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
991
|
+
/* @__PURE__ */ jsx22(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
992
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ jsx22("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
|
|
993
|
+
/* @__PURE__ */ jsx22(Badge, { variant: STATUS_BADGE[scenario.status], "aria-label": `Status: ${label}`, children: label }),
|
|
994
|
+
actions ? /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-0.5", children: [
|
|
995
|
+
/* @__PURE__ */ jsx22(
|
|
849
996
|
"button",
|
|
850
997
|
{
|
|
851
998
|
type: "button",
|
|
@@ -856,7 +1003,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
856
1003
|
children: "link"
|
|
857
1004
|
}
|
|
858
1005
|
),
|
|
859
|
-
/* @__PURE__ */
|
|
1006
|
+
/* @__PURE__ */ jsx22(
|
|
860
1007
|
"button",
|
|
861
1008
|
{
|
|
862
1009
|
type: "button",
|
|
@@ -867,7 +1014,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
867
1014
|
children: "md"
|
|
868
1015
|
}
|
|
869
1016
|
),
|
|
870
|
-
/* @__PURE__ */
|
|
1017
|
+
/* @__PURE__ */ jsx22(
|
|
871
1018
|
"button",
|
|
872
1019
|
{
|
|
873
1020
|
type: "button",
|
|
@@ -881,13 +1028,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
881
1028
|
] }) : null
|
|
882
1029
|
] })
|
|
883
1030
|
] }),
|
|
884
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */
|
|
885
|
-
scenario.tags.map((t) => /* @__PURE__ */
|
|
886
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */
|
|
1031
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs14("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
1032
|
+
scenario.tags.map((t) => /* @__PURE__ */ jsx22("li", { children: /* @__PURE__ */ jsx22(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
1033
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx22("li", { children: ticket.url ? /* @__PURE__ */ jsx22("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx22(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx22(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
887
1034
|
] }) : null
|
|
888
1035
|
] }),
|
|
889
|
-
/* @__PURE__ */
|
|
890
|
-
scenario.errorMessage ? /* @__PURE__ */
|
|
1036
|
+
/* @__PURE__ */ jsxs14(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1037
|
+
scenario.errorMessage ? /* @__PURE__ */ jsx22(
|
|
891
1038
|
"pre",
|
|
892
1039
|
{
|
|
893
1040
|
role: "alert",
|
|
@@ -895,10 +1042,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
895
1042
|
children: scenario.errorMessage
|
|
896
1043
|
}
|
|
897
1044
|
) : null,
|
|
898
|
-
/* @__PURE__ */
|
|
899
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */
|
|
900
|
-
/* @__PURE__ */
|
|
901
|
-
/* @__PURE__ */
|
|
1045
|
+
/* @__PURE__ */ jsx22(ReportSteps, { scenario }),
|
|
1046
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx22("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx22(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
1047
|
+
/* @__PURE__ */ jsx22(ReportAttachments, { attachments: scenario.attachments }),
|
|
1048
|
+
/* @__PURE__ */ jsx22(ReportTrace, { spans: scenario.otelSpans })
|
|
902
1049
|
] })
|
|
903
1050
|
]
|
|
904
1051
|
}
|
|
@@ -906,13 +1053,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
906
1053
|
}
|
|
907
1054
|
|
|
908
1055
|
// src/components/ReportScenarioList.tsx
|
|
909
|
-
import { Fragment as
|
|
1056
|
+
import { Fragment as Fragment8, jsx as jsx23 } from "react/jsx-runtime";
|
|
910
1057
|
function ReportScenarioList({ feature }) {
|
|
911
|
-
return /* @__PURE__ */
|
|
1058
|
+
return /* @__PURE__ */ jsx23(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx23(ReportScenario, { scenario }, scenario.id)) });
|
|
912
1059
|
}
|
|
913
1060
|
|
|
914
1061
|
// src/components/ReportFeature.tsx
|
|
915
|
-
import { jsx as
|
|
1062
|
+
import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
916
1063
|
function ReportFeature({ feature }) {
|
|
917
1064
|
const titleId = `${feature.id}-title`;
|
|
918
1065
|
const bodyId = `${feature.id}-body`;
|
|
@@ -920,7 +1067,7 @@ function ReportFeature({ feature }) {
|
|
|
920
1067
|
const skipped = s.skipped + s.pending;
|
|
921
1068
|
const collapse = useCollapse();
|
|
922
1069
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
923
|
-
return /* @__PURE__ */
|
|
1070
|
+
return /* @__PURE__ */ jsxs15(
|
|
924
1071
|
"section",
|
|
925
1072
|
{
|
|
926
1073
|
id: feature.id,
|
|
@@ -928,9 +1075,9 @@ function ReportFeature({ feature }) {
|
|
|
928
1075
|
"aria-labelledby": titleId,
|
|
929
1076
|
className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm",
|
|
930
1077
|
children: [
|
|
931
|
-
/* @__PURE__ */
|
|
932
|
-
/* @__PURE__ */
|
|
933
|
-
collapse ? /* @__PURE__ */
|
|
1078
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex items-center justify-between gap-4 border-b border-border bg-muted/40 px-4 py-3", children: [
|
|
1079
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1080
|
+
collapse ? /* @__PURE__ */ jsx24(
|
|
934
1081
|
"button",
|
|
935
1082
|
{
|
|
936
1083
|
type: "button",
|
|
@@ -939,30 +1086,30 @@ function ReportFeature({ feature }) {
|
|
|
939
1086
|
"aria-controls": bodyId,
|
|
940
1087
|
"aria-label": `Toggle ${feature.title}`,
|
|
941
1088
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
942
|
-
children: /* @__PURE__ */
|
|
1089
|
+
children: /* @__PURE__ */ jsx24("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
943
1090
|
}
|
|
944
1091
|
) : null,
|
|
945
|
-
/* @__PURE__ */
|
|
946
|
-
/* @__PURE__ */
|
|
947
|
-
/* @__PURE__ */
|
|
1092
|
+
/* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
|
|
1093
|
+
/* @__PURE__ */ jsx24("h2", { id: titleId, className: "text-sm font-semibold tracking-tight text-foreground", children: feature.title }),
|
|
1094
|
+
/* @__PURE__ */ jsx24("p", { className: "mt-0.5 truncate font-mono text-xs text-muted-foreground", children: feature.sourceFile })
|
|
948
1095
|
] })
|
|
949
1096
|
] }),
|
|
950
|
-
/* @__PURE__ */
|
|
1097
|
+
/* @__PURE__ */ jsxs15(
|
|
951
1098
|
"div",
|
|
952
1099
|
{
|
|
953
1100
|
role: "img",
|
|
954
1101
|
className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
|
|
955
1102
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
956
1103
|
children: [
|
|
957
|
-
/* @__PURE__ */
|
|
1104
|
+
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-pass", children: [
|
|
958
1105
|
"\u2713",
|
|
959
1106
|
s.passed
|
|
960
1107
|
] }),
|
|
961
|
-
/* @__PURE__ */
|
|
1108
|
+
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-fail", children: [
|
|
962
1109
|
"\u2717",
|
|
963
1110
|
s.failed
|
|
964
1111
|
] }),
|
|
965
|
-
/* @__PURE__ */
|
|
1112
|
+
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-skip", children: [
|
|
966
1113
|
"\u25CB",
|
|
967
1114
|
skipped
|
|
968
1115
|
] })
|
|
@@ -970,24 +1117,24 @@ function ReportFeature({ feature }) {
|
|
|
970
1117
|
}
|
|
971
1118
|
)
|
|
972
1119
|
] }),
|
|
973
|
-
/* @__PURE__ */
|
|
1120
|
+
/* @__PURE__ */ jsx24("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-2 p-3", children: /* @__PURE__ */ jsx24(ReportScenarioList, { feature }) })
|
|
974
1121
|
]
|
|
975
1122
|
}
|
|
976
1123
|
);
|
|
977
1124
|
}
|
|
978
1125
|
|
|
979
1126
|
// src/components/ReportFeatureList.tsx
|
|
980
|
-
import { Fragment as
|
|
1127
|
+
import { Fragment as Fragment9, jsx as jsx25 } from "react/jsx-runtime";
|
|
981
1128
|
function ReportFeatureList() {
|
|
982
1129
|
const report = useReport();
|
|
983
|
-
return /* @__PURE__ */
|
|
1130
|
+
return /* @__PURE__ */ jsx25(Fragment9, { children: report.features.map((feature) => /* @__PURE__ */ jsx25(ReportFeature, { feature }, feature.id)) });
|
|
984
1131
|
}
|
|
985
1132
|
|
|
986
1133
|
// src/components/ui/empty.tsx
|
|
987
1134
|
import { cva as cva2 } from "class-variance-authority";
|
|
988
|
-
import { jsx as
|
|
1135
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
989
1136
|
function Empty({ className, ...props }) {
|
|
990
|
-
return /* @__PURE__ */
|
|
1137
|
+
return /* @__PURE__ */ jsx26(
|
|
991
1138
|
"div",
|
|
992
1139
|
{
|
|
993
1140
|
"data-slot": "empty",
|
|
@@ -1000,7 +1147,7 @@ function Empty({ className, ...props }) {
|
|
|
1000
1147
|
);
|
|
1001
1148
|
}
|
|
1002
1149
|
function EmptyHeader({ className, ...props }) {
|
|
1003
|
-
return /* @__PURE__ */
|
|
1150
|
+
return /* @__PURE__ */ jsx26(
|
|
1004
1151
|
"div",
|
|
1005
1152
|
{
|
|
1006
1153
|
"data-slot": "empty-header",
|
|
@@ -1027,7 +1174,7 @@ var emptyMediaVariants = cva2(
|
|
|
1027
1174
|
}
|
|
1028
1175
|
);
|
|
1029
1176
|
function EmptyTitle({ className, ...props }) {
|
|
1030
|
-
return /* @__PURE__ */
|
|
1177
|
+
return /* @__PURE__ */ jsx26(
|
|
1031
1178
|
"div",
|
|
1032
1179
|
{
|
|
1033
1180
|
"data-slot": "empty-title",
|
|
@@ -1038,16 +1185,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1038
1185
|
}
|
|
1039
1186
|
|
|
1040
1187
|
// src/components/ReportEmpty.tsx
|
|
1041
|
-
import { jsx as
|
|
1188
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
1042
1189
|
function ReportEmpty({ message }) {
|
|
1043
|
-
return /* @__PURE__ */
|
|
1190
|
+
return /* @__PURE__ */ jsx27(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx27(EmptyHeader, { children: /* @__PURE__ */ jsx27(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1044
1191
|
}
|
|
1045
1192
|
|
|
1046
1193
|
// src/components/ReportSummary.tsx
|
|
1047
|
-
import { Fragment as
|
|
1194
|
+
import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1048
1195
|
function ReportSummary({ className }) {
|
|
1049
1196
|
const report = useReport();
|
|
1050
|
-
return /* @__PURE__ */
|
|
1197
|
+
return /* @__PURE__ */ jsx28(
|
|
1051
1198
|
ReportSummaryView,
|
|
1052
1199
|
{
|
|
1053
1200
|
summary: report.summary,
|
|
@@ -1057,32 +1204,32 @@ function ReportSummary({ className }) {
|
|
|
1057
1204
|
);
|
|
1058
1205
|
}
|
|
1059
1206
|
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
1060
|
-
return /* @__PURE__ */
|
|
1061
|
-
/* @__PURE__ */
|
|
1062
|
-
/* @__PURE__ */
|
|
1207
|
+
return /* @__PURE__ */ jsxs16("p", { className: cn("text-sm text-muted-foreground", className), "aria-label": ariaLabel, children: [
|
|
1208
|
+
/* @__PURE__ */ jsxs16("span", { children: [
|
|
1209
|
+
/* @__PURE__ */ jsx28("strong", { className: "font-semibold text-foreground", children: summary.total }),
|
|
1063
1210
|
" scenario",
|
|
1064
1211
|
summary.total === 1 ? "" : "s"
|
|
1065
1212
|
] }),
|
|
1066
1213
|
" \xB7 ",
|
|
1067
|
-
/* @__PURE__ */
|
|
1214
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "passed", className: "text-pass", children: [
|
|
1068
1215
|
summary.passed,
|
|
1069
1216
|
" passed"
|
|
1070
1217
|
] }),
|
|
1071
1218
|
" \xB7 ",
|
|
1072
|
-
/* @__PURE__ */
|
|
1219
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "failed", className: "text-fail", children: [
|
|
1073
1220
|
summary.failed,
|
|
1074
1221
|
" failed"
|
|
1075
1222
|
] }),
|
|
1076
|
-
summary.skipped > 0 ? /* @__PURE__ */
|
|
1223
|
+
summary.skipped > 0 ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
1077
1224
|
" \xB7 ",
|
|
1078
|
-
/* @__PURE__ */
|
|
1225
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "skipped", className: "text-skip", children: [
|
|
1079
1226
|
summary.skipped,
|
|
1080
1227
|
" skipped"
|
|
1081
1228
|
] })
|
|
1082
1229
|
] }) : null,
|
|
1083
|
-
summary.pending > 0 ? /* @__PURE__ */
|
|
1230
|
+
summary.pending > 0 ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
1084
1231
|
" \xB7 ",
|
|
1085
|
-
/* @__PURE__ */
|
|
1232
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "pending", className: "text-pend", children: [
|
|
1086
1233
|
summary.pending,
|
|
1087
1234
|
" pending"
|
|
1088
1235
|
] })
|
|
@@ -1090,69 +1237,135 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1090
1237
|
] });
|
|
1091
1238
|
}
|
|
1092
1239
|
|
|
1240
|
+
// src/lib/provenance.ts
|
|
1241
|
+
function reportLastRunMs(report) {
|
|
1242
|
+
if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
|
|
1243
|
+
if (report.startedAtMs && report.startedAtMs > 0) return report.startedAtMs;
|
|
1244
|
+
return void 0;
|
|
1245
|
+
}
|
|
1246
|
+
function formatRelativeAge(thenMs, nowMs) {
|
|
1247
|
+
const deltaMs = Math.max(0, nowMs - thenMs);
|
|
1248
|
+
const minutes = Math.floor(deltaMs / 6e4);
|
|
1249
|
+
if (minutes < 1) return "just now";
|
|
1250
|
+
if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
|
|
1251
|
+
const hours = Math.floor(minutes / 60);
|
|
1252
|
+
if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
|
|
1253
|
+
const days = Math.floor(hours / 24);
|
|
1254
|
+
return `${days} day${days === 1 ? "" : "s"} ago`;
|
|
1255
|
+
}
|
|
1256
|
+
function isReportStale(lastRunMs, staleAfterDays, nowMs) {
|
|
1257
|
+
if (lastRunMs === void 0 || staleAfterDays <= 0) return false;
|
|
1258
|
+
return nowMs - lastRunMs >= staleAfterDays * 864e5;
|
|
1259
|
+
}
|
|
1260
|
+
var CI_DISPLAY_NAMES = {
|
|
1261
|
+
github: "GitHub Actions",
|
|
1262
|
+
gitlab: "GitLab CI",
|
|
1263
|
+
circleci: "CircleCI",
|
|
1264
|
+
jenkins: "Jenkins",
|
|
1265
|
+
azure: "Azure DevOps",
|
|
1266
|
+
buildkite: "Buildkite",
|
|
1267
|
+
travis: "Travis CI",
|
|
1268
|
+
ci: "CI"
|
|
1269
|
+
};
|
|
1270
|
+
function ciDisplayName(name) {
|
|
1271
|
+
return CI_DISPLAY_NAMES[name] ?? name;
|
|
1272
|
+
}
|
|
1273
|
+
function commitUrl(ci, sha) {
|
|
1274
|
+
const commitSha = ci?.commitSha ?? sha;
|
|
1275
|
+
if (!ci?.url || !commitSha) return void 0;
|
|
1276
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1277
|
+
if (github) return `${github[1]}/commit/${commitSha}`;
|
|
1278
|
+
const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
|
|
1279
|
+
if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
|
|
1280
|
+
return void 0;
|
|
1281
|
+
}
|
|
1282
|
+
function prUrl(ci) {
|
|
1283
|
+
if (!ci?.url || !ci.prNumber) return void 0;
|
|
1284
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1285
|
+
if (github) return `${github[1]}/pull/${ci.prNumber}`;
|
|
1286
|
+
return void 0;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1093
1289
|
// src/components/ReportMeta.tsx
|
|
1094
|
-
import {
|
|
1095
|
-
import { jsx as jsx28, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1290
|
+
import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1096
1291
|
function ReportMeta() {
|
|
1097
1292
|
const report = useReport();
|
|
1098
1293
|
const items = [];
|
|
1294
|
+
const linkClass = "text-primary underline underline-offset-2";
|
|
1099
1295
|
if (report.startedAtMs) {
|
|
1100
1296
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1101
1297
|
}
|
|
1102
1298
|
if (report.durationMs) {
|
|
1103
|
-
items.push({ label: "Duration", value:
|
|
1299
|
+
items.push({ label: "Duration", value: formatDuration(report.durationMs) });
|
|
1104
1300
|
}
|
|
1105
1301
|
if (report.packageVersion) {
|
|
1106
1302
|
items.push({ label: "Version", value: report.packageVersion });
|
|
1107
1303
|
}
|
|
1108
|
-
if (report.
|
|
1109
|
-
items.push({ label: "
|
|
1304
|
+
if (report.ci?.branch) {
|
|
1305
|
+
items.push({ label: "Branch", value: report.ci.branch });
|
|
1306
|
+
}
|
|
1307
|
+
const sha = report.ci?.commitSha ?? report.gitSha;
|
|
1308
|
+
if (sha) {
|
|
1309
|
+
const url = commitUrl(report.ci, report.gitSha);
|
|
1310
|
+
const short = sha.slice(0, 8);
|
|
1311
|
+
items.push({
|
|
1312
|
+
label: "Commit",
|
|
1313
|
+
value: url ? /* @__PURE__ */ jsx29("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1314
|
+
});
|
|
1110
1315
|
}
|
|
1111
1316
|
if (report.ci) {
|
|
1112
1317
|
items.push({
|
|
1113
1318
|
label: "CI",
|
|
1114
|
-
value: report.ci.url ? /* @__PURE__ */
|
|
1115
|
-
report.ci.name,
|
|
1319
|
+
value: report.ci.url ? /* @__PURE__ */ jsxs17("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1320
|
+
ciDisplayName(report.ci.name),
|
|
1116
1321
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1117
|
-
] }) : `${report.ci.name}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1322
|
+
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1325
|
+
if (report.ci?.prNumber) {
|
|
1326
|
+
const url = prUrl(report.ci);
|
|
1327
|
+
const label = `#${report.ci.prNumber}`;
|
|
1328
|
+
items.push({
|
|
1329
|
+
label: "PR",
|
|
1330
|
+
value: url ? /* @__PURE__ */ jsx29("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1118
1331
|
});
|
|
1119
1332
|
}
|
|
1120
1333
|
if (items.length === 0) return null;
|
|
1121
|
-
return /* @__PURE__ */
|
|
1122
|
-
/* @__PURE__ */
|
|
1123
|
-
/* @__PURE__ */
|
|
1334
|
+
return /* @__PURE__ */ jsx29("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__ */ jsxs17("div", { className: "flex gap-1.5", children: [
|
|
1335
|
+
/* @__PURE__ */ jsx29("dt", { className: "font-medium", children: item.label }),
|
|
1336
|
+
/* @__PURE__ */ jsx29("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1124
1337
|
] }, item.label)) });
|
|
1125
1338
|
}
|
|
1126
1339
|
|
|
1127
1340
|
// src/components/ReportSchemaError.tsx
|
|
1128
|
-
import { jsx as
|
|
1341
|
+
import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1129
1342
|
function ReportSchemaError({ error }) {
|
|
1130
|
-
return /* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1132
|
-
/* @__PURE__ */
|
|
1133
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */
|
|
1343
|
+
return /* @__PURE__ */ jsxs18("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1344
|
+
/* @__PURE__ */ jsx30("p", { children: /* @__PURE__ */ jsx30("strong", { children: "Report could not be displayed." }) }),
|
|
1345
|
+
/* @__PURE__ */ jsx30("p", { children: error.message }),
|
|
1346
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs18("p", { children: [
|
|
1134
1347
|
"The report bundle is newer than this version of ",
|
|
1135
|
-
/* @__PURE__ */
|
|
1348
|
+
/* @__PURE__ */ jsx30("code", { children: "executable-stories-react" }),
|
|
1136
1349
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1137
1350
|
] }) : null,
|
|
1138
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */
|
|
1139
|
-
/* @__PURE__ */
|
|
1351
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs18("details", { children: [
|
|
1352
|
+
/* @__PURE__ */ jsxs18("summary", { children: [
|
|
1140
1353
|
error.issues.length,
|
|
1141
1354
|
" validation issue",
|
|
1142
1355
|
error.issues.length === 1 ? "" : "s"
|
|
1143
1356
|
] }),
|
|
1144
|
-
/* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsx30("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1145
1358
|
] }) : null
|
|
1146
1359
|
] });
|
|
1147
1360
|
}
|
|
1148
1361
|
|
|
1149
1362
|
// src/components/ReportShell.tsx
|
|
1150
|
-
import { Fragment as
|
|
1363
|
+
import { Fragment as Fragment11, jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1151
1364
|
function ReportTitleBlock({ title }) {
|
|
1152
|
-
return /* @__PURE__ */
|
|
1153
|
-
/* @__PURE__ */
|
|
1154
|
-
/* @__PURE__ */
|
|
1155
|
-
/* @__PURE__ */
|
|
1365
|
+
return /* @__PURE__ */ jsxs19(Fragment11, { children: [
|
|
1366
|
+
/* @__PURE__ */ jsx31("h1", { children: title ?? "Story Report" }),
|
|
1367
|
+
/* @__PURE__ */ jsx31(ReportSummary, {}),
|
|
1368
|
+
/* @__PURE__ */ jsx31(ReportMeta, {})
|
|
1156
1369
|
] });
|
|
1157
1370
|
}
|
|
1158
1371
|
function ReportErrorShell({
|
|
@@ -1161,7 +1374,7 @@ function ReportErrorShell({
|
|
|
1161
1374
|
title,
|
|
1162
1375
|
dataTheme
|
|
1163
1376
|
}) {
|
|
1164
|
-
return /* @__PURE__ */
|
|
1377
|
+
return /* @__PURE__ */ jsx31("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx31(ReportSchemaError, { error }) });
|
|
1165
1378
|
}
|
|
1166
1379
|
|
|
1167
1380
|
// src/interactive/ReportSearch.tsx
|
|
@@ -1169,7 +1382,7 @@ import {
|
|
|
1169
1382
|
forwardRef,
|
|
1170
1383
|
useId
|
|
1171
1384
|
} from "react";
|
|
1172
|
-
import { jsx as
|
|
1385
|
+
import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1173
1386
|
var ReportSearch = forwardRef(
|
|
1174
1387
|
function ReportSearch2(props, ref) {
|
|
1175
1388
|
const {
|
|
@@ -1191,9 +1404,9 @@ var ReportSearch = forwardRef(
|
|
|
1191
1404
|
e.preventDefault();
|
|
1192
1405
|
}
|
|
1193
1406
|
}
|
|
1194
|
-
return /* @__PURE__ */
|
|
1195
|
-
/* @__PURE__ */
|
|
1196
|
-
/* @__PURE__ */
|
|
1407
|
+
return /* @__PURE__ */ jsxs20("div", { className: ["es-search", className].filter(Boolean).join(" "), children: [
|
|
1408
|
+
/* @__PURE__ */ jsx32("label", { htmlFor: inputId, className: "es-search-label", children: "Search" }),
|
|
1409
|
+
/* @__PURE__ */ jsx32(
|
|
1197
1410
|
"input",
|
|
1198
1411
|
{
|
|
1199
1412
|
ref,
|
|
@@ -1208,7 +1421,7 @@ var ReportSearch = forwardRef(
|
|
|
1208
1421
|
"aria-keyshortcuts": "/"
|
|
1209
1422
|
}
|
|
1210
1423
|
),
|
|
1211
|
-
showCounts ? /* @__PURE__ */
|
|
1424
|
+
showCounts ? /* @__PURE__ */ jsx32("span", { className: "es-search-counts", "aria-live": "polite", children: value ? `${matchedCount} of ${totalCount}` : `${totalCount} total` }) : null
|
|
1212
1425
|
] });
|
|
1213
1426
|
}
|
|
1214
1427
|
);
|
|
@@ -1237,7 +1450,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
1237
1450
|
}
|
|
1238
1451
|
|
|
1239
1452
|
// src/interactive/ReportFailureBanner.tsx
|
|
1240
|
-
import { jsx as
|
|
1453
|
+
import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1241
1454
|
function ReportFailureBanner({ failures }) {
|
|
1242
1455
|
const first = failures[0];
|
|
1243
1456
|
const jumpToFirst = useCallback2(() => {
|
|
@@ -1245,7 +1458,7 @@ function ReportFailureBanner({ failures }) {
|
|
|
1245
1458
|
scrollToScenarioId(first.scenarioId);
|
|
1246
1459
|
}, [first]);
|
|
1247
1460
|
if (failures.length === 0) return null;
|
|
1248
|
-
return /* @__PURE__ */
|
|
1461
|
+
return /* @__PURE__ */ jsxs21(
|
|
1249
1462
|
"div",
|
|
1250
1463
|
{
|
|
1251
1464
|
className: "es-failure-banner",
|
|
@@ -1253,13 +1466,13 @@ function ReportFailureBanner({ failures }) {
|
|
|
1253
1466
|
"aria-live": "polite",
|
|
1254
1467
|
"aria-label": "Failure summary",
|
|
1255
1468
|
children: [
|
|
1256
|
-
/* @__PURE__ */
|
|
1257
|
-
/* @__PURE__ */
|
|
1469
|
+
/* @__PURE__ */ jsxs21("span", { className: "es-failure-banner-text", children: [
|
|
1470
|
+
/* @__PURE__ */ jsx33("strong", { children: failures.length }),
|
|
1258
1471
|
" ",
|
|
1259
1472
|
"failure",
|
|
1260
1473
|
failures.length === 1 ? "" : "s"
|
|
1261
1474
|
] }),
|
|
1262
|
-
/* @__PURE__ */
|
|
1475
|
+
/* @__PURE__ */ jsx33(
|
|
1263
1476
|
"button",
|
|
1264
1477
|
{
|
|
1265
1478
|
type: "button",
|
|
@@ -1274,9 +1487,101 @@ function ReportFailureBanner({ failures }) {
|
|
|
1274
1487
|
);
|
|
1275
1488
|
}
|
|
1276
1489
|
|
|
1490
|
+
// src/interactive/ReportFreshness.tsx
|
|
1491
|
+
import { useEffect, useState as useState2 } from "react";
|
|
1492
|
+
import { Fragment as Fragment12, jsx as jsx34, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1493
|
+
function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
1494
|
+
const [now, setNow] = useState2(nowMs);
|
|
1495
|
+
useEffect(() => {
|
|
1496
|
+
if (nowMs === void 0) setNow(Date.now());
|
|
1497
|
+
}, [nowMs]);
|
|
1498
|
+
if (lastRunMs === void 0 || now === void 0) return null;
|
|
1499
|
+
const age = formatRelativeAge(lastRunMs, now);
|
|
1500
|
+
const runLink = ciUrl ? /* @__PURE__ */ jsx34(
|
|
1501
|
+
"a",
|
|
1502
|
+
{
|
|
1503
|
+
href: ciUrl,
|
|
1504
|
+
target: "_blank",
|
|
1505
|
+
rel: "noreferrer noopener",
|
|
1506
|
+
className: "underline underline-offset-2",
|
|
1507
|
+
children: "View CI run"
|
|
1508
|
+
}
|
|
1509
|
+
) : null;
|
|
1510
|
+
if (isReportStale(lastRunMs, staleAfterDays, now)) {
|
|
1511
|
+
return /* @__PURE__ */ jsxs22("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
|
|
1512
|
+
/* @__PURE__ */ jsxs22("span", { children: [
|
|
1513
|
+
/* @__PURE__ */ jsxs22("strong", { children: [
|
|
1514
|
+
"Last verified ",
|
|
1515
|
+
age,
|
|
1516
|
+
"."
|
|
1517
|
+
] }),
|
|
1518
|
+
" This report may no longer match the current behavior \u2014 look for a newer run."
|
|
1519
|
+
] }),
|
|
1520
|
+
runLink
|
|
1521
|
+
] });
|
|
1522
|
+
}
|
|
1523
|
+
return /* @__PURE__ */ jsxs22("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
|
|
1524
|
+
"Verified ",
|
|
1525
|
+
age,
|
|
1526
|
+
runLink ? /* @__PURE__ */ jsxs22(Fragment12, { children: [
|
|
1527
|
+
" \xB7 ",
|
|
1528
|
+
runLink
|
|
1529
|
+
] }) : null
|
|
1530
|
+
] });
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// src/interactive/ReportLastRunDelta.tsx
|
|
1534
|
+
import { Fragment as Fragment13, jsx as jsx35, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1535
|
+
var MAX_LINKS = 5;
|
|
1536
|
+
function ScenarioLinks({ scenarios, tone }) {
|
|
1537
|
+
return /* @__PURE__ */ jsxs23(Fragment13, { children: [
|
|
1538
|
+
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ jsxs23("span", { children: [
|
|
1539
|
+
i > 0 ? ", " : "",
|
|
1540
|
+
/* @__PURE__ */ jsx35("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
|
|
1541
|
+
] }, s.id)),
|
|
1542
|
+
scenarios.length > MAX_LINKS ? /* @__PURE__ */ jsxs23("span", { children: [
|
|
1543
|
+
" and ",
|
|
1544
|
+
scenarios.length - MAX_LINKS,
|
|
1545
|
+
" more"
|
|
1546
|
+
] }) : null
|
|
1547
|
+
] });
|
|
1548
|
+
}
|
|
1549
|
+
function ReportLastRunDelta({
|
|
1550
|
+
history: history2,
|
|
1551
|
+
report
|
|
1552
|
+
}) {
|
|
1553
|
+
if (!history2) return null;
|
|
1554
|
+
const scenarios = report.features.flatMap((f) => f.scenarios.map((s) => ({ id: s.id, title: s.title })));
|
|
1555
|
+
const delta = diffSinceLastRun(history2, scenarios);
|
|
1556
|
+
if (!delta) return null;
|
|
1557
|
+
const { newlyFailing, newlyPassing, added } = delta;
|
|
1558
|
+
if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
|
|
1559
|
+
return /* @__PURE__ */ jsx35("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
|
|
1560
|
+
}
|
|
1561
|
+
return /* @__PURE__ */ jsxs23("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
|
|
1562
|
+
/* @__PURE__ */ jsx35("span", { className: "font-medium text-foreground", children: "Since last run:" }),
|
|
1563
|
+
newlyFailing.length > 0 ? /* @__PURE__ */ jsxs23("span", { className: "ml-2 text-fail", children: [
|
|
1564
|
+
newlyFailing.length,
|
|
1565
|
+
" newly failing (",
|
|
1566
|
+
/* @__PURE__ */ jsx35(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
|
|
1567
|
+
")"
|
|
1568
|
+
] }) : null,
|
|
1569
|
+
newlyPassing.length > 0 ? /* @__PURE__ */ jsxs23("span", { className: "ml-2 text-pass", children: [
|
|
1570
|
+
newlyPassing.length,
|
|
1571
|
+
" fixed (",
|
|
1572
|
+
/* @__PURE__ */ jsx35(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
|
|
1573
|
+
")"
|
|
1574
|
+
] }) : null,
|
|
1575
|
+
added.length > 0 ? /* @__PURE__ */ jsxs23("span", { className: "ml-2", children: [
|
|
1576
|
+
added.length,
|
|
1577
|
+
" new"
|
|
1578
|
+
] }) : null
|
|
1579
|
+
] });
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1277
1582
|
// src/interactive/ReportShortcutsHelp.tsx
|
|
1278
|
-
import { useEffect, useRef } from "react";
|
|
1279
|
-
import { jsx as
|
|
1583
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
1584
|
+
import { jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1280
1585
|
var SHORTCUTS = [
|
|
1281
1586
|
{ keys: "/", description: "Focus search" },
|
|
1282
1587
|
{ keys: "f", description: "Jump to next failure" },
|
|
@@ -1286,13 +1591,13 @@ var SHORTCUTS = [
|
|
|
1286
1591
|
];
|
|
1287
1592
|
function ReportShortcutsHelp({ open, onClose }) {
|
|
1288
1593
|
const dialogRef = useRef(null);
|
|
1289
|
-
|
|
1594
|
+
useEffect2(() => {
|
|
1290
1595
|
const dialog = dialogRef.current;
|
|
1291
1596
|
if (!dialog) return;
|
|
1292
1597
|
if (open && !dialog.open) dialog.showModal();
|
|
1293
1598
|
else if (!open && dialog.open) dialog.close();
|
|
1294
1599
|
}, [open]);
|
|
1295
|
-
return /* @__PURE__ */
|
|
1600
|
+
return /* @__PURE__ */ jsxs24(
|
|
1296
1601
|
"dialog",
|
|
1297
1602
|
{
|
|
1298
1603
|
ref: dialogRef,
|
|
@@ -1300,19 +1605,19 @@ function ReportShortcutsHelp({ open, onClose }) {
|
|
|
1300
1605
|
"aria-label": "Keyboard shortcuts",
|
|
1301
1606
|
onClose,
|
|
1302
1607
|
children: [
|
|
1303
|
-
/* @__PURE__ */
|
|
1304
|
-
/* @__PURE__ */
|
|
1305
|
-
/* @__PURE__ */
|
|
1306
|
-
/* @__PURE__ */
|
|
1608
|
+
/* @__PURE__ */ jsx36("h2", { children: "Keyboard shortcuts" }),
|
|
1609
|
+
/* @__PURE__ */ jsx36("dl", { children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs24("div", { children: [
|
|
1610
|
+
/* @__PURE__ */ jsx36("dt", { children: /* @__PURE__ */ jsx36("kbd", { children: s.keys }) }),
|
|
1611
|
+
/* @__PURE__ */ jsx36("dd", { children: s.description })
|
|
1307
1612
|
] }, s.keys)) }),
|
|
1308
|
-
/* @__PURE__ */
|
|
1613
|
+
/* @__PURE__ */ jsx36("form", { method: "dialog", children: /* @__PURE__ */ jsx36("button", { type: "submit", className: "es-shortcuts-close", children: "Close" }) })
|
|
1309
1614
|
]
|
|
1310
1615
|
}
|
|
1311
1616
|
);
|
|
1312
1617
|
}
|
|
1313
1618
|
|
|
1314
1619
|
// src/interactive/use-keyboard-shortcuts.ts
|
|
1315
|
-
import { useEffect as
|
|
1620
|
+
import { useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
1316
1621
|
function isEditableTarget(target) {
|
|
1317
1622
|
if (!(target instanceof HTMLElement)) return false;
|
|
1318
1623
|
const tag = target.tagName;
|
|
@@ -1323,7 +1628,7 @@ function isEditableTarget(target) {
|
|
|
1323
1628
|
function useKeyboardShortcuts(handlers) {
|
|
1324
1629
|
const ref = useRef2(handlers);
|
|
1325
1630
|
ref.current = handlers;
|
|
1326
|
-
|
|
1631
|
+
useEffect3(() => {
|
|
1327
1632
|
function onKeyDown(e) {
|
|
1328
1633
|
const h = ref.current;
|
|
1329
1634
|
if (e.key === "Escape") {
|
|
@@ -1367,9 +1672,9 @@ function useKeyboardShortcuts(handlers) {
|
|
|
1367
1672
|
}
|
|
1368
1673
|
|
|
1369
1674
|
// src/interactive/use-deep-link-scroll.ts
|
|
1370
|
-
import { useEffect as
|
|
1675
|
+
import { useEffect as useEffect4 } from "react";
|
|
1371
1676
|
function useDeepLinkScroll() {
|
|
1372
|
-
|
|
1677
|
+
useEffect4(() => {
|
|
1373
1678
|
function scrollToHash() {
|
|
1374
1679
|
const hash = window.location.hash.replace(/^#/, "");
|
|
1375
1680
|
if (!hash) return;
|
|
@@ -1478,7 +1783,7 @@ function listFailures(report) {
|
|
|
1478
1783
|
}
|
|
1479
1784
|
|
|
1480
1785
|
// src/interactive/ReportFilters.tsx
|
|
1481
|
-
import { jsx as
|
|
1786
|
+
import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1482
1787
|
function ReportFilters({
|
|
1483
1788
|
statuses,
|
|
1484
1789
|
status,
|
|
@@ -1488,10 +1793,10 @@ function ReportFilters({
|
|
|
1488
1793
|
onToggleTag
|
|
1489
1794
|
}) {
|
|
1490
1795
|
if (statuses.length === 0 && tags.length === 0) return null;
|
|
1491
|
-
return /* @__PURE__ */
|
|
1492
|
-
statuses.length > 0 ? /* @__PURE__ */
|
|
1796
|
+
return /* @__PURE__ */ jsxs25("div", { className: "flex flex-col gap-2", children: [
|
|
1797
|
+
statuses.length > 0 ? /* @__PURE__ */ jsx37("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
|
|
1493
1798
|
const active = status === opt.key;
|
|
1494
|
-
return /* @__PURE__ */
|
|
1799
|
+
return /* @__PURE__ */ jsxs25(
|
|
1495
1800
|
"button",
|
|
1496
1801
|
{
|
|
1497
1802
|
type: "button",
|
|
@@ -1503,22 +1808,22 @@ function ReportFilters({
|
|
|
1503
1808
|
),
|
|
1504
1809
|
children: [
|
|
1505
1810
|
opt.label,
|
|
1506
|
-
/* @__PURE__ */
|
|
1811
|
+
/* @__PURE__ */ jsx37("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
|
|
1507
1812
|
]
|
|
1508
1813
|
},
|
|
1509
1814
|
opt.key
|
|
1510
1815
|
);
|
|
1511
1816
|
}) }) : null,
|
|
1512
|
-
tags.length > 0 ? /* @__PURE__ */
|
|
1817
|
+
tags.length > 0 ? /* @__PURE__ */ jsx37("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap gap-1.5", children: tags.map((tag) => {
|
|
1513
1818
|
const active = activeTags.includes(tag);
|
|
1514
|
-
return /* @__PURE__ */
|
|
1819
|
+
return /* @__PURE__ */ jsx37("li", { children: /* @__PURE__ */ jsx37(
|
|
1515
1820
|
"button",
|
|
1516
1821
|
{
|
|
1517
1822
|
type: "button",
|
|
1518
1823
|
"aria-pressed": active,
|
|
1519
1824
|
onClick: () => onToggleTag(tag),
|
|
1520
1825
|
className: "cursor-pointer",
|
|
1521
|
-
children: /* @__PURE__ */
|
|
1826
|
+
children: /* @__PURE__ */ jsx37(Badge, { variant: active ? "default" : "tag", children: tag })
|
|
1522
1827
|
}
|
|
1523
1828
|
) }, tag);
|
|
1524
1829
|
}) }) : null
|
|
@@ -1526,8 +1831,8 @@ function ReportFilters({
|
|
|
1526
1831
|
}
|
|
1527
1832
|
|
|
1528
1833
|
// src/interactive/ReportToc.tsx
|
|
1529
|
-
import { useEffect as
|
|
1530
|
-
import { jsx as
|
|
1834
|
+
import { useEffect as useEffect5, useMemo as useMemo2, useState as useState3 } from "react";
|
|
1835
|
+
import { jsx as jsx38, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
1531
1836
|
var DOT = {
|
|
1532
1837
|
passed: "text-pass",
|
|
1533
1838
|
failed: "text-fail",
|
|
@@ -1536,12 +1841,12 @@ var DOT = {
|
|
|
1536
1841
|
};
|
|
1537
1842
|
function ReportToc() {
|
|
1538
1843
|
const report = useReport();
|
|
1539
|
-
const [activeId, setActiveId] =
|
|
1844
|
+
const [activeId, setActiveId] = useState3(null);
|
|
1540
1845
|
const scenarioIds = useMemo2(
|
|
1541
1846
|
() => report.features.flatMap((f) => f.scenarios.map((s) => s.id)),
|
|
1542
1847
|
[report]
|
|
1543
1848
|
);
|
|
1544
|
-
|
|
1849
|
+
useEffect5(() => {
|
|
1545
1850
|
if (typeof IntersectionObserver === "undefined") return;
|
|
1546
1851
|
const els = scenarioIds.map((id) => document.getElementById(id)).filter((el) => el !== null);
|
|
1547
1852
|
if (els.length === 0) return;
|
|
@@ -1556,16 +1861,16 @@ function ReportToc() {
|
|
|
1556
1861
|
return () => observer.disconnect();
|
|
1557
1862
|
}, [scenarioIds]);
|
|
1558
1863
|
if (report.features.length === 0) return null;
|
|
1559
|
-
return /* @__PURE__ */
|
|
1864
|
+
return /* @__PURE__ */ jsx38(
|
|
1560
1865
|
"nav",
|
|
1561
1866
|
{
|
|
1562
1867
|
"aria-label": "Table of contents",
|
|
1563
1868
|
className: "sticky top-4 hidden h-fit w-56 shrink-0 self-start text-xs lg:block",
|
|
1564
|
-
children: /* @__PURE__ */
|
|
1565
|
-
/* @__PURE__ */
|
|
1566
|
-
/* @__PURE__ */
|
|
1869
|
+
children: /* @__PURE__ */ jsx38("ul", { className: "flex flex-col gap-3", children: report.features.map((feature) => /* @__PURE__ */ jsxs26("li", { children: [
|
|
1870
|
+
/* @__PURE__ */ jsx38("p", { className: "mb-1 truncate font-semibold text-foreground", children: feature.title }),
|
|
1871
|
+
/* @__PURE__ */ jsx38("ul", { className: "flex flex-col", children: feature.scenarios.map((scenario) => {
|
|
1567
1872
|
const active = activeId === scenario.id;
|
|
1568
|
-
return /* @__PURE__ */
|
|
1873
|
+
return /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs26(
|
|
1569
1874
|
"a",
|
|
1570
1875
|
{
|
|
1571
1876
|
href: `#${scenario.id}`,
|
|
@@ -1579,8 +1884,8 @@ function ReportToc() {
|
|
|
1579
1884
|
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
1580
1885
|
),
|
|
1581
1886
|
children: [
|
|
1582
|
-
/* @__PURE__ */
|
|
1583
|
-
/* @__PURE__ */
|
|
1887
|
+
/* @__PURE__ */ jsx38("span", { "aria-hidden": true, className: cn("shrink-0", DOT[scenario.status]), children: "\u2022" }),
|
|
1888
|
+
/* @__PURE__ */ jsx38("span", { className: "truncate", children: scenario.title })
|
|
1584
1889
|
]
|
|
1585
1890
|
}
|
|
1586
1891
|
) }, scenario.id);
|
|
@@ -1591,7 +1896,7 @@ function ReportToc() {
|
|
|
1591
1896
|
}
|
|
1592
1897
|
|
|
1593
1898
|
// src/interactive/use-theme.ts
|
|
1594
|
-
import { useCallback as useCallback3, useEffect as
|
|
1899
|
+
import { useCallback as useCallback3, useEffect as useEffect6, useState as useState4 } from "react";
|
|
1595
1900
|
var KEY = "es-theme";
|
|
1596
1901
|
function applyTheme(theme) {
|
|
1597
1902
|
if (typeof document !== "undefined") {
|
|
@@ -1612,8 +1917,8 @@ function initialTheme() {
|
|
|
1612
1917
|
return "light";
|
|
1613
1918
|
}
|
|
1614
1919
|
function useTheme() {
|
|
1615
|
-
const [theme, setTheme] =
|
|
1616
|
-
|
|
1920
|
+
const [theme, setTheme] = useState4("light");
|
|
1921
|
+
useEffect6(() => {
|
|
1617
1922
|
const t = initialTheme();
|
|
1618
1923
|
setTheme(t);
|
|
1619
1924
|
applyTheme(t);
|
|
@@ -1633,14 +1938,14 @@ function useTheme() {
|
|
|
1633
1938
|
}
|
|
1634
1939
|
|
|
1635
1940
|
// src/interactive/ReportInteractive.tsx
|
|
1636
|
-
import { jsx as
|
|
1941
|
+
import { jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1637
1942
|
function ReportInteractive(props) {
|
|
1638
1943
|
const { report, className, title, dataTheme } = props;
|
|
1639
1944
|
const result = unwrapReport(report);
|
|
1640
1945
|
if (!result.ok) {
|
|
1641
|
-
return /* @__PURE__ */
|
|
1946
|
+
return /* @__PURE__ */ jsx39(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1642
1947
|
}
|
|
1643
|
-
return /* @__PURE__ */
|
|
1948
|
+
return /* @__PURE__ */ jsx39(ReportInteractiveView, { ...props, report: result.data });
|
|
1644
1949
|
}
|
|
1645
1950
|
function ReportInteractiveView({
|
|
1646
1951
|
report,
|
|
@@ -1649,13 +1954,15 @@ function ReportInteractiveView({
|
|
|
1649
1954
|
className,
|
|
1650
1955
|
title,
|
|
1651
1956
|
dataTheme,
|
|
1652
|
-
hideHeader = false
|
|
1957
|
+
hideHeader = false,
|
|
1958
|
+
staleAfterDays = 7,
|
|
1959
|
+
scenarioHistory
|
|
1653
1960
|
}) {
|
|
1654
|
-
const [query, setQuery] =
|
|
1655
|
-
const [statusFilter, setStatusFilter] =
|
|
1656
|
-
const [activeTags, setActiveTags] =
|
|
1657
|
-
const [detail, setDetail] =
|
|
1658
|
-
const [helpOpen, setHelpOpen] =
|
|
1961
|
+
const [query, setQuery] = useState5("");
|
|
1962
|
+
const [statusFilter, setStatusFilter] = useState5("all");
|
|
1963
|
+
const [activeTags, setActiveTags] = useState5([]);
|
|
1964
|
+
const [detail, setDetail] = useState5("full");
|
|
1965
|
+
const [helpOpen, setHelpOpen] = useState5(false);
|
|
1659
1966
|
const { theme, toggle: toggleTheme } = useTheme();
|
|
1660
1967
|
const searchRef = useRef3(null);
|
|
1661
1968
|
const deferredQuery = useDeferredValue(query);
|
|
@@ -1681,7 +1988,7 @@ function ReportInteractiveView({
|
|
|
1681
1988
|
(tag) => setActiveTags((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]),
|
|
1682
1989
|
[]
|
|
1683
1990
|
);
|
|
1684
|
-
const [toast, setToast] =
|
|
1991
|
+
const [toast, setToast] = useState5(null);
|
|
1685
1992
|
const toastTimer = useRef3(null);
|
|
1686
1993
|
const showToast = useCallback4((msg) => {
|
|
1687
1994
|
setToast(msg);
|
|
@@ -1743,13 +2050,13 @@ function ReportInteractiveView({
|
|
|
1743
2050
|
const hasContent = filtered.features.length > 0;
|
|
1744
2051
|
const totalScenarios = report.summary.total;
|
|
1745
2052
|
const matchedScenarios = filtered.summary.total;
|
|
1746
|
-
return /* @__PURE__ */
|
|
2053
|
+
return /* @__PURE__ */ jsx39(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ jsx39(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ jsx39(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ jsx39(
|
|
1747
2054
|
ReportRoot,
|
|
1748
2055
|
{
|
|
1749
2056
|
report: filtered,
|
|
1750
2057
|
customRenderers,
|
|
1751
2058
|
renderers,
|
|
1752
|
-
children: /* @__PURE__ */
|
|
2059
|
+
children: /* @__PURE__ */ jsxs27(
|
|
1753
2060
|
"main",
|
|
1754
2061
|
{
|
|
1755
2062
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -1758,10 +2065,19 @@ function ReportInteractiveView({
|
|
|
1758
2065
|
"data-theme": dataTheme,
|
|
1759
2066
|
"data-detail-level": detail,
|
|
1760
2067
|
children: [
|
|
1761
|
-
/* @__PURE__ */
|
|
1762
|
-
hideHeader ? null : /* @__PURE__ */
|
|
1763
|
-
/* @__PURE__ */
|
|
1764
|
-
|
|
2068
|
+
/* @__PURE__ */ jsxs27("header", { className: "es-report-header", children: [
|
|
2069
|
+
hideHeader ? null : /* @__PURE__ */ jsx39(ReportTitleBlock, { title }),
|
|
2070
|
+
/* @__PURE__ */ jsx39(
|
|
2071
|
+
ReportFreshness,
|
|
2072
|
+
{
|
|
2073
|
+
lastRunMs: reportLastRunMs(report),
|
|
2074
|
+
ciUrl: report.ci?.url,
|
|
2075
|
+
staleAfterDays
|
|
2076
|
+
}
|
|
2077
|
+
),
|
|
2078
|
+
/* @__PURE__ */ jsx39(ReportLastRunDelta, { history: scenarioHistory, report }),
|
|
2079
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2080
|
+
/* @__PURE__ */ jsx39(
|
|
1765
2081
|
ReportSearch,
|
|
1766
2082
|
{
|
|
1767
2083
|
ref: searchRef,
|
|
@@ -1771,8 +2087,8 @@ function ReportInteractiveView({
|
|
|
1771
2087
|
totalCount: totalScenarios
|
|
1772
2088
|
}
|
|
1773
2089
|
),
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
2090
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex shrink-0 gap-1", children: [
|
|
2091
|
+
/* @__PURE__ */ jsx39(
|
|
1776
2092
|
"button",
|
|
1777
2093
|
{
|
|
1778
2094
|
type: "button",
|
|
@@ -1782,7 +2098,7 @@ function ReportInteractiveView({
|
|
|
1782
2098
|
children: "Expand all"
|
|
1783
2099
|
}
|
|
1784
2100
|
),
|
|
1785
|
-
/* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsx39(
|
|
1786
2102
|
"button",
|
|
1787
2103
|
{
|
|
1788
2104
|
type: "button",
|
|
@@ -1792,7 +2108,7 @@ function ReportInteractiveView({
|
|
|
1792
2108
|
children: "Collapse all"
|
|
1793
2109
|
}
|
|
1794
2110
|
),
|
|
1795
|
-
/* @__PURE__ */
|
|
2111
|
+
/* @__PURE__ */ jsx39(
|
|
1796
2112
|
"button",
|
|
1797
2113
|
{
|
|
1798
2114
|
type: "button",
|
|
@@ -1802,7 +2118,7 @@ function ReportInteractiveView({
|
|
|
1802
2118
|
children: detail === "full" ? "Hide docs" : "Show docs"
|
|
1803
2119
|
}
|
|
1804
2120
|
),
|
|
1805
|
-
/* @__PURE__ */
|
|
2121
|
+
/* @__PURE__ */ jsx39(
|
|
1806
2122
|
"button",
|
|
1807
2123
|
{
|
|
1808
2124
|
type: "button",
|
|
@@ -1814,7 +2130,7 @@ function ReportInteractiveView({
|
|
|
1814
2130
|
)
|
|
1815
2131
|
] })
|
|
1816
2132
|
] }),
|
|
1817
|
-
/* @__PURE__ */
|
|
2133
|
+
/* @__PURE__ */ jsx39(
|
|
1818
2134
|
ReportFilters,
|
|
1819
2135
|
{
|
|
1820
2136
|
statuses: statusOptions,
|
|
@@ -1826,12 +2142,12 @@ function ReportInteractiveView({
|
|
|
1826
2142
|
}
|
|
1827
2143
|
)
|
|
1828
2144
|
] }),
|
|
1829
|
-
/* @__PURE__ */
|
|
1830
|
-
hasContent ? /* @__PURE__ */
|
|
1831
|
-
/* @__PURE__ */
|
|
1832
|
-
/* @__PURE__ */
|
|
1833
|
-
] }) : /* @__PURE__ */
|
|
1834
|
-
/* @__PURE__ */
|
|
2145
|
+
/* @__PURE__ */ jsx39(ReportFailureBanner, { failures }),
|
|
2146
|
+
hasContent ? /* @__PURE__ */ jsxs27("div", { className: "flex gap-6", children: [
|
|
2147
|
+
/* @__PURE__ */ jsx39(ReportToc, {}),
|
|
2148
|
+
/* @__PURE__ */ jsx39("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx39(ReportFeatureList, {}) })
|
|
2149
|
+
] }) : /* @__PURE__ */ jsx39(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
|
|
2150
|
+
/* @__PURE__ */ jsx39(
|
|
1835
2151
|
"button",
|
|
1836
2152
|
{
|
|
1837
2153
|
type: "button",
|
|
@@ -1842,8 +2158,8 @@ function ReportInteractiveView({
|
|
|
1842
2158
|
children: "?"
|
|
1843
2159
|
}
|
|
1844
2160
|
),
|
|
1845
|
-
/* @__PURE__ */
|
|
1846
|
-
toast ? /* @__PURE__ */
|
|
2161
|
+
/* @__PURE__ */ jsx39(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
|
|
2162
|
+
toast ? /* @__PURE__ */ jsx39(
|
|
1847
2163
|
"div",
|
|
1848
2164
|
{
|
|
1849
2165
|
role: "status",
|
|
@@ -1856,18 +2172,18 @@ function ReportInteractiveView({
|
|
|
1856
2172
|
}
|
|
1857
2173
|
)
|
|
1858
2174
|
}
|
|
1859
|
-
) }) });
|
|
2175
|
+
) }) }) });
|
|
1860
2176
|
}
|
|
1861
2177
|
|
|
1862
2178
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
1863
2179
|
import { useMemo as useMemo4 } from "react";
|
|
1864
2180
|
|
|
1865
2181
|
// src/interactive/island-renderers.tsx
|
|
1866
|
-
import { createElement, useEffect as
|
|
2182
|
+
import { createElement, useEffect as useEffect8, useState as useState7 } from "react";
|
|
1867
2183
|
|
|
1868
2184
|
// src/components/doc/MermaidView.tsx
|
|
1869
|
-
import { useEffect as
|
|
1870
|
-
import { jsx as
|
|
2185
|
+
import { useEffect as useEffect7, useRef as useRef4, useState as useState6 } from "react";
|
|
2186
|
+
import { jsx as jsx40, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
1871
2187
|
function prefersDark(el) {
|
|
1872
2188
|
const themed = el?.closest("[data-theme]");
|
|
1873
2189
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -1876,8 +2192,8 @@ function prefersDark(el) {
|
|
|
1876
2192
|
}
|
|
1877
2193
|
function MermaidView({ entry, load }) {
|
|
1878
2194
|
const ref = useRef4(null);
|
|
1879
|
-
const [svg, setSvg] =
|
|
1880
|
-
|
|
2195
|
+
const [svg, setSvg] = useState6(null);
|
|
2196
|
+
useEffect7(() => {
|
|
1881
2197
|
let cancelled = false;
|
|
1882
2198
|
const draw = async () => {
|
|
1883
2199
|
try {
|
|
@@ -1904,9 +2220,9 @@ function MermaidView({ entry, load }) {
|
|
|
1904
2220
|
};
|
|
1905
2221
|
}, [entry.code, load]);
|
|
1906
2222
|
if (svg) {
|
|
1907
|
-
return /* @__PURE__ */
|
|
1908
|
-
entry.title ? /* @__PURE__ */
|
|
1909
|
-
/* @__PURE__ */
|
|
2223
|
+
return /* @__PURE__ */ jsxs28("figure", { ref, className: "my-3", children: [
|
|
2224
|
+
entry.title ? /* @__PURE__ */ jsx40("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
2225
|
+
/* @__PURE__ */ jsx40(
|
|
1910
2226
|
"div",
|
|
1911
2227
|
{
|
|
1912
2228
|
role: "img",
|
|
@@ -1917,11 +2233,11 @@ function MermaidView({ entry, load }) {
|
|
|
1917
2233
|
)
|
|
1918
2234
|
] });
|
|
1919
2235
|
}
|
|
1920
|
-
return /* @__PURE__ */
|
|
2236
|
+
return /* @__PURE__ */ jsx40(MermaidSource, { entry, ref });
|
|
1921
2237
|
}
|
|
1922
2238
|
|
|
1923
2239
|
// src/interactive/island-renderers.tsx
|
|
1924
|
-
import { jsx as
|
|
2240
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
1925
2241
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
1926
2242
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
1927
2243
|
function cachedCdnModule(url) {
|
|
@@ -1938,8 +2254,8 @@ function cachedCdnModule(url) {
|
|
|
1938
2254
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
1939
2255
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
1940
2256
|
function CdnHighlightedCode({ entry }) {
|
|
1941
|
-
const [html, setHtml] =
|
|
1942
|
-
|
|
2257
|
+
const [html, setHtml] = useState7(null);
|
|
2258
|
+
useEffect8(() => {
|
|
1943
2259
|
let cancelled = false;
|
|
1944
2260
|
void (async () => {
|
|
1945
2261
|
try {
|
|
@@ -1955,13 +2271,13 @@ function CdnHighlightedCode({ entry }) {
|
|
|
1955
2271
|
cancelled = true;
|
|
1956
2272
|
};
|
|
1957
2273
|
}, [entry.content, entry.lang]);
|
|
1958
|
-
return /* @__PURE__ */
|
|
2274
|
+
return /* @__PURE__ */ jsx41(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ jsx41(
|
|
1959
2275
|
"code",
|
|
1960
2276
|
{
|
|
1961
2277
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
1962
2278
|
dangerouslySetInnerHTML: { __html: html }
|
|
1963
2279
|
}
|
|
1964
|
-
) : /* @__PURE__ */
|
|
2280
|
+
) : /* @__PURE__ */ jsx41("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
1965
2281
|
}
|
|
1966
2282
|
function buildIslandRenderers(opts) {
|
|
1967
2283
|
const renderers = {};
|
|
@@ -1975,7 +2291,7 @@ function buildIslandRenderers(opts) {
|
|
|
1975
2291
|
}
|
|
1976
2292
|
|
|
1977
2293
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
1978
|
-
import { jsx as
|
|
2294
|
+
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
1979
2295
|
function ReportInteractiveIsland({
|
|
1980
2296
|
report,
|
|
1981
2297
|
title,
|
|
@@ -1988,7 +2304,7 @@ function ReportInteractiveIsland({
|
|
|
1988
2304
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
1989
2305
|
[syntaxHighlighting, mermaid]
|
|
1990
2306
|
);
|
|
1991
|
-
return /* @__PURE__ */
|
|
2307
|
+
return /* @__PURE__ */ jsx42(
|
|
1992
2308
|
ReportInteractive,
|
|
1993
2309
|
{
|
|
1994
2310
|
report,
|
|
@@ -2001,6 +2317,7 @@ function ReportInteractiveIsland({
|
|
|
2001
2317
|
}
|
|
2002
2318
|
export {
|
|
2003
2319
|
ReportFailureBanner,
|
|
2320
|
+
ReportFreshness,
|
|
2004
2321
|
ReportInteractive,
|
|
2005
2322
|
ReportInteractiveIsland,
|
|
2006
2323
|
ReportSearch,
|