executable-stories-react 0.2.1 → 0.4.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 +345 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +295 -9
- package/dist/index.d.ts +295 -9
- package/dist/index.js +329 -145
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +533 -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 +494 -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 +275 -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 +272 -115
- package/dist/ssr.js.map +1 -1
- package/dist/styles.css +26 -0
- package/dist/tailwind.css +1 -1
- package/package.json +7 -6
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";
|
|
@@ -246,7 +258,7 @@ function DocSection({ entry }) {
|
|
|
246
258
|
/* @__PURE__ */ jsx8(
|
|
247
259
|
"div",
|
|
248
260
|
{
|
|
249
|
-
className: "es-doc-prose
|
|
261
|
+
className: "es-doc-prose prose prose-sm max-w-none",
|
|
250
262
|
dangerouslySetInnerHTML: { __html: html }
|
|
251
263
|
}
|
|
252
264
|
)
|
|
@@ -545,9 +557,6 @@ function ReportAttachments({ attachments }) {
|
|
|
545
557
|
);
|
|
546
558
|
}
|
|
547
559
|
|
|
548
|
-
// src/components/ReportTrace.tsx
|
|
549
|
-
import { formatDuration as formatDuration2 } from "executable-stories-core/utils/duration";
|
|
550
|
-
|
|
551
560
|
// src/lib/trace.ts
|
|
552
561
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
553
562
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -642,7 +651,7 @@ function ReportTrace({ spans }) {
|
|
|
642
651
|
" span",
|
|
643
652
|
rows.length === 1 ? "" : "s",
|
|
644
653
|
", ",
|
|
645
|
-
|
|
654
|
+
formatDuration(totalMs),
|
|
646
655
|
")"
|
|
647
656
|
] }),
|
|
648
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) => {
|
|
@@ -665,18 +674,131 @@ function ReportTrace({ spans }) {
|
|
|
665
674
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
666
675
|
}
|
|
667
676
|
) }),
|
|
668
|
-
/* @__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) })
|
|
669
678
|
] }, `${row.spanId}-${i}`);
|
|
670
679
|
}) })
|
|
671
680
|
] });
|
|
672
681
|
}
|
|
673
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
|
+
|
|
674
796
|
// src/interactive/collapse-context.ts
|
|
675
|
-
import { createContext as
|
|
676
|
-
var CollapseContext =
|
|
797
|
+
import { createContext as createContext3, useCallback, useContext as useContext4, useState } from "react";
|
|
798
|
+
var CollapseContext = createContext3(null);
|
|
677
799
|
var CollapseProvider = CollapseContext.Provider;
|
|
678
800
|
function useCollapse() {
|
|
679
|
-
return
|
|
801
|
+
return useContext4(CollapseContext);
|
|
680
802
|
}
|
|
681
803
|
var STORAGE_KEY = "es-collapsed-ids";
|
|
682
804
|
function readPersisted() {
|
|
@@ -725,11 +847,11 @@ function useCollapseState() {
|
|
|
725
847
|
}
|
|
726
848
|
|
|
727
849
|
// src/interactive/scenario-actions.tsx
|
|
728
|
-
import { createContext as
|
|
729
|
-
var ScenarioActionsContext =
|
|
850
|
+
import { createContext as createContext4, useContext as useContext5 } from "react";
|
|
851
|
+
var ScenarioActionsContext = createContext4(null);
|
|
730
852
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
731
853
|
function useScenarioActions() {
|
|
732
|
-
return
|
|
854
|
+
return useContext5(ScenarioActionsContext);
|
|
733
855
|
}
|
|
734
856
|
function scenarioToMarkdown(scenario) {
|
|
735
857
|
const lines = [`## ${scenario.title} _(${scenario.status})_`, ""];
|
|
@@ -767,9 +889,9 @@ function scenarioPermalink(scenario) {
|
|
|
767
889
|
|
|
768
890
|
// src/components/ui/card.tsx
|
|
769
891
|
import "react";
|
|
770
|
-
import { jsx as
|
|
892
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
771
893
|
function Card({ className, ...props }) {
|
|
772
|
-
return /* @__PURE__ */
|
|
894
|
+
return /* @__PURE__ */ jsx21(
|
|
773
895
|
"div",
|
|
774
896
|
{
|
|
775
897
|
"data-slot": "card",
|
|
@@ -782,7 +904,7 @@ function Card({ className, ...props }) {
|
|
|
782
904
|
);
|
|
783
905
|
}
|
|
784
906
|
function CardHeader({ className, ...props }) {
|
|
785
|
-
return /* @__PURE__ */
|
|
907
|
+
return /* @__PURE__ */ jsx21(
|
|
786
908
|
"div",
|
|
787
909
|
{
|
|
788
910
|
"data-slot": "card-header",
|
|
@@ -795,7 +917,7 @@ function CardHeader({ className, ...props }) {
|
|
|
795
917
|
);
|
|
796
918
|
}
|
|
797
919
|
function CardContent({ className, ...props }) {
|
|
798
|
-
return /* @__PURE__ */
|
|
920
|
+
return /* @__PURE__ */ jsx21(
|
|
799
921
|
"div",
|
|
800
922
|
{
|
|
801
923
|
"data-slot": "card-content",
|
|
@@ -806,7 +928,7 @@ function CardContent({ className, ...props }) {
|
|
|
806
928
|
}
|
|
807
929
|
|
|
808
930
|
// src/components/ReportScenario.tsx
|
|
809
|
-
import { jsx as
|
|
931
|
+
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
810
932
|
var STATUS_LABEL = {
|
|
811
933
|
passed: "Passed",
|
|
812
934
|
failed: "Failed",
|
|
@@ -834,12 +956,12 @@ var STATUS_BADGE = {
|
|
|
834
956
|
function ReportScenario({ scenario, hideTitle = false }) {
|
|
835
957
|
const titleId = `${scenario.id}-title`;
|
|
836
958
|
const bodyId = `${scenario.id}-body`;
|
|
837
|
-
const label = STATUS_LABEL[scenario.status];
|
|
959
|
+
const label = scenario.planned ? "Planned" : STATUS_LABEL[scenario.status];
|
|
838
960
|
const collapse = useCollapse();
|
|
839
961
|
const collapsible = collapse !== null && !hideTitle;
|
|
840
962
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
841
963
|
const actions = useScenarioActions();
|
|
842
|
-
return /* @__PURE__ */
|
|
964
|
+
return /* @__PURE__ */ jsxs14(
|
|
843
965
|
Card,
|
|
844
966
|
{
|
|
845
967
|
id: scenario.id,
|
|
@@ -847,10 +969,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
847
969
|
...hideTitle ? { "aria-label": `${scenario.title} \u2014 ${label}` } : { "aria-labelledby": titleId },
|
|
848
970
|
className: "gap-0 py-0",
|
|
849
971
|
children: [
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
852
|
-
hideTitle ? null : /* @__PURE__ */
|
|
853
|
-
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(
|
|
854
976
|
"button",
|
|
855
977
|
{
|
|
856
978
|
type: "button",
|
|
@@ -859,17 +981,18 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
859
981
|
"aria-controls": bodyId,
|
|
860
982
|
"aria-label": `Toggle ${scenario.title}`,
|
|
861
983
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
862
|
-
children: /* @__PURE__ */
|
|
984
|
+
children: /* @__PURE__ */ jsx22("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
863
985
|
}
|
|
864
986
|
) : null,
|
|
865
|
-
/* @__PURE__ */
|
|
866
|
-
/* @__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 })
|
|
867
989
|
] }),
|
|
868
|
-
/* @__PURE__ */
|
|
869
|
-
|
|
870
|
-
/* @__PURE__ */
|
|
871
|
-
|
|
872
|
-
|
|
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(
|
|
873
996
|
"button",
|
|
874
997
|
{
|
|
875
998
|
type: "button",
|
|
@@ -880,7 +1003,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
880
1003
|
children: "link"
|
|
881
1004
|
}
|
|
882
1005
|
),
|
|
883
|
-
/* @__PURE__ */
|
|
1006
|
+
/* @__PURE__ */ jsx22(
|
|
884
1007
|
"button",
|
|
885
1008
|
{
|
|
886
1009
|
type: "button",
|
|
@@ -891,7 +1014,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
891
1014
|
children: "md"
|
|
892
1015
|
}
|
|
893
1016
|
),
|
|
894
|
-
/* @__PURE__ */
|
|
1017
|
+
/* @__PURE__ */ jsx22(
|
|
895
1018
|
"button",
|
|
896
1019
|
{
|
|
897
1020
|
type: "button",
|
|
@@ -905,13 +1028,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
905
1028
|
] }) : null
|
|
906
1029
|
] })
|
|
907
1030
|
] }),
|
|
908
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */
|
|
909
|
-
scenario.tags.map((t) => /* @__PURE__ */
|
|
910
|
-
(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}`))
|
|
911
1034
|
] }) : null
|
|
912
1035
|
] }),
|
|
913
|
-
/* @__PURE__ */
|
|
914
|
-
scenario.errorMessage ? /* @__PURE__ */
|
|
1036
|
+
/* @__PURE__ */ jsxs14(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1037
|
+
scenario.errorMessage ? /* @__PURE__ */ jsx22(
|
|
915
1038
|
"pre",
|
|
916
1039
|
{
|
|
917
1040
|
role: "alert",
|
|
@@ -919,10 +1042,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
919
1042
|
children: scenario.errorMessage
|
|
920
1043
|
}
|
|
921
1044
|
) : null,
|
|
922
|
-
/* @__PURE__ */
|
|
923
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */
|
|
924
|
-
/* @__PURE__ */
|
|
925
|
-
/* @__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 })
|
|
926
1049
|
] })
|
|
927
1050
|
]
|
|
928
1051
|
}
|
|
@@ -930,13 +1053,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
930
1053
|
}
|
|
931
1054
|
|
|
932
1055
|
// src/components/ReportScenarioList.tsx
|
|
933
|
-
import { Fragment as
|
|
1056
|
+
import { Fragment as Fragment8, jsx as jsx23 } from "react/jsx-runtime";
|
|
934
1057
|
function ReportScenarioList({ feature }) {
|
|
935
|
-
return /* @__PURE__ */
|
|
1058
|
+
return /* @__PURE__ */ jsx23(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx23(ReportScenario, { scenario }, scenario.id)) });
|
|
936
1059
|
}
|
|
937
1060
|
|
|
938
1061
|
// src/components/ReportFeature.tsx
|
|
939
|
-
import { jsx as
|
|
1062
|
+
import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
940
1063
|
function ReportFeature({ feature }) {
|
|
941
1064
|
const titleId = `${feature.id}-title`;
|
|
942
1065
|
const bodyId = `${feature.id}-body`;
|
|
@@ -944,7 +1067,7 @@ function ReportFeature({ feature }) {
|
|
|
944
1067
|
const skipped = s.skipped + s.pending;
|
|
945
1068
|
const collapse = useCollapse();
|
|
946
1069
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
947
|
-
return /* @__PURE__ */
|
|
1070
|
+
return /* @__PURE__ */ jsxs15(
|
|
948
1071
|
"section",
|
|
949
1072
|
{
|
|
950
1073
|
id: feature.id,
|
|
@@ -952,9 +1075,9 @@ function ReportFeature({ feature }) {
|
|
|
952
1075
|
"aria-labelledby": titleId,
|
|
953
1076
|
className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm",
|
|
954
1077
|
children: [
|
|
955
|
-
/* @__PURE__ */
|
|
956
|
-
/* @__PURE__ */
|
|
957
|
-
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(
|
|
958
1081
|
"button",
|
|
959
1082
|
{
|
|
960
1083
|
type: "button",
|
|
@@ -963,30 +1086,30 @@ function ReportFeature({ feature }) {
|
|
|
963
1086
|
"aria-controls": bodyId,
|
|
964
1087
|
"aria-label": `Toggle ${feature.title}`,
|
|
965
1088
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
966
|
-
children: /* @__PURE__ */
|
|
1089
|
+
children: /* @__PURE__ */ jsx24("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
967
1090
|
}
|
|
968
1091
|
) : null,
|
|
969
|
-
/* @__PURE__ */
|
|
970
|
-
/* @__PURE__ */
|
|
971
|
-
/* @__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 })
|
|
972
1095
|
] })
|
|
973
1096
|
] }),
|
|
974
|
-
/* @__PURE__ */
|
|
1097
|
+
/* @__PURE__ */ jsxs15(
|
|
975
1098
|
"div",
|
|
976
1099
|
{
|
|
977
1100
|
role: "img",
|
|
978
1101
|
className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
|
|
979
1102
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
980
1103
|
children: [
|
|
981
|
-
/* @__PURE__ */
|
|
1104
|
+
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-pass", children: [
|
|
982
1105
|
"\u2713",
|
|
983
1106
|
s.passed
|
|
984
1107
|
] }),
|
|
985
|
-
/* @__PURE__ */
|
|
1108
|
+
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-fail", children: [
|
|
986
1109
|
"\u2717",
|
|
987
1110
|
s.failed
|
|
988
1111
|
] }),
|
|
989
|
-
/* @__PURE__ */
|
|
1112
|
+
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-skip", children: [
|
|
990
1113
|
"\u25CB",
|
|
991
1114
|
skipped
|
|
992
1115
|
] })
|
|
@@ -994,24 +1117,24 @@ function ReportFeature({ feature }) {
|
|
|
994
1117
|
}
|
|
995
1118
|
)
|
|
996
1119
|
] }),
|
|
997
|
-
/* @__PURE__ */
|
|
1120
|
+
/* @__PURE__ */ jsx24("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-2 p-3", children: /* @__PURE__ */ jsx24(ReportScenarioList, { feature }) })
|
|
998
1121
|
]
|
|
999
1122
|
}
|
|
1000
1123
|
);
|
|
1001
1124
|
}
|
|
1002
1125
|
|
|
1003
1126
|
// src/components/ReportFeatureList.tsx
|
|
1004
|
-
import { Fragment as
|
|
1127
|
+
import { Fragment as Fragment9, jsx as jsx25 } from "react/jsx-runtime";
|
|
1005
1128
|
function ReportFeatureList() {
|
|
1006
1129
|
const report = useReport();
|
|
1007
|
-
return /* @__PURE__ */
|
|
1130
|
+
return /* @__PURE__ */ jsx25(Fragment9, { children: report.features.map((feature) => /* @__PURE__ */ jsx25(ReportFeature, { feature }, feature.id)) });
|
|
1008
1131
|
}
|
|
1009
1132
|
|
|
1010
1133
|
// src/components/ui/empty.tsx
|
|
1011
1134
|
import { cva as cva2 } from "class-variance-authority";
|
|
1012
|
-
import { jsx as
|
|
1135
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
1013
1136
|
function Empty({ className, ...props }) {
|
|
1014
|
-
return /* @__PURE__ */
|
|
1137
|
+
return /* @__PURE__ */ jsx26(
|
|
1015
1138
|
"div",
|
|
1016
1139
|
{
|
|
1017
1140
|
"data-slot": "empty",
|
|
@@ -1024,7 +1147,7 @@ function Empty({ className, ...props }) {
|
|
|
1024
1147
|
);
|
|
1025
1148
|
}
|
|
1026
1149
|
function EmptyHeader({ className, ...props }) {
|
|
1027
|
-
return /* @__PURE__ */
|
|
1150
|
+
return /* @__PURE__ */ jsx26(
|
|
1028
1151
|
"div",
|
|
1029
1152
|
{
|
|
1030
1153
|
"data-slot": "empty-header",
|
|
@@ -1051,7 +1174,7 @@ var emptyMediaVariants = cva2(
|
|
|
1051
1174
|
}
|
|
1052
1175
|
);
|
|
1053
1176
|
function EmptyTitle({ className, ...props }) {
|
|
1054
|
-
return /* @__PURE__ */
|
|
1177
|
+
return /* @__PURE__ */ jsx26(
|
|
1055
1178
|
"div",
|
|
1056
1179
|
{
|
|
1057
1180
|
"data-slot": "empty-title",
|
|
@@ -1062,16 +1185,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1062
1185
|
}
|
|
1063
1186
|
|
|
1064
1187
|
// src/components/ReportEmpty.tsx
|
|
1065
|
-
import { jsx as
|
|
1188
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
1066
1189
|
function ReportEmpty({ message }) {
|
|
1067
|
-
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." }) }) });
|
|
1068
1191
|
}
|
|
1069
1192
|
|
|
1070
1193
|
// src/components/ReportSummary.tsx
|
|
1071
|
-
import { Fragment as
|
|
1194
|
+
import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1072
1195
|
function ReportSummary({ className }) {
|
|
1073
1196
|
const report = useReport();
|
|
1074
|
-
return /* @__PURE__ */
|
|
1197
|
+
return /* @__PURE__ */ jsx28(
|
|
1075
1198
|
ReportSummaryView,
|
|
1076
1199
|
{
|
|
1077
1200
|
summary: report.summary,
|
|
@@ -1081,32 +1204,32 @@ function ReportSummary({ className }) {
|
|
|
1081
1204
|
);
|
|
1082
1205
|
}
|
|
1083
1206
|
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
1084
|
-
return /* @__PURE__ */
|
|
1085
|
-
/* @__PURE__ */
|
|
1086
|
-
/* @__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 }),
|
|
1087
1210
|
" scenario",
|
|
1088
1211
|
summary.total === 1 ? "" : "s"
|
|
1089
1212
|
] }),
|
|
1090
1213
|
" \xB7 ",
|
|
1091
|
-
/* @__PURE__ */
|
|
1214
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "passed", className: "text-pass", children: [
|
|
1092
1215
|
summary.passed,
|
|
1093
1216
|
" passed"
|
|
1094
1217
|
] }),
|
|
1095
1218
|
" \xB7 ",
|
|
1096
|
-
/* @__PURE__ */
|
|
1219
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "failed", className: "text-fail", children: [
|
|
1097
1220
|
summary.failed,
|
|
1098
1221
|
" failed"
|
|
1099
1222
|
] }),
|
|
1100
|
-
summary.skipped > 0 ? /* @__PURE__ */
|
|
1223
|
+
summary.skipped > 0 ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
1101
1224
|
" \xB7 ",
|
|
1102
|
-
/* @__PURE__ */
|
|
1225
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "skipped", className: "text-skip", children: [
|
|
1103
1226
|
summary.skipped,
|
|
1104
1227
|
" skipped"
|
|
1105
1228
|
] })
|
|
1106
1229
|
] }) : null,
|
|
1107
|
-
summary.pending > 0 ? /* @__PURE__ */
|
|
1230
|
+
summary.pending > 0 ? /* @__PURE__ */ jsxs16(Fragment10, { children: [
|
|
1108
1231
|
" \xB7 ",
|
|
1109
|
-
/* @__PURE__ */
|
|
1232
|
+
/* @__PURE__ */ jsxs16("span", { "data-status": "pending", className: "text-pend", children: [
|
|
1110
1233
|
summary.pending,
|
|
1111
1234
|
" pending"
|
|
1112
1235
|
] })
|
|
@@ -1114,69 +1237,135 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1114
1237
|
] });
|
|
1115
1238
|
}
|
|
1116
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
|
+
|
|
1117
1289
|
// src/components/ReportMeta.tsx
|
|
1118
|
-
import {
|
|
1119
|
-
import { jsx as jsx28, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1290
|
+
import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1120
1291
|
function ReportMeta() {
|
|
1121
1292
|
const report = useReport();
|
|
1122
1293
|
const items = [];
|
|
1294
|
+
const linkClass = "text-primary underline underline-offset-2";
|
|
1123
1295
|
if (report.startedAtMs) {
|
|
1124
1296
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1125
1297
|
}
|
|
1126
1298
|
if (report.durationMs) {
|
|
1127
|
-
items.push({ label: "Duration", value:
|
|
1299
|
+
items.push({ label: "Duration", value: formatDuration(report.durationMs) });
|
|
1128
1300
|
}
|
|
1129
1301
|
if (report.packageVersion) {
|
|
1130
1302
|
items.push({ label: "Version", value: report.packageVersion });
|
|
1131
1303
|
}
|
|
1132
|
-
if (report.
|
|
1133
|
-
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
|
+
});
|
|
1134
1315
|
}
|
|
1135
1316
|
if (report.ci) {
|
|
1136
1317
|
items.push({
|
|
1137
1318
|
label: "CI",
|
|
1138
|
-
value: report.ci.url ? /* @__PURE__ */
|
|
1139
|
-
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),
|
|
1140
1321
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1141
|
-
] }) : `${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
|
|
1142
1331
|
});
|
|
1143
1332
|
}
|
|
1144
1333
|
if (items.length === 0) return null;
|
|
1145
|
-
return /* @__PURE__ */
|
|
1146
|
-
/* @__PURE__ */
|
|
1147
|
-
/* @__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 })
|
|
1148
1337
|
] }, item.label)) });
|
|
1149
1338
|
}
|
|
1150
1339
|
|
|
1151
1340
|
// src/components/ReportSchemaError.tsx
|
|
1152
|
-
import { jsx as
|
|
1341
|
+
import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1153
1342
|
function ReportSchemaError({ error }) {
|
|
1154
|
-
return /* @__PURE__ */
|
|
1155
|
-
/* @__PURE__ */
|
|
1156
|
-
/* @__PURE__ */
|
|
1157
|
-
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: [
|
|
1158
1347
|
"The report bundle is newer than this version of ",
|
|
1159
|
-
/* @__PURE__ */
|
|
1348
|
+
/* @__PURE__ */ jsx30("code", { children: "executable-stories-react" }),
|
|
1160
1349
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1161
1350
|
] }) : null,
|
|
1162
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */
|
|
1163
|
-
/* @__PURE__ */
|
|
1351
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs18("details", { children: [
|
|
1352
|
+
/* @__PURE__ */ jsxs18("summary", { children: [
|
|
1164
1353
|
error.issues.length,
|
|
1165
1354
|
" validation issue",
|
|
1166
1355
|
error.issues.length === 1 ? "" : "s"
|
|
1167
1356
|
] }),
|
|
1168
|
-
/* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsx30("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1169
1358
|
] }) : null
|
|
1170
1359
|
] });
|
|
1171
1360
|
}
|
|
1172
1361
|
|
|
1173
1362
|
// src/components/ReportShell.tsx
|
|
1174
|
-
import { Fragment as
|
|
1363
|
+
import { Fragment as Fragment11, jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1175
1364
|
function ReportTitleBlock({ title }) {
|
|
1176
|
-
return /* @__PURE__ */
|
|
1177
|
-
/* @__PURE__ */
|
|
1178
|
-
/* @__PURE__ */
|
|
1179
|
-
/* @__PURE__ */
|
|
1365
|
+
return /* @__PURE__ */ jsxs19(Fragment11, { children: [
|
|
1366
|
+
/* @__PURE__ */ jsx31("h1", { children: title ?? "Story Report" }),
|
|
1367
|
+
/* @__PURE__ */ jsx31(ReportSummary, {}),
|
|
1368
|
+
/* @__PURE__ */ jsx31(ReportMeta, {})
|
|
1180
1369
|
] });
|
|
1181
1370
|
}
|
|
1182
1371
|
function ReportErrorShell({
|
|
@@ -1185,7 +1374,7 @@ function ReportErrorShell({
|
|
|
1185
1374
|
title,
|
|
1186
1375
|
dataTheme
|
|
1187
1376
|
}) {
|
|
1188
|
-
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 }) });
|
|
1189
1378
|
}
|
|
1190
1379
|
|
|
1191
1380
|
// src/interactive/ReportSearch.tsx
|
|
@@ -1193,7 +1382,7 @@ import {
|
|
|
1193
1382
|
forwardRef,
|
|
1194
1383
|
useId
|
|
1195
1384
|
} from "react";
|
|
1196
|
-
import { jsx as
|
|
1385
|
+
import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1197
1386
|
var ReportSearch = forwardRef(
|
|
1198
1387
|
function ReportSearch2(props, ref) {
|
|
1199
1388
|
const {
|
|
@@ -1215,9 +1404,9 @@ var ReportSearch = forwardRef(
|
|
|
1215
1404
|
e.preventDefault();
|
|
1216
1405
|
}
|
|
1217
1406
|
}
|
|
1218
|
-
return /* @__PURE__ */
|
|
1219
|
-
/* @__PURE__ */
|
|
1220
|
-
/* @__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(
|
|
1221
1410
|
"input",
|
|
1222
1411
|
{
|
|
1223
1412
|
ref,
|
|
@@ -1232,7 +1421,7 @@ var ReportSearch = forwardRef(
|
|
|
1232
1421
|
"aria-keyshortcuts": "/"
|
|
1233
1422
|
}
|
|
1234
1423
|
),
|
|
1235
|
-
showCounts ? /* @__PURE__ */
|
|
1424
|
+
showCounts ? /* @__PURE__ */ jsx32("span", { className: "es-search-counts", "aria-live": "polite", children: value ? `${matchedCount} of ${totalCount}` : `${totalCount} total` }) : null
|
|
1236
1425
|
] });
|
|
1237
1426
|
}
|
|
1238
1427
|
);
|
|
@@ -1261,7 +1450,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
1261
1450
|
}
|
|
1262
1451
|
|
|
1263
1452
|
// src/interactive/ReportFailureBanner.tsx
|
|
1264
|
-
import { jsx as
|
|
1453
|
+
import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1265
1454
|
function ReportFailureBanner({ failures }) {
|
|
1266
1455
|
const first = failures[0];
|
|
1267
1456
|
const jumpToFirst = useCallback2(() => {
|
|
@@ -1269,7 +1458,7 @@ function ReportFailureBanner({ failures }) {
|
|
|
1269
1458
|
scrollToScenarioId(first.scenarioId);
|
|
1270
1459
|
}, [first]);
|
|
1271
1460
|
if (failures.length === 0) return null;
|
|
1272
|
-
return /* @__PURE__ */
|
|
1461
|
+
return /* @__PURE__ */ jsxs21(
|
|
1273
1462
|
"div",
|
|
1274
1463
|
{
|
|
1275
1464
|
className: "es-failure-banner",
|
|
@@ -1277,13 +1466,13 @@ function ReportFailureBanner({ failures }) {
|
|
|
1277
1466
|
"aria-live": "polite",
|
|
1278
1467
|
"aria-label": "Failure summary",
|
|
1279
1468
|
children: [
|
|
1280
|
-
/* @__PURE__ */
|
|
1281
|
-
/* @__PURE__ */
|
|
1469
|
+
/* @__PURE__ */ jsxs21("span", { className: "es-failure-banner-text", children: [
|
|
1470
|
+
/* @__PURE__ */ jsx33("strong", { children: failures.length }),
|
|
1282
1471
|
" ",
|
|
1283
1472
|
"failure",
|
|
1284
1473
|
failures.length === 1 ? "" : "s"
|
|
1285
1474
|
] }),
|
|
1286
|
-
/* @__PURE__ */
|
|
1475
|
+
/* @__PURE__ */ jsx33(
|
|
1287
1476
|
"button",
|
|
1288
1477
|
{
|
|
1289
1478
|
type: "button",
|
|
@@ -1298,9 +1487,101 @@ function ReportFailureBanner({ failures }) {
|
|
|
1298
1487
|
);
|
|
1299
1488
|
}
|
|
1300
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
|
+
|
|
1301
1582
|
// src/interactive/ReportShortcutsHelp.tsx
|
|
1302
|
-
import { useEffect, useRef } from "react";
|
|
1303
|
-
import { jsx as
|
|
1583
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
1584
|
+
import { jsx as jsx36, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1304
1585
|
var SHORTCUTS = [
|
|
1305
1586
|
{ keys: "/", description: "Focus search" },
|
|
1306
1587
|
{ keys: "f", description: "Jump to next failure" },
|
|
@@ -1310,13 +1591,13 @@ var SHORTCUTS = [
|
|
|
1310
1591
|
];
|
|
1311
1592
|
function ReportShortcutsHelp({ open, onClose }) {
|
|
1312
1593
|
const dialogRef = useRef(null);
|
|
1313
|
-
|
|
1594
|
+
useEffect2(() => {
|
|
1314
1595
|
const dialog = dialogRef.current;
|
|
1315
1596
|
if (!dialog) return;
|
|
1316
1597
|
if (open && !dialog.open) dialog.showModal();
|
|
1317
1598
|
else if (!open && dialog.open) dialog.close();
|
|
1318
1599
|
}, [open]);
|
|
1319
|
-
return /* @__PURE__ */
|
|
1600
|
+
return /* @__PURE__ */ jsxs24(
|
|
1320
1601
|
"dialog",
|
|
1321
1602
|
{
|
|
1322
1603
|
ref: dialogRef,
|
|
@@ -1324,19 +1605,19 @@ function ReportShortcutsHelp({ open, onClose }) {
|
|
|
1324
1605
|
"aria-label": "Keyboard shortcuts",
|
|
1325
1606
|
onClose,
|
|
1326
1607
|
children: [
|
|
1327
|
-
/* @__PURE__ */
|
|
1328
|
-
/* @__PURE__ */
|
|
1329
|
-
/* @__PURE__ */
|
|
1330
|
-
/* @__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 })
|
|
1331
1612
|
] }, s.keys)) }),
|
|
1332
|
-
/* @__PURE__ */
|
|
1613
|
+
/* @__PURE__ */ jsx36("form", { method: "dialog", children: /* @__PURE__ */ jsx36("button", { type: "submit", className: "es-shortcuts-close", children: "Close" }) })
|
|
1333
1614
|
]
|
|
1334
1615
|
}
|
|
1335
1616
|
);
|
|
1336
1617
|
}
|
|
1337
1618
|
|
|
1338
1619
|
// src/interactive/use-keyboard-shortcuts.ts
|
|
1339
|
-
import { useEffect as
|
|
1620
|
+
import { useEffect as useEffect3, useRef as useRef2 } from "react";
|
|
1340
1621
|
function isEditableTarget(target) {
|
|
1341
1622
|
if (!(target instanceof HTMLElement)) return false;
|
|
1342
1623
|
const tag = target.tagName;
|
|
@@ -1347,7 +1628,7 @@ function isEditableTarget(target) {
|
|
|
1347
1628
|
function useKeyboardShortcuts(handlers) {
|
|
1348
1629
|
const ref = useRef2(handlers);
|
|
1349
1630
|
ref.current = handlers;
|
|
1350
|
-
|
|
1631
|
+
useEffect3(() => {
|
|
1351
1632
|
function onKeyDown(e) {
|
|
1352
1633
|
const h = ref.current;
|
|
1353
1634
|
if (e.key === "Escape") {
|
|
@@ -1391,9 +1672,9 @@ function useKeyboardShortcuts(handlers) {
|
|
|
1391
1672
|
}
|
|
1392
1673
|
|
|
1393
1674
|
// src/interactive/use-deep-link-scroll.ts
|
|
1394
|
-
import { useEffect as
|
|
1675
|
+
import { useEffect as useEffect4 } from "react";
|
|
1395
1676
|
function useDeepLinkScroll() {
|
|
1396
|
-
|
|
1677
|
+
useEffect4(() => {
|
|
1397
1678
|
function scrollToHash() {
|
|
1398
1679
|
const hash = window.location.hash.replace(/^#/, "");
|
|
1399
1680
|
if (!hash) return;
|
|
@@ -1502,7 +1783,7 @@ function listFailures(report) {
|
|
|
1502
1783
|
}
|
|
1503
1784
|
|
|
1504
1785
|
// src/interactive/ReportFilters.tsx
|
|
1505
|
-
import { jsx as
|
|
1786
|
+
import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1506
1787
|
function ReportFilters({
|
|
1507
1788
|
statuses,
|
|
1508
1789
|
status,
|
|
@@ -1512,10 +1793,10 @@ function ReportFilters({
|
|
|
1512
1793
|
onToggleTag
|
|
1513
1794
|
}) {
|
|
1514
1795
|
if (statuses.length === 0 && tags.length === 0) return null;
|
|
1515
|
-
return /* @__PURE__ */
|
|
1516
|
-
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) => {
|
|
1517
1798
|
const active = status === opt.key;
|
|
1518
|
-
return /* @__PURE__ */
|
|
1799
|
+
return /* @__PURE__ */ jsxs25(
|
|
1519
1800
|
"button",
|
|
1520
1801
|
{
|
|
1521
1802
|
type: "button",
|
|
@@ -1527,22 +1808,22 @@ function ReportFilters({
|
|
|
1527
1808
|
),
|
|
1528
1809
|
children: [
|
|
1529
1810
|
opt.label,
|
|
1530
|
-
/* @__PURE__ */
|
|
1811
|
+
/* @__PURE__ */ jsx37("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
|
|
1531
1812
|
]
|
|
1532
1813
|
},
|
|
1533
1814
|
opt.key
|
|
1534
1815
|
);
|
|
1535
1816
|
}) }) : null,
|
|
1536
|
-
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) => {
|
|
1537
1818
|
const active = activeTags.includes(tag);
|
|
1538
|
-
return /* @__PURE__ */
|
|
1819
|
+
return /* @__PURE__ */ jsx37("li", { children: /* @__PURE__ */ jsx37(
|
|
1539
1820
|
"button",
|
|
1540
1821
|
{
|
|
1541
1822
|
type: "button",
|
|
1542
1823
|
"aria-pressed": active,
|
|
1543
1824
|
onClick: () => onToggleTag(tag),
|
|
1544
1825
|
className: "cursor-pointer",
|
|
1545
|
-
children: /* @__PURE__ */
|
|
1826
|
+
children: /* @__PURE__ */ jsx37(Badge, { variant: active ? "default" : "tag", children: tag })
|
|
1546
1827
|
}
|
|
1547
1828
|
) }, tag);
|
|
1548
1829
|
}) }) : null
|
|
@@ -1550,8 +1831,8 @@ function ReportFilters({
|
|
|
1550
1831
|
}
|
|
1551
1832
|
|
|
1552
1833
|
// src/interactive/ReportToc.tsx
|
|
1553
|
-
import { useEffect as
|
|
1554
|
-
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";
|
|
1555
1836
|
var DOT = {
|
|
1556
1837
|
passed: "text-pass",
|
|
1557
1838
|
failed: "text-fail",
|
|
@@ -1560,12 +1841,12 @@ var DOT = {
|
|
|
1560
1841
|
};
|
|
1561
1842
|
function ReportToc() {
|
|
1562
1843
|
const report = useReport();
|
|
1563
|
-
const [activeId, setActiveId] =
|
|
1844
|
+
const [activeId, setActiveId] = useState3(null);
|
|
1564
1845
|
const scenarioIds = useMemo2(
|
|
1565
1846
|
() => report.features.flatMap((f) => f.scenarios.map((s) => s.id)),
|
|
1566
1847
|
[report]
|
|
1567
1848
|
);
|
|
1568
|
-
|
|
1849
|
+
useEffect5(() => {
|
|
1569
1850
|
if (typeof IntersectionObserver === "undefined") return;
|
|
1570
1851
|
const els = scenarioIds.map((id) => document.getElementById(id)).filter((el) => el !== null);
|
|
1571
1852
|
if (els.length === 0) return;
|
|
@@ -1580,16 +1861,16 @@ function ReportToc() {
|
|
|
1580
1861
|
return () => observer.disconnect();
|
|
1581
1862
|
}, [scenarioIds]);
|
|
1582
1863
|
if (report.features.length === 0) return null;
|
|
1583
|
-
return /* @__PURE__ */
|
|
1864
|
+
return /* @__PURE__ */ jsx38(
|
|
1584
1865
|
"nav",
|
|
1585
1866
|
{
|
|
1586
1867
|
"aria-label": "Table of contents",
|
|
1587
1868
|
className: "sticky top-4 hidden h-fit w-56 shrink-0 self-start text-xs lg:block",
|
|
1588
|
-
children: /* @__PURE__ */
|
|
1589
|
-
/* @__PURE__ */
|
|
1590
|
-
/* @__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) => {
|
|
1591
1872
|
const active = activeId === scenario.id;
|
|
1592
|
-
return /* @__PURE__ */
|
|
1873
|
+
return /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs26(
|
|
1593
1874
|
"a",
|
|
1594
1875
|
{
|
|
1595
1876
|
href: `#${scenario.id}`,
|
|
@@ -1603,8 +1884,8 @@ function ReportToc() {
|
|
|
1603
1884
|
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
1604
1885
|
),
|
|
1605
1886
|
children: [
|
|
1606
|
-
/* @__PURE__ */
|
|
1607
|
-
/* @__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 })
|
|
1608
1889
|
]
|
|
1609
1890
|
}
|
|
1610
1891
|
) }, scenario.id);
|
|
@@ -1615,7 +1896,7 @@ function ReportToc() {
|
|
|
1615
1896
|
}
|
|
1616
1897
|
|
|
1617
1898
|
// src/interactive/use-theme.ts
|
|
1618
|
-
import { useCallback as useCallback3, useEffect as
|
|
1899
|
+
import { useCallback as useCallback3, useEffect as useEffect6, useState as useState4 } from "react";
|
|
1619
1900
|
var KEY = "es-theme";
|
|
1620
1901
|
function applyTheme(theme) {
|
|
1621
1902
|
if (typeof document !== "undefined") {
|
|
@@ -1636,8 +1917,8 @@ function initialTheme() {
|
|
|
1636
1917
|
return "light";
|
|
1637
1918
|
}
|
|
1638
1919
|
function useTheme() {
|
|
1639
|
-
const [theme, setTheme] =
|
|
1640
|
-
|
|
1920
|
+
const [theme, setTheme] = useState4("light");
|
|
1921
|
+
useEffect6(() => {
|
|
1641
1922
|
const t = initialTheme();
|
|
1642
1923
|
setTheme(t);
|
|
1643
1924
|
applyTheme(t);
|
|
@@ -1657,14 +1938,14 @@ function useTheme() {
|
|
|
1657
1938
|
}
|
|
1658
1939
|
|
|
1659
1940
|
// src/interactive/ReportInteractive.tsx
|
|
1660
|
-
import { jsx as
|
|
1941
|
+
import { jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1661
1942
|
function ReportInteractive(props) {
|
|
1662
1943
|
const { report, className, title, dataTheme } = props;
|
|
1663
1944
|
const result = unwrapReport(report);
|
|
1664
1945
|
if (!result.ok) {
|
|
1665
|
-
return /* @__PURE__ */
|
|
1946
|
+
return /* @__PURE__ */ jsx39(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1666
1947
|
}
|
|
1667
|
-
return /* @__PURE__ */
|
|
1948
|
+
return /* @__PURE__ */ jsx39(ReportInteractiveView, { ...props, report: result.data });
|
|
1668
1949
|
}
|
|
1669
1950
|
function ReportInteractiveView({
|
|
1670
1951
|
report,
|
|
@@ -1673,13 +1954,15 @@ function ReportInteractiveView({
|
|
|
1673
1954
|
className,
|
|
1674
1955
|
title,
|
|
1675
1956
|
dataTheme,
|
|
1676
|
-
hideHeader = false
|
|
1957
|
+
hideHeader = false,
|
|
1958
|
+
staleAfterDays = 7,
|
|
1959
|
+
scenarioHistory
|
|
1677
1960
|
}) {
|
|
1678
|
-
const [query, setQuery] =
|
|
1679
|
-
const [statusFilter, setStatusFilter] =
|
|
1680
|
-
const [activeTags, setActiveTags] =
|
|
1681
|
-
const [detail, setDetail] =
|
|
1682
|
-
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);
|
|
1683
1966
|
const { theme, toggle: toggleTheme } = useTheme();
|
|
1684
1967
|
const searchRef = useRef3(null);
|
|
1685
1968
|
const deferredQuery = useDeferredValue(query);
|
|
@@ -1705,7 +1988,7 @@ function ReportInteractiveView({
|
|
|
1705
1988
|
(tag) => setActiveTags((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]),
|
|
1706
1989
|
[]
|
|
1707
1990
|
);
|
|
1708
|
-
const [toast, setToast] =
|
|
1991
|
+
const [toast, setToast] = useState5(null);
|
|
1709
1992
|
const toastTimer = useRef3(null);
|
|
1710
1993
|
const showToast = useCallback4((msg) => {
|
|
1711
1994
|
setToast(msg);
|
|
@@ -1767,13 +2050,13 @@ function ReportInteractiveView({
|
|
|
1767
2050
|
const hasContent = filtered.features.length > 0;
|
|
1768
2051
|
const totalScenarios = report.summary.total;
|
|
1769
2052
|
const matchedScenarios = filtered.summary.total;
|
|
1770
|
-
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(
|
|
1771
2054
|
ReportRoot,
|
|
1772
2055
|
{
|
|
1773
2056
|
report: filtered,
|
|
1774
2057
|
customRenderers,
|
|
1775
2058
|
renderers,
|
|
1776
|
-
children: /* @__PURE__ */
|
|
2059
|
+
children: /* @__PURE__ */ jsxs27(
|
|
1777
2060
|
"main",
|
|
1778
2061
|
{
|
|
1779
2062
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -1782,10 +2065,19 @@ function ReportInteractiveView({
|
|
|
1782
2065
|
"data-theme": dataTheme,
|
|
1783
2066
|
"data-detail-level": detail,
|
|
1784
2067
|
children: [
|
|
1785
|
-
/* @__PURE__ */
|
|
1786
|
-
hideHeader ? null : /* @__PURE__ */
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
-
|
|
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(
|
|
1789
2081
|
ReportSearch,
|
|
1790
2082
|
{
|
|
1791
2083
|
ref: searchRef,
|
|
@@ -1795,8 +2087,8 @@ function ReportInteractiveView({
|
|
|
1795
2087
|
totalCount: totalScenarios
|
|
1796
2088
|
}
|
|
1797
2089
|
),
|
|
1798
|
-
/* @__PURE__ */
|
|
1799
|
-
/* @__PURE__ */
|
|
2090
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex shrink-0 gap-1", children: [
|
|
2091
|
+
/* @__PURE__ */ jsx39(
|
|
1800
2092
|
"button",
|
|
1801
2093
|
{
|
|
1802
2094
|
type: "button",
|
|
@@ -1806,7 +2098,7 @@ function ReportInteractiveView({
|
|
|
1806
2098
|
children: "Expand all"
|
|
1807
2099
|
}
|
|
1808
2100
|
),
|
|
1809
|
-
/* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsx39(
|
|
1810
2102
|
"button",
|
|
1811
2103
|
{
|
|
1812
2104
|
type: "button",
|
|
@@ -1816,7 +2108,7 @@ function ReportInteractiveView({
|
|
|
1816
2108
|
children: "Collapse all"
|
|
1817
2109
|
}
|
|
1818
2110
|
),
|
|
1819
|
-
/* @__PURE__ */
|
|
2111
|
+
/* @__PURE__ */ jsx39(
|
|
1820
2112
|
"button",
|
|
1821
2113
|
{
|
|
1822
2114
|
type: "button",
|
|
@@ -1826,7 +2118,7 @@ function ReportInteractiveView({
|
|
|
1826
2118
|
children: detail === "full" ? "Hide docs" : "Show docs"
|
|
1827
2119
|
}
|
|
1828
2120
|
),
|
|
1829
|
-
/* @__PURE__ */
|
|
2121
|
+
/* @__PURE__ */ jsx39(
|
|
1830
2122
|
"button",
|
|
1831
2123
|
{
|
|
1832
2124
|
type: "button",
|
|
@@ -1838,7 +2130,7 @@ function ReportInteractiveView({
|
|
|
1838
2130
|
)
|
|
1839
2131
|
] })
|
|
1840
2132
|
] }),
|
|
1841
|
-
/* @__PURE__ */
|
|
2133
|
+
/* @__PURE__ */ jsx39(
|
|
1842
2134
|
ReportFilters,
|
|
1843
2135
|
{
|
|
1844
2136
|
statuses: statusOptions,
|
|
@@ -1850,12 +2142,12 @@ function ReportInteractiveView({
|
|
|
1850
2142
|
}
|
|
1851
2143
|
)
|
|
1852
2144
|
] }),
|
|
1853
|
-
/* @__PURE__ */
|
|
1854
|
-
hasContent ? /* @__PURE__ */
|
|
1855
|
-
/* @__PURE__ */
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
] }) : /* @__PURE__ */
|
|
1858
|
-
/* @__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(
|
|
1859
2151
|
"button",
|
|
1860
2152
|
{
|
|
1861
2153
|
type: "button",
|
|
@@ -1866,8 +2158,8 @@ function ReportInteractiveView({
|
|
|
1866
2158
|
children: "?"
|
|
1867
2159
|
}
|
|
1868
2160
|
),
|
|
1869
|
-
/* @__PURE__ */
|
|
1870
|
-
toast ? /* @__PURE__ */
|
|
2161
|
+
/* @__PURE__ */ jsx39(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
|
|
2162
|
+
toast ? /* @__PURE__ */ jsx39(
|
|
1871
2163
|
"div",
|
|
1872
2164
|
{
|
|
1873
2165
|
role: "status",
|
|
@@ -1880,18 +2172,18 @@ function ReportInteractiveView({
|
|
|
1880
2172
|
}
|
|
1881
2173
|
)
|
|
1882
2174
|
}
|
|
1883
|
-
) }) });
|
|
2175
|
+
) }) }) });
|
|
1884
2176
|
}
|
|
1885
2177
|
|
|
1886
2178
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
1887
2179
|
import { useMemo as useMemo4 } from "react";
|
|
1888
2180
|
|
|
1889
2181
|
// src/interactive/island-renderers.tsx
|
|
1890
|
-
import { createElement, useEffect as
|
|
2182
|
+
import { createElement, useEffect as useEffect8, useState as useState7 } from "react";
|
|
1891
2183
|
|
|
1892
2184
|
// src/components/doc/MermaidView.tsx
|
|
1893
|
-
import { useEffect as
|
|
1894
|
-
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";
|
|
1895
2187
|
function prefersDark(el) {
|
|
1896
2188
|
const themed = el?.closest("[data-theme]");
|
|
1897
2189
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -1900,8 +2192,8 @@ function prefersDark(el) {
|
|
|
1900
2192
|
}
|
|
1901
2193
|
function MermaidView({ entry, load }) {
|
|
1902
2194
|
const ref = useRef4(null);
|
|
1903
|
-
const [svg, setSvg] =
|
|
1904
|
-
|
|
2195
|
+
const [svg, setSvg] = useState6(null);
|
|
2196
|
+
useEffect7(() => {
|
|
1905
2197
|
let cancelled = false;
|
|
1906
2198
|
const draw = async () => {
|
|
1907
2199
|
try {
|
|
@@ -1928,9 +2220,9 @@ function MermaidView({ entry, load }) {
|
|
|
1928
2220
|
};
|
|
1929
2221
|
}, [entry.code, load]);
|
|
1930
2222
|
if (svg) {
|
|
1931
|
-
return /* @__PURE__ */
|
|
1932
|
-
entry.title ? /* @__PURE__ */
|
|
1933
|
-
/* @__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(
|
|
1934
2226
|
"div",
|
|
1935
2227
|
{
|
|
1936
2228
|
role: "img",
|
|
@@ -1941,11 +2233,11 @@ function MermaidView({ entry, load }) {
|
|
|
1941
2233
|
)
|
|
1942
2234
|
] });
|
|
1943
2235
|
}
|
|
1944
|
-
return /* @__PURE__ */
|
|
2236
|
+
return /* @__PURE__ */ jsx40(MermaidSource, { entry, ref });
|
|
1945
2237
|
}
|
|
1946
2238
|
|
|
1947
2239
|
// src/interactive/island-renderers.tsx
|
|
1948
|
-
import { jsx as
|
|
2240
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
1949
2241
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
1950
2242
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
1951
2243
|
function cachedCdnModule(url) {
|
|
@@ -1962,8 +2254,8 @@ function cachedCdnModule(url) {
|
|
|
1962
2254
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
1963
2255
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
1964
2256
|
function CdnHighlightedCode({ entry }) {
|
|
1965
|
-
const [html, setHtml] =
|
|
1966
|
-
|
|
2257
|
+
const [html, setHtml] = useState7(null);
|
|
2258
|
+
useEffect8(() => {
|
|
1967
2259
|
let cancelled = false;
|
|
1968
2260
|
void (async () => {
|
|
1969
2261
|
try {
|
|
@@ -1979,13 +2271,13 @@ function CdnHighlightedCode({ entry }) {
|
|
|
1979
2271
|
cancelled = true;
|
|
1980
2272
|
};
|
|
1981
2273
|
}, [entry.content, entry.lang]);
|
|
1982
|
-
return /* @__PURE__ */
|
|
2274
|
+
return /* @__PURE__ */ jsx41(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ jsx41(
|
|
1983
2275
|
"code",
|
|
1984
2276
|
{
|
|
1985
2277
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
1986
2278
|
dangerouslySetInnerHTML: { __html: html }
|
|
1987
2279
|
}
|
|
1988
|
-
) : /* @__PURE__ */
|
|
2280
|
+
) : /* @__PURE__ */ jsx41("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
1989
2281
|
}
|
|
1990
2282
|
function buildIslandRenderers(opts) {
|
|
1991
2283
|
const renderers = {};
|
|
@@ -1999,7 +2291,7 @@ function buildIslandRenderers(opts) {
|
|
|
1999
2291
|
}
|
|
2000
2292
|
|
|
2001
2293
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
2002
|
-
import { jsx as
|
|
2294
|
+
import { jsx as jsx42 } from "react/jsx-runtime";
|
|
2003
2295
|
function ReportInteractiveIsland({
|
|
2004
2296
|
report,
|
|
2005
2297
|
title,
|
|
@@ -2012,7 +2304,7 @@ function ReportInteractiveIsland({
|
|
|
2012
2304
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
2013
2305
|
[syntaxHighlighting, mermaid]
|
|
2014
2306
|
);
|
|
2015
|
-
return /* @__PURE__ */
|
|
2307
|
+
return /* @__PURE__ */ jsx42(
|
|
2016
2308
|
ReportInteractive,
|
|
2017
2309
|
{
|
|
2018
2310
|
report,
|
|
@@ -2025,6 +2317,7 @@ function ReportInteractiveIsland({
|
|
|
2025
2317
|
}
|
|
2026
2318
|
export {
|
|
2027
2319
|
ReportFailureBanner,
|
|
2320
|
+
ReportFreshness,
|
|
2028
2321
|
ReportInteractive,
|
|
2029
2322
|
ReportInteractiveIsland,
|
|
2030
2323
|
ReportSearch,
|