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/index.cjs
CHANGED
|
@@ -64,9 +64,18 @@ __export(src_exports, {
|
|
|
64
64
|
ReportTrace: () => ReportTrace,
|
|
65
65
|
STORY_REPORT_SCHEMA_MAJOR: () => STORY_REPORT_SCHEMA_MAJOR,
|
|
66
66
|
ScenarioExplorer: () => ScenarioExplorer,
|
|
67
|
+
ScenarioRunHistory: () => ScenarioRunHistory,
|
|
68
|
+
ciDisplayName: () => ciDisplayName,
|
|
69
|
+
commitUrl: () => commitUrl,
|
|
70
|
+
currentStreak: () => currentStreak,
|
|
71
|
+
describeRunHistory: () => describeRunHistory,
|
|
67
72
|
err: () => err,
|
|
73
|
+
formatRelativeAge: () => formatRelativeAge,
|
|
74
|
+
isReportStale: () => isReportStale,
|
|
68
75
|
ok: () => ok,
|
|
69
76
|
parseStoryReport: () => parseStoryReport,
|
|
77
|
+
prUrl: () => prUrl,
|
|
78
|
+
reportLastRunMs: () => reportLastRunMs,
|
|
70
79
|
storyReportSchema: () => storyReportSchema,
|
|
71
80
|
useBuiltinRenderers: () => useBuiltinRenderers,
|
|
72
81
|
useCustomRenderers: () => useCustomRenderers,
|
|
@@ -124,11 +133,23 @@ function useReport() {
|
|
|
124
133
|
return ctx.report;
|
|
125
134
|
}
|
|
126
135
|
|
|
127
|
-
//
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
// ../executable-stories-core/dist/utils/duration.js
|
|
137
|
+
function formatDuration(ms) {
|
|
138
|
+
if (ms < 1e3) {
|
|
139
|
+
return `${Math.round(ms)} ms`;
|
|
140
|
+
}
|
|
141
|
+
if (ms < 6e4) {
|
|
142
|
+
return `${(ms / 1e3).toFixed(2)} s`;
|
|
143
|
+
}
|
|
144
|
+
const minutes = Math.floor(ms / 6e4);
|
|
145
|
+
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
146
|
+
if (minutes < 60) {
|
|
147
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
148
|
+
}
|
|
149
|
+
const hours = Math.floor(minutes / 60);
|
|
150
|
+
const remainingMinutes = minutes % 60;
|
|
151
|
+
return `${hours}h ${remainingMinutes}m`;
|
|
152
|
+
}
|
|
132
153
|
|
|
133
154
|
// src/components/doc/DocNote.tsx
|
|
134
155
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -312,7 +333,7 @@ function DocSection({ entry }) {
|
|
|
312
333
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
313
334
|
"div",
|
|
314
335
|
{
|
|
315
|
-
className: "es-doc-prose
|
|
336
|
+
className: "es-doc-prose prose prose-sm max-w-none",
|
|
316
337
|
dangerouslySetInnerHTML: { __html: html }
|
|
317
338
|
}
|
|
318
339
|
)
|
|
@@ -553,7 +574,7 @@ function ReportStepItem({ step }) {
|
|
|
553
574
|
}
|
|
554
575
|
),
|
|
555
576
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
556
|
-
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children:
|
|
577
|
+
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
|
|
557
578
|
] }),
|
|
558
579
|
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
559
580
|
"pre",
|
|
@@ -611,9 +632,6 @@ function ReportAttachments({ attachments }) {
|
|
|
611
632
|
);
|
|
612
633
|
}
|
|
613
634
|
|
|
614
|
-
// src/components/ReportTrace.tsx
|
|
615
|
-
var import_duration2 = require("executable-stories-core/utils/duration");
|
|
616
|
-
|
|
617
635
|
// src/lib/trace.ts
|
|
618
636
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
619
637
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -708,7 +726,7 @@ function ReportTrace({ spans }) {
|
|
|
708
726
|
" span",
|
|
709
727
|
rows.length === 1 ? "" : "s",
|
|
710
728
|
", ",
|
|
711
|
-
|
|
729
|
+
formatDuration(totalMs),
|
|
712
730
|
")"
|
|
713
731
|
] }),
|
|
714
732
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
|
|
@@ -731,33 +749,128 @@ function ReportTrace({ spans }) {
|
|
|
731
749
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
732
750
|
}
|
|
733
751
|
) }),
|
|
734
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children:
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
|
|
735
753
|
] }, `${row.spanId}-${i}`);
|
|
736
754
|
}) })
|
|
737
755
|
] });
|
|
738
756
|
}
|
|
739
757
|
|
|
740
|
-
// src/interactive/
|
|
758
|
+
// src/interactive/scenario-history-context.ts
|
|
741
759
|
var import_react5 = require("react");
|
|
742
|
-
var
|
|
760
|
+
var ScenarioHistoryContext = (0, import_react5.createContext)(null);
|
|
761
|
+
var ScenarioHistoryProvider = ScenarioHistoryContext.Provider;
|
|
762
|
+
function useScenarioHistory() {
|
|
763
|
+
return (0, import_react5.useContext)(ScenarioHistoryContext);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// src/lib/run-history.ts
|
|
767
|
+
function currentStreak(entries) {
|
|
768
|
+
const last = entries[entries.length - 1];
|
|
769
|
+
if (!last) return void 0;
|
|
770
|
+
let count = 0;
|
|
771
|
+
for (let i = entries.length - 1; i >= 0 && entries[i].status === last.status; i--) count++;
|
|
772
|
+
return { status: last.status, count };
|
|
773
|
+
}
|
|
774
|
+
var STREAK_VERB = {
|
|
775
|
+
passed: "Passing",
|
|
776
|
+
failed: "Failing",
|
|
777
|
+
skipped: "Skipped",
|
|
778
|
+
pending: "Pending"
|
|
779
|
+
};
|
|
780
|
+
function describeRunHistory(entries) {
|
|
781
|
+
const passed = entries.filter((e) => e.status === "passed").length;
|
|
782
|
+
const base = `${passed}/${entries.length} runs passed`;
|
|
783
|
+
const streak = currentStreak(entries);
|
|
784
|
+
if (!streak || entries.length < 2) return base;
|
|
785
|
+
return `${base} \xB7 ${STREAK_VERB[streak.status]} for the last ${streak.count === 1 ? "run" : `${streak.count} runs`}`;
|
|
786
|
+
}
|
|
787
|
+
var MIN_FLAKINESS_SAMPLES = 3;
|
|
788
|
+
function flakinessOf(entries) {
|
|
789
|
+
const countable = entries.filter((e) => e.status === "passed" || e.status === "failed");
|
|
790
|
+
if (countable.length < MIN_FLAKINESS_SAMPLES) return "stable";
|
|
791
|
+
let transitions = 0;
|
|
792
|
+
for (let i = 1; i < countable.length; i++) {
|
|
793
|
+
if (countable[i].status !== countable[i - 1].status) transitions++;
|
|
794
|
+
}
|
|
795
|
+
const transitionScore = transitions / (countable.length - 1);
|
|
796
|
+
const failureRate = countable.filter((e) => e.status === "failed").length / countable.length;
|
|
797
|
+
if (transitionScore > 0.5 || transitionScore > 0.3 && failureRate > 0.2) return "flaky";
|
|
798
|
+
if (transitionScore > 0.2 || failureRate > 0.3) return "unstable";
|
|
799
|
+
return "stable";
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// src/components/ScenarioRunHistory.tsx
|
|
803
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
804
|
+
var MAX_DOTS = 10;
|
|
805
|
+
var DOT_COLOR = {
|
|
806
|
+
passed: "bg-pass",
|
|
807
|
+
failed: "bg-fail",
|
|
808
|
+
skipped: "bg-skip",
|
|
809
|
+
pending: "bg-pend"
|
|
810
|
+
};
|
|
811
|
+
function dotTitle(e) {
|
|
812
|
+
const parts = [new Date(e.timestamp).toLocaleString(), e.status];
|
|
813
|
+
if (e.commitSha) parts.push(e.commitSha.slice(0, 8));
|
|
814
|
+
return parts.join(" \xB7 ");
|
|
815
|
+
}
|
|
816
|
+
function ScenarioRunHistory({ scenarioId }) {
|
|
817
|
+
const history = useScenarioHistory();
|
|
818
|
+
const entries = history?.[scenarioId];
|
|
819
|
+
if (!entries || entries.length < 2) return null;
|
|
820
|
+
const recent = entries.slice(-MAX_DOTS);
|
|
821
|
+
const flaky = flakinessOf(recent) === "flaky";
|
|
822
|
+
const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
|
|
823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
824
|
+
flaky ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
826
|
+
"span",
|
|
827
|
+
{
|
|
828
|
+
role: "img",
|
|
829
|
+
"aria-label": `Run history: ${summary}`,
|
|
830
|
+
title: summary,
|
|
831
|
+
className: "flex shrink-0 items-center gap-[3px]",
|
|
832
|
+
children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
833
|
+
"span",
|
|
834
|
+
{
|
|
835
|
+
"aria-hidden": true,
|
|
836
|
+
title: dotTitle(e),
|
|
837
|
+
className: cn(
|
|
838
|
+
"inline-block size-1.5 rounded-full",
|
|
839
|
+
DOT_COLOR[e.status],
|
|
840
|
+
// The newest run (rightmost dot) is the one the card's status
|
|
841
|
+
// badge already describes; ring it so the timeline reads as
|
|
842
|
+
// "…leading up to now".
|
|
843
|
+
i === recent.length - 1 && "ring-1 ring-border ring-offset-1 ring-offset-card"
|
|
844
|
+
)
|
|
845
|
+
},
|
|
846
|
+
`${e.timestamp}-${i}`
|
|
847
|
+
))
|
|
848
|
+
}
|
|
849
|
+
)
|
|
850
|
+
] });
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/interactive/collapse-context.ts
|
|
854
|
+
var import_react6 = require("react");
|
|
855
|
+
var CollapseContext = (0, import_react6.createContext)(null);
|
|
743
856
|
var CollapseProvider = CollapseContext.Provider;
|
|
744
857
|
function useCollapse() {
|
|
745
|
-
return (0,
|
|
858
|
+
return (0, import_react6.useContext)(CollapseContext);
|
|
746
859
|
}
|
|
747
860
|
|
|
748
861
|
// src/interactive/scenario-actions.tsx
|
|
749
|
-
var
|
|
750
|
-
var ScenarioActionsContext = (0,
|
|
862
|
+
var import_react7 = require("react");
|
|
863
|
+
var ScenarioActionsContext = (0, import_react7.createContext)(null);
|
|
751
864
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
752
865
|
function useScenarioActions() {
|
|
753
|
-
return (0,
|
|
866
|
+
return (0, import_react7.useContext)(ScenarioActionsContext);
|
|
754
867
|
}
|
|
755
868
|
|
|
756
869
|
// src/components/ui/card.tsx
|
|
757
870
|
var React2 = require("react");
|
|
758
|
-
var
|
|
871
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
759
872
|
function Card({ className, ...props }) {
|
|
760
|
-
return /* @__PURE__ */ (0,
|
|
873
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
761
874
|
"div",
|
|
762
875
|
{
|
|
763
876
|
"data-slot": "card",
|
|
@@ -770,7 +883,7 @@ function Card({ className, ...props }) {
|
|
|
770
883
|
);
|
|
771
884
|
}
|
|
772
885
|
function CardHeader({ className, ...props }) {
|
|
773
|
-
return /* @__PURE__ */ (0,
|
|
886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
774
887
|
"div",
|
|
775
888
|
{
|
|
776
889
|
"data-slot": "card-header",
|
|
@@ -783,7 +896,7 @@ function CardHeader({ className, ...props }) {
|
|
|
783
896
|
);
|
|
784
897
|
}
|
|
785
898
|
function CardContent({ className, ...props }) {
|
|
786
|
-
return /* @__PURE__ */ (0,
|
|
899
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
787
900
|
"div",
|
|
788
901
|
{
|
|
789
902
|
"data-slot": "card-content",
|
|
@@ -794,7 +907,7 @@ function CardContent({ className, ...props }) {
|
|
|
794
907
|
}
|
|
795
908
|
|
|
796
909
|
// src/components/ReportScenario.tsx
|
|
797
|
-
var
|
|
910
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
798
911
|
var STATUS_LABEL = {
|
|
799
912
|
passed: "Passed",
|
|
800
913
|
failed: "Failed",
|
|
@@ -822,12 +935,12 @@ var STATUS_BADGE = {
|
|
|
822
935
|
function ReportScenario({ scenario, hideTitle = false }) {
|
|
823
936
|
const titleId = `${scenario.id}-title`;
|
|
824
937
|
const bodyId = `${scenario.id}-body`;
|
|
825
|
-
const label = STATUS_LABEL[scenario.status];
|
|
938
|
+
const label = scenario.planned ? "Planned" : STATUS_LABEL[scenario.status];
|
|
826
939
|
const collapse = useCollapse();
|
|
827
940
|
const collapsible = collapse !== null && !hideTitle;
|
|
828
941
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
829
942
|
const actions = useScenarioActions();
|
|
830
|
-
return /* @__PURE__ */ (0,
|
|
943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
831
944
|
Card,
|
|
832
945
|
{
|
|
833
946
|
id: scenario.id,
|
|
@@ -835,10 +948,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
835
948
|
...hideTitle ? { "aria-label": `${scenario.title} \u2014 ${label}` } : { "aria-labelledby": titleId },
|
|
836
949
|
className: "gap-0 py-0",
|
|
837
950
|
children: [
|
|
838
|
-
/* @__PURE__ */ (0,
|
|
839
|
-
/* @__PURE__ */ (0,
|
|
840
|
-
hideTitle ? null : /* @__PURE__ */ (0,
|
|
841
|
-
collapsible ? /* @__PURE__ */ (0,
|
|
951
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CardHeader, { className: "px-4 py-3", children: [
|
|
952
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
953
|
+
hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
954
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
842
955
|
"button",
|
|
843
956
|
{
|
|
844
957
|
type: "button",
|
|
@@ -847,17 +960,18 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
847
960
|
"aria-controls": bodyId,
|
|
848
961
|
"aria-label": `Toggle ${scenario.title}`,
|
|
849
962
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
850
|
-
children: /* @__PURE__ */ (0,
|
|
963
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
851
964
|
}
|
|
852
965
|
) : null,
|
|
853
|
-
/* @__PURE__ */ (0,
|
|
854
|
-
/* @__PURE__ */ (0,
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", STATUS_GLYPH_COLOR[scenario.status]), children: STATUS_GLYPH[scenario.status] }),
|
|
967
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: scenario.title })
|
|
855
968
|
] }),
|
|
856
|
-
/* @__PURE__ */ (0,
|
|
857
|
-
|
|
858
|
-
/* @__PURE__ */ (0,
|
|
859
|
-
|
|
860
|
-
|
|
969
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
970
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
971
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
|
|
972
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: STATUS_BADGE[scenario.status], "aria-label": `Status: ${label}`, children: label }),
|
|
973
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-0.5", children: [
|
|
974
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
861
975
|
"button",
|
|
862
976
|
{
|
|
863
977
|
type: "button",
|
|
@@ -868,7 +982,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
868
982
|
children: "link"
|
|
869
983
|
}
|
|
870
984
|
),
|
|
871
|
-
/* @__PURE__ */ (0,
|
|
985
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
872
986
|
"button",
|
|
873
987
|
{
|
|
874
988
|
type: "button",
|
|
@@ -879,7 +993,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
879
993
|
children: "md"
|
|
880
994
|
}
|
|
881
995
|
),
|
|
882
|
-
/* @__PURE__ */ (0,
|
|
996
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
883
997
|
"button",
|
|
884
998
|
{
|
|
885
999
|
type: "button",
|
|
@@ -893,13 +1007,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
893
1007
|
] }) : null
|
|
894
1008
|
] })
|
|
895
1009
|
] }),
|
|
896
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0,
|
|
897
|
-
scenario.tags.map((t) => /* @__PURE__ */ (0,
|
|
898
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0,
|
|
1010
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
1011
|
+
scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
1012
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
899
1013
|
] }) : null
|
|
900
1014
|
] }),
|
|
901
|
-
/* @__PURE__ */ (0,
|
|
902
|
-
scenario.errorMessage ? /* @__PURE__ */ (0,
|
|
1015
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1016
|
+
scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
903
1017
|
"pre",
|
|
904
1018
|
{
|
|
905
1019
|
role: "alert",
|
|
@@ -907,10 +1021,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
907
1021
|
children: scenario.errorMessage
|
|
908
1022
|
}
|
|
909
1023
|
) : null,
|
|
910
|
-
/* @__PURE__ */ (0,
|
|
911
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
912
|
-
/* @__PURE__ */ (0,
|
|
913
|
-
/* @__PURE__ */ (0,
|
|
1024
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportSteps, { scenario }),
|
|
1025
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
1026
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportAttachments, { attachments: scenario.attachments }),
|
|
1027
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportTrace, { spans: scenario.otelSpans })
|
|
914
1028
|
] })
|
|
915
1029
|
]
|
|
916
1030
|
}
|
|
@@ -918,13 +1032,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
918
1032
|
}
|
|
919
1033
|
|
|
920
1034
|
// src/components/ReportScenarioList.tsx
|
|
921
|
-
var
|
|
1035
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
922
1036
|
function ReportScenarioList({ feature }) {
|
|
923
|
-
return /* @__PURE__ */ (0,
|
|
1037
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportScenario, { scenario }, scenario.id)) });
|
|
924
1038
|
}
|
|
925
1039
|
|
|
926
1040
|
// src/components/ReportFeature.tsx
|
|
927
|
-
var
|
|
1041
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
928
1042
|
function ReportFeature({ feature }) {
|
|
929
1043
|
const titleId = `${feature.id}-title`;
|
|
930
1044
|
const bodyId = `${feature.id}-body`;
|
|
@@ -932,7 +1046,7 @@ function ReportFeature({ feature }) {
|
|
|
932
1046
|
const skipped = s.skipped + s.pending;
|
|
933
1047
|
const collapse = useCollapse();
|
|
934
1048
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
935
|
-
return /* @__PURE__ */ (0,
|
|
1049
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
936
1050
|
"section",
|
|
937
1051
|
{
|
|
938
1052
|
id: feature.id,
|
|
@@ -940,9 +1054,9 @@ function ReportFeature({ feature }) {
|
|
|
940
1054
|
"aria-labelledby": titleId,
|
|
941
1055
|
className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm",
|
|
942
1056
|
children: [
|
|
943
|
-
/* @__PURE__ */ (0,
|
|
944
|
-
/* @__PURE__ */ (0,
|
|
945
|
-
collapse ? /* @__PURE__ */ (0,
|
|
1057
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center justify-between gap-4 border-b border-border bg-muted/40 px-4 py-3", children: [
|
|
1058
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1059
|
+
collapse ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
946
1060
|
"button",
|
|
947
1061
|
{
|
|
948
1062
|
type: "button",
|
|
@@ -951,30 +1065,30 @@ function ReportFeature({ feature }) {
|
|
|
951
1065
|
"aria-controls": bodyId,
|
|
952
1066
|
"aria-label": `Toggle ${feature.title}`,
|
|
953
1067
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
954
|
-
children: /* @__PURE__ */ (0,
|
|
1068
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
955
1069
|
}
|
|
956
1070
|
) : null,
|
|
957
|
-
/* @__PURE__ */ (0,
|
|
958
|
-
/* @__PURE__ */ (0,
|
|
959
|
-
/* @__PURE__ */ (0,
|
|
1071
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "min-w-0", children: [
|
|
1072
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { id: titleId, className: "text-sm font-semibold tracking-tight text-foreground", children: feature.title }),
|
|
1073
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mt-0.5 truncate font-mono text-xs text-muted-foreground", children: feature.sourceFile })
|
|
960
1074
|
] })
|
|
961
1075
|
] }),
|
|
962
|
-
/* @__PURE__ */ (0,
|
|
1076
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
963
1077
|
"div",
|
|
964
1078
|
{
|
|
965
1079
|
role: "img",
|
|
966
1080
|
className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
|
|
967
1081
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
968
1082
|
children: [
|
|
969
|
-
/* @__PURE__ */ (0,
|
|
1083
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-pass", children: [
|
|
970
1084
|
"\u2713",
|
|
971
1085
|
s.passed
|
|
972
1086
|
] }),
|
|
973
|
-
/* @__PURE__ */ (0,
|
|
1087
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-fail", children: [
|
|
974
1088
|
"\u2717",
|
|
975
1089
|
s.failed
|
|
976
1090
|
] }),
|
|
977
|
-
/* @__PURE__ */ (0,
|
|
1091
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-skip", children: [
|
|
978
1092
|
"\u25CB",
|
|
979
1093
|
skipped
|
|
980
1094
|
] })
|
|
@@ -982,24 +1096,24 @@ function ReportFeature({ feature }) {
|
|
|
982
1096
|
}
|
|
983
1097
|
)
|
|
984
1098
|
] }),
|
|
985
|
-
/* @__PURE__ */ (0,
|
|
1099
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-2 p-3", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportScenarioList, { feature }) })
|
|
986
1100
|
]
|
|
987
1101
|
}
|
|
988
1102
|
);
|
|
989
1103
|
}
|
|
990
1104
|
|
|
991
1105
|
// src/components/ReportFeatureList.tsx
|
|
992
|
-
var
|
|
1106
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
993
1107
|
function ReportFeatureList() {
|
|
994
1108
|
const report = useReport();
|
|
995
|
-
return /* @__PURE__ */ (0,
|
|
1109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportFeature, { feature }, feature.id)) });
|
|
996
1110
|
}
|
|
997
1111
|
|
|
998
1112
|
// src/components/ui/empty.tsx
|
|
999
1113
|
var import_class_variance_authority2 = require("class-variance-authority");
|
|
1000
|
-
var
|
|
1114
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1001
1115
|
function Empty({ className, ...props }) {
|
|
1002
|
-
return /* @__PURE__ */ (0,
|
|
1116
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1003
1117
|
"div",
|
|
1004
1118
|
{
|
|
1005
1119
|
"data-slot": "empty",
|
|
@@ -1012,7 +1126,7 @@ function Empty({ className, ...props }) {
|
|
|
1012
1126
|
);
|
|
1013
1127
|
}
|
|
1014
1128
|
function EmptyHeader({ className, ...props }) {
|
|
1015
|
-
return /* @__PURE__ */ (0,
|
|
1129
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1016
1130
|
"div",
|
|
1017
1131
|
{
|
|
1018
1132
|
"data-slot": "empty-header",
|
|
@@ -1039,7 +1153,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
|
1039
1153
|
}
|
|
1040
1154
|
);
|
|
1041
1155
|
function EmptyTitle({ className, ...props }) {
|
|
1042
|
-
return /* @__PURE__ */ (0,
|
|
1156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1043
1157
|
"div",
|
|
1044
1158
|
{
|
|
1045
1159
|
"data-slot": "empty-title",
|
|
@@ -1050,16 +1164,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1050
1164
|
}
|
|
1051
1165
|
|
|
1052
1166
|
// src/components/ReportEmpty.tsx
|
|
1053
|
-
var
|
|
1167
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1054
1168
|
function ReportEmpty({ message }) {
|
|
1055
|
-
return /* @__PURE__ */ (0,
|
|
1169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1056
1170
|
}
|
|
1057
1171
|
|
|
1058
1172
|
// src/components/ReportSummary.tsx
|
|
1059
|
-
var
|
|
1173
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1060
1174
|
function ReportSummary({ className }) {
|
|
1061
1175
|
const report = useReport();
|
|
1062
|
-
return /* @__PURE__ */ (0,
|
|
1176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1063
1177
|
ReportSummaryView,
|
|
1064
1178
|
{
|
|
1065
1179
|
summary: report.summary,
|
|
@@ -1069,32 +1183,32 @@ function ReportSummary({ className }) {
|
|
|
1069
1183
|
);
|
|
1070
1184
|
}
|
|
1071
1185
|
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
1072
|
-
return /* @__PURE__ */ (0,
|
|
1073
|
-
/* @__PURE__ */ (0,
|
|
1074
|
-
/* @__PURE__ */ (0,
|
|
1186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: cn("text-sm text-muted-foreground", className), "aria-label": ariaLabel, children: [
|
|
1187
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
|
|
1188
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("strong", { className: "font-semibold text-foreground", children: summary.total }),
|
|
1075
1189
|
" scenario",
|
|
1076
1190
|
summary.total === 1 ? "" : "s"
|
|
1077
1191
|
] }),
|
|
1078
1192
|
" \xB7 ",
|
|
1079
|
-
/* @__PURE__ */ (0,
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "passed", className: "text-pass", children: [
|
|
1080
1194
|
summary.passed,
|
|
1081
1195
|
" passed"
|
|
1082
1196
|
] }),
|
|
1083
1197
|
" \xB7 ",
|
|
1084
|
-
/* @__PURE__ */ (0,
|
|
1198
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "failed", className: "text-fail", children: [
|
|
1085
1199
|
summary.failed,
|
|
1086
1200
|
" failed"
|
|
1087
1201
|
] }),
|
|
1088
|
-
summary.skipped > 0 ? /* @__PURE__ */ (0,
|
|
1202
|
+
summary.skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
1089
1203
|
" \xB7 ",
|
|
1090
|
-
/* @__PURE__ */ (0,
|
|
1204
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "skipped", className: "text-skip", children: [
|
|
1091
1205
|
summary.skipped,
|
|
1092
1206
|
" skipped"
|
|
1093
1207
|
] })
|
|
1094
1208
|
] }) : null,
|
|
1095
|
-
summary.pending > 0 ? /* @__PURE__ */ (0,
|
|
1209
|
+
summary.pending > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
1096
1210
|
" \xB7 ",
|
|
1097
|
-
/* @__PURE__ */ (0,
|
|
1211
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "pending", className: "text-pend", children: [
|
|
1098
1212
|
summary.pending,
|
|
1099
1213
|
" pending"
|
|
1100
1214
|
] })
|
|
@@ -1102,69 +1216,135 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1102
1216
|
] });
|
|
1103
1217
|
}
|
|
1104
1218
|
|
|
1219
|
+
// src/lib/provenance.ts
|
|
1220
|
+
function reportLastRunMs(report) {
|
|
1221
|
+
if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
|
|
1222
|
+
if (report.startedAtMs && report.startedAtMs > 0) return report.startedAtMs;
|
|
1223
|
+
return void 0;
|
|
1224
|
+
}
|
|
1225
|
+
function formatRelativeAge(thenMs, nowMs) {
|
|
1226
|
+
const deltaMs = Math.max(0, nowMs - thenMs);
|
|
1227
|
+
const minutes = Math.floor(deltaMs / 6e4);
|
|
1228
|
+
if (minutes < 1) return "just now";
|
|
1229
|
+
if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
|
|
1230
|
+
const hours = Math.floor(minutes / 60);
|
|
1231
|
+
if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
|
|
1232
|
+
const days = Math.floor(hours / 24);
|
|
1233
|
+
return `${days} day${days === 1 ? "" : "s"} ago`;
|
|
1234
|
+
}
|
|
1235
|
+
function isReportStale(lastRunMs, staleAfterDays, nowMs) {
|
|
1236
|
+
if (lastRunMs === void 0 || staleAfterDays <= 0) return false;
|
|
1237
|
+
return nowMs - lastRunMs >= staleAfterDays * 864e5;
|
|
1238
|
+
}
|
|
1239
|
+
var CI_DISPLAY_NAMES = {
|
|
1240
|
+
github: "GitHub Actions",
|
|
1241
|
+
gitlab: "GitLab CI",
|
|
1242
|
+
circleci: "CircleCI",
|
|
1243
|
+
jenkins: "Jenkins",
|
|
1244
|
+
azure: "Azure DevOps",
|
|
1245
|
+
buildkite: "Buildkite",
|
|
1246
|
+
travis: "Travis CI",
|
|
1247
|
+
ci: "CI"
|
|
1248
|
+
};
|
|
1249
|
+
function ciDisplayName(name) {
|
|
1250
|
+
return CI_DISPLAY_NAMES[name] ?? name;
|
|
1251
|
+
}
|
|
1252
|
+
function commitUrl(ci, sha) {
|
|
1253
|
+
const commitSha = ci?.commitSha ?? sha;
|
|
1254
|
+
if (!ci?.url || !commitSha) return void 0;
|
|
1255
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1256
|
+
if (github) return `${github[1]}/commit/${commitSha}`;
|
|
1257
|
+
const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
|
|
1258
|
+
if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
|
|
1259
|
+
return void 0;
|
|
1260
|
+
}
|
|
1261
|
+
function prUrl(ci) {
|
|
1262
|
+
if (!ci?.url || !ci.prNumber) return void 0;
|
|
1263
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1264
|
+
if (github) return `${github[1]}/pull/${ci.prNumber}`;
|
|
1265
|
+
return void 0;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1105
1268
|
// src/components/ReportMeta.tsx
|
|
1106
|
-
var
|
|
1107
|
-
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1269
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1108
1270
|
function ReportMeta() {
|
|
1109
1271
|
const report = useReport();
|
|
1110
1272
|
const items = [];
|
|
1273
|
+
const linkClass = "text-primary underline underline-offset-2";
|
|
1111
1274
|
if (report.startedAtMs) {
|
|
1112
1275
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1113
1276
|
}
|
|
1114
1277
|
if (report.durationMs) {
|
|
1115
|
-
items.push({ label: "Duration", value:
|
|
1278
|
+
items.push({ label: "Duration", value: formatDuration(report.durationMs) });
|
|
1116
1279
|
}
|
|
1117
1280
|
if (report.packageVersion) {
|
|
1118
1281
|
items.push({ label: "Version", value: report.packageVersion });
|
|
1119
1282
|
}
|
|
1120
|
-
if (report.
|
|
1121
|
-
items.push({ label: "
|
|
1283
|
+
if (report.ci?.branch) {
|
|
1284
|
+
items.push({ label: "Branch", value: report.ci.branch });
|
|
1285
|
+
}
|
|
1286
|
+
const sha = report.ci?.commitSha ?? report.gitSha;
|
|
1287
|
+
if (sha) {
|
|
1288
|
+
const url = commitUrl(report.ci, report.gitSha);
|
|
1289
|
+
const short = sha.slice(0, 8);
|
|
1290
|
+
items.push({
|
|
1291
|
+
label: "Commit",
|
|
1292
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1293
|
+
});
|
|
1122
1294
|
}
|
|
1123
1295
|
if (report.ci) {
|
|
1124
1296
|
items.push({
|
|
1125
1297
|
label: "CI",
|
|
1126
|
-
value: report.ci.url ? /* @__PURE__ */ (0,
|
|
1127
|
-
report.ci.name,
|
|
1298
|
+
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1299
|
+
ciDisplayName(report.ci.name),
|
|
1128
1300
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1129
|
-
] }) : `${report.ci.name}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1301
|
+
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
if (report.ci?.prNumber) {
|
|
1305
|
+
const url = prUrl(report.ci);
|
|
1306
|
+
const label = `#${report.ci.prNumber}`;
|
|
1307
|
+
items.push({
|
|
1308
|
+
label: "PR",
|
|
1309
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1130
1310
|
});
|
|
1131
1311
|
}
|
|
1132
1312
|
if (items.length === 0) return null;
|
|
1133
|
-
return /* @__PURE__ */ (0,
|
|
1134
|
-
/* @__PURE__ */ (0,
|
|
1135
|
-
/* @__PURE__ */ (0,
|
|
1313
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dl", { "aria-label": "Run metadata", className: "flex flex-wrap gap-x-6 gap-y-1 text-xs text-muted-foreground", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex gap-1.5", children: [
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1315
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1136
1316
|
] }, item.label)) });
|
|
1137
1317
|
}
|
|
1138
1318
|
|
|
1139
1319
|
// src/components/ReportSchemaError.tsx
|
|
1140
|
-
var
|
|
1320
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1141
1321
|
function ReportSchemaError({ error }) {
|
|
1142
|
-
return /* @__PURE__ */ (0,
|
|
1143
|
-
/* @__PURE__ */ (0,
|
|
1144
|
-
/* @__PURE__ */ (0,
|
|
1145
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0,
|
|
1322
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: error.message }),
|
|
1325
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { children: [
|
|
1146
1326
|
"The report bundle is newer than this version of ",
|
|
1147
|
-
/* @__PURE__ */ (0,
|
|
1327
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("code", { children: "executable-stories-react" }),
|
|
1148
1328
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1149
1329
|
] }) : null,
|
|
1150
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0,
|
|
1151
|
-
/* @__PURE__ */ (0,
|
|
1330
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("details", { children: [
|
|
1331
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("summary", { children: [
|
|
1152
1332
|
error.issues.length,
|
|
1153
1333
|
" validation issue",
|
|
1154
1334
|
error.issues.length === 1 ? "" : "s"
|
|
1155
1335
|
] }),
|
|
1156
|
-
/* @__PURE__ */ (0,
|
|
1336
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1157
1337
|
] }) : null
|
|
1158
1338
|
] });
|
|
1159
1339
|
}
|
|
1160
1340
|
|
|
1161
1341
|
// src/components/ReportShell.tsx
|
|
1162
|
-
var
|
|
1342
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1163
1343
|
function ReportTitleBlock({ title }) {
|
|
1164
|
-
return /* @__PURE__ */ (0,
|
|
1165
|
-
/* @__PURE__ */ (0,
|
|
1166
|
-
/* @__PURE__ */ (0,
|
|
1167
|
-
/* @__PURE__ */ (0,
|
|
1344
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h1", { children: title ?? "Story Report" }),
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportSummary, {}),
|
|
1347
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportMeta, {})
|
|
1168
1348
|
] });
|
|
1169
1349
|
}
|
|
1170
1350
|
function ReportErrorShell({
|
|
@@ -1173,28 +1353,28 @@ function ReportErrorShell({
|
|
|
1173
1353
|
title,
|
|
1174
1354
|
dataTheme
|
|
1175
1355
|
}) {
|
|
1176
|
-
return /* @__PURE__ */ (0,
|
|
1356
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportSchemaError, { error }) });
|
|
1177
1357
|
}
|
|
1178
1358
|
|
|
1179
1359
|
// src/components/Report.tsx
|
|
1180
|
-
var
|
|
1360
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1181
1361
|
function Report(props) {
|
|
1182
1362
|
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1183
1363
|
const result = unwrapReport(report);
|
|
1184
1364
|
if (!result.ok) {
|
|
1185
|
-
return /* @__PURE__ */ (0,
|
|
1365
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1186
1366
|
}
|
|
1187
1367
|
const data = result.data;
|
|
1188
1368
|
const hasContent = data.features.length > 0;
|
|
1189
|
-
return /* @__PURE__ */ (0,
|
|
1190
|
-
/* @__PURE__ */ (0,
|
|
1191
|
-
hasContent ? /* @__PURE__ */ (0,
|
|
1369
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
|
|
1370
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("header", { className: "es-report-header", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ReportTitleBlock, { title }) }),
|
|
1371
|
+
hasContent ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ReportFeatureList, {}) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ReportEmpty, {})
|
|
1192
1372
|
] }) });
|
|
1193
1373
|
}
|
|
1194
1374
|
|
|
1195
1375
|
// src/components/doc/MermaidView.tsx
|
|
1196
|
-
var
|
|
1197
|
-
var
|
|
1376
|
+
var import_react8 = require("react");
|
|
1377
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1198
1378
|
function prefersDark(el) {
|
|
1199
1379
|
const themed = el?.closest("[data-theme]");
|
|
1200
1380
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -1202,9 +1382,9 @@ function prefersDark(el) {
|
|
|
1202
1382
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
1203
1383
|
}
|
|
1204
1384
|
function MermaidView({ entry, load }) {
|
|
1205
|
-
const ref = (0,
|
|
1206
|
-
const [svg, setSvg] = (0,
|
|
1207
|
-
(0,
|
|
1385
|
+
const ref = (0, import_react8.useRef)(null);
|
|
1386
|
+
const [svg, setSvg] = (0, import_react8.useState)(null);
|
|
1387
|
+
(0, import_react8.useEffect)(() => {
|
|
1208
1388
|
let cancelled = false;
|
|
1209
1389
|
const draw = async () => {
|
|
1210
1390
|
try {
|
|
@@ -1231,9 +1411,9 @@ function MermaidView({ entry, load }) {
|
|
|
1231
1411
|
};
|
|
1232
1412
|
}, [entry.code, load]);
|
|
1233
1413
|
if (svg) {
|
|
1234
|
-
return /* @__PURE__ */ (0,
|
|
1235
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
1236
|
-
/* @__PURE__ */ (0,
|
|
1414
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("figure", { ref, className: "my-3", children: [
|
|
1415
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1237
1417
|
"div",
|
|
1238
1418
|
{
|
|
1239
1419
|
role: "img",
|
|
@@ -1244,33 +1424,33 @@ function MermaidView({ entry, load }) {
|
|
|
1244
1424
|
)
|
|
1245
1425
|
] });
|
|
1246
1426
|
}
|
|
1247
|
-
return /* @__PURE__ */ (0,
|
|
1427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MermaidSource, { entry, ref });
|
|
1248
1428
|
}
|
|
1249
1429
|
|
|
1250
1430
|
// src/components/doc/MermaidDiagram.tsx
|
|
1251
|
-
var
|
|
1431
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1252
1432
|
var mermaidLoader;
|
|
1253
1433
|
function loadMermaid() {
|
|
1254
1434
|
mermaidLoader ??= import("mermaid").then((m) => m.default);
|
|
1255
1435
|
return mermaidLoader;
|
|
1256
1436
|
}
|
|
1257
1437
|
function MermaidDiagram({ entry, load = loadMermaid }) {
|
|
1258
|
-
return /* @__PURE__ */ (0,
|
|
1438
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(MermaidView, { entry, load });
|
|
1259
1439
|
}
|
|
1260
1440
|
|
|
1261
1441
|
// src/components/ScenarioExplorer.tsx
|
|
1262
|
-
var
|
|
1442
|
+
var import_react9 = require("react");
|
|
1263
1443
|
|
|
1264
1444
|
// src/components/ui/command.tsx
|
|
1265
1445
|
var React3 = require("react");
|
|
1266
1446
|
var import_cmdk = require("cmdk");
|
|
1267
1447
|
var import_lucide_react = require("lucide-react");
|
|
1268
|
-
var
|
|
1448
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1269
1449
|
function Command({
|
|
1270
1450
|
className,
|
|
1271
1451
|
...props
|
|
1272
1452
|
}) {
|
|
1273
|
-
return /* @__PURE__ */ (0,
|
|
1453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1274
1454
|
import_cmdk.Command,
|
|
1275
1455
|
{
|
|
1276
1456
|
"data-slot": "command",
|
|
@@ -1286,14 +1466,14 @@ function CommandInput({
|
|
|
1286
1466
|
className,
|
|
1287
1467
|
...props
|
|
1288
1468
|
}) {
|
|
1289
|
-
return /* @__PURE__ */ (0,
|
|
1469
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
1290
1470
|
"div",
|
|
1291
1471
|
{
|
|
1292
1472
|
"data-slot": "command-input-wrapper",
|
|
1293
1473
|
className: "flex h-9 items-center gap-2 border-b px-3",
|
|
1294
1474
|
children: [
|
|
1295
|
-
/* @__PURE__ */ (0,
|
|
1296
|
-
/* @__PURE__ */ (0,
|
|
1475
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
|
|
1476
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1297
1477
|
import_cmdk.Command.Input,
|
|
1298
1478
|
{
|
|
1299
1479
|
"data-slot": "command-input",
|
|
@@ -1312,7 +1492,7 @@ function CommandList({
|
|
|
1312
1492
|
className,
|
|
1313
1493
|
...props
|
|
1314
1494
|
}) {
|
|
1315
|
-
return /* @__PURE__ */ (0,
|
|
1495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1316
1496
|
import_cmdk.Command.List,
|
|
1317
1497
|
{
|
|
1318
1498
|
"data-slot": "command-list",
|
|
@@ -1327,7 +1507,7 @@ function CommandList({
|
|
|
1327
1507
|
function CommandEmpty({
|
|
1328
1508
|
...props
|
|
1329
1509
|
}) {
|
|
1330
|
-
return /* @__PURE__ */ (0,
|
|
1510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1331
1511
|
import_cmdk.Command.Empty,
|
|
1332
1512
|
{
|
|
1333
1513
|
"data-slot": "command-empty",
|
|
@@ -1340,7 +1520,7 @@ function CommandGroup({
|
|
|
1340
1520
|
className,
|
|
1341
1521
|
...props
|
|
1342
1522
|
}) {
|
|
1343
|
-
return /* @__PURE__ */ (0,
|
|
1523
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1344
1524
|
import_cmdk.Command.Group,
|
|
1345
1525
|
{
|
|
1346
1526
|
"data-slot": "command-group",
|
|
@@ -1356,7 +1536,7 @@ function CommandItem({
|
|
|
1356
1536
|
className,
|
|
1357
1537
|
...props
|
|
1358
1538
|
}) {
|
|
1359
|
-
return /* @__PURE__ */ (0,
|
|
1539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1360
1540
|
import_cmdk.Command.Item,
|
|
1361
1541
|
{
|
|
1362
1542
|
"data-slot": "command-item",
|
|
@@ -1370,7 +1550,7 @@ function CommandItem({
|
|
|
1370
1550
|
}
|
|
1371
1551
|
|
|
1372
1552
|
// src/components/ScenarioExplorer.tsx
|
|
1373
|
-
var
|
|
1553
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1374
1554
|
var STATUS_FILTERS = [
|
|
1375
1555
|
{ value: "all", label: "All" },
|
|
1376
1556
|
{ value: "passed", label: "Passed" },
|
|
@@ -1379,24 +1559,24 @@ var STATUS_FILTERS = [
|
|
|
1379
1559
|
{ value: "pending", label: "Pending" }
|
|
1380
1560
|
];
|
|
1381
1561
|
function ScenarioExplorer({ scenarios, summary }) {
|
|
1382
|
-
const [status, setStatus] = (0,
|
|
1562
|
+
const [status, setStatus] = (0, import_react9.useState)("all");
|
|
1383
1563
|
const visible = status === "all" ? scenarios : scenarios.filter((s) => s.status === status);
|
|
1384
|
-
return /* @__PURE__ */ (0,
|
|
1385
|
-
summary ? /* @__PURE__ */ (0,
|
|
1564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "font-sans text-foreground", children: [
|
|
1565
|
+
summary ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { className: "mb-3 text-sm text-muted-foreground", children: [
|
|
1386
1566
|
summary.total,
|
|
1387
1567
|
" scenarios \xB7 ",
|
|
1388
|
-
/* @__PURE__ */ (0,
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-pass", children: [
|
|
1389
1569
|
summary.passed,
|
|
1390
1570
|
" passed"
|
|
1391
1571
|
] }),
|
|
1392
1572
|
" \xB7",
|
|
1393
1573
|
" ",
|
|
1394
|
-
/* @__PURE__ */ (0,
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-fail", children: [
|
|
1395
1575
|
summary.failed,
|
|
1396
1576
|
" failed"
|
|
1397
1577
|
] })
|
|
1398
1578
|
] }) : null,
|
|
1399
|
-
/* @__PURE__ */ (0,
|
|
1579
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "mb-2 flex flex-wrap gap-1.5", role: "group", "aria-label": "Filter by status", children: STATUS_FILTERS.map((f) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1400
1580
|
"button",
|
|
1401
1581
|
{
|
|
1402
1582
|
type: "button",
|
|
@@ -1411,26 +1591,26 @@ function ScenarioExplorer({ scenarios, summary }) {
|
|
|
1411
1591
|
},
|
|
1412
1592
|
f.value
|
|
1413
1593
|
)) }),
|
|
1414
|
-
/* @__PURE__ */ (0,
|
|
1594
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
1415
1595
|
Command,
|
|
1416
1596
|
{
|
|
1417
1597
|
className: "rounded-lg border border-border bg-card",
|
|
1418
1598
|
filter: (value, search) => value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0,
|
|
1419
1599
|
children: [
|
|
1420
|
-
/* @__PURE__ */ (0,
|
|
1421
|
-
/* @__PURE__ */ (0,
|
|
1422
|
-
/* @__PURE__ */ (0,
|
|
1423
|
-
/* @__PURE__ */ (0,
|
|
1600
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommandInput, { placeholder: "Search scenarios\u2026" }),
|
|
1601
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(CommandList, { className: "max-h-none", children: [
|
|
1602
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommandEmpty, { children: "No scenarios match your filters." }),
|
|
1603
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommandGroup, { children: visible.map((s) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1424
1604
|
CommandItem,
|
|
1425
1605
|
{
|
|
1426
1606
|
value: `${s.title} ${s.feature} ${s.tags.join(" ")}`,
|
|
1427
1607
|
asChild: true,
|
|
1428
1608
|
className: "cursor-pointer",
|
|
1429
|
-
children: /* @__PURE__ */ (0,
|
|
1430
|
-
/* @__PURE__ */ (0,
|
|
1431
|
-
/* @__PURE__ */ (0,
|
|
1432
|
-
s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0,
|
|
1433
|
-
/* @__PURE__ */ (0,
|
|
1609
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("a", { href: s.href, className: "flex items-center gap-3", children: [
|
|
1610
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Badge, { variant: s.status, children: s.status }),
|
|
1611
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "min-w-0 flex-1 truncate font-medium", children: s.title }),
|
|
1612
|
+
s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Badge, { variant: "tag", className: "hidden sm:inline-flex", children: t }, t)),
|
|
1613
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { className: "hidden font-mono text-xs text-muted-foreground md:inline", children: s.feature })
|
|
1434
1614
|
] })
|
|
1435
1615
|
},
|
|
1436
1616
|
s.id
|
|
@@ -1620,6 +1800,10 @@ var story_report_v1_default = {
|
|
|
1620
1800
|
status: {
|
|
1621
1801
|
$ref: "#/$defs/TestStatus"
|
|
1622
1802
|
},
|
|
1803
|
+
planned: {
|
|
1804
|
+
type: "boolean",
|
|
1805
|
+
description: "True for scenarios authored with the framework's todo modifier (e.g. it.todo): specified behavior with no implementation yet. Canonical status stays 'pending'; this flags why."
|
|
1806
|
+
},
|
|
1623
1807
|
durationMs: {
|
|
1624
1808
|
type: "number",
|
|
1625
1809
|
minimum: 0
|
|
@@ -2452,9 +2636,18 @@ function parseStoryReport(input) {
|
|
|
2452
2636
|
ReportTrace,
|
|
2453
2637
|
STORY_REPORT_SCHEMA_MAJOR,
|
|
2454
2638
|
ScenarioExplorer,
|
|
2639
|
+
ScenarioRunHistory,
|
|
2640
|
+
ciDisplayName,
|
|
2641
|
+
commitUrl,
|
|
2642
|
+
currentStreak,
|
|
2643
|
+
describeRunHistory,
|
|
2455
2644
|
err,
|
|
2645
|
+
formatRelativeAge,
|
|
2646
|
+
isReportStale,
|
|
2456
2647
|
ok,
|
|
2457
2648
|
parseStoryReport,
|
|
2649
|
+
prUrl,
|
|
2650
|
+
reportLastRunMs,
|
|
2458
2651
|
storyReportSchema,
|
|
2459
2652
|
useBuiltinRenderers,
|
|
2460
2653
|
useCustomRenderers,
|