executable-stories-react 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +369 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +307 -9
- package/dist/index.d.ts +307 -9
- package/dist/index.js +353 -145
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +557 -239
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +269 -2
- package/dist/interactive.d.ts +269 -2
- package/dist/interactive.js +518 -201
- package/dist/interactive.js.map +1 -1
- package/dist/parse.cjs +4 -0
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +225 -3
- package/dist/parse.d.ts +225 -3
- package/dist/parse.js +4 -0
- package/dist/parse.js.map +1 -1
- package/dist/report-island.global.js +32 -32
- package/dist/ssr.cjs +299 -118
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.d.cts +250 -2
- package/dist/ssr.d.ts +250 -2
- package/dist/ssr.js +296 -115
- package/dist/ssr.js.map +1 -1
- package/dist/styles.css +26 -0
- package/dist/tailwind.css +1 -1
- package/package.json +4 -4
package/dist/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");
|
|
@@ -282,6 +303,15 @@ function safeUrl(value) {
|
|
|
282
303
|
if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
|
|
283
304
|
return trimmed;
|
|
284
305
|
}
|
|
306
|
+
function safeImageUrl(value) {
|
|
307
|
+
if (!value) return void 0;
|
|
308
|
+
const trimmed = value.trim();
|
|
309
|
+
if (/^data:image\//i.test(trimmed)) return trimmed;
|
|
310
|
+
return safeUrl(trimmed);
|
|
311
|
+
}
|
|
312
|
+
function isLocalFsPath(value) {
|
|
313
|
+
return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
|
|
314
|
+
}
|
|
285
315
|
|
|
286
316
|
// src/components/doc/DocSection.tsx
|
|
287
317
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
@@ -337,11 +367,19 @@ function DocMermaid({ entry }) {
|
|
|
337
367
|
// src/components/doc/DocScreenshot.tsx
|
|
338
368
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
339
369
|
function DocScreenshot({ entry }) {
|
|
370
|
+
const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
|
|
371
|
+
if (!src) {
|
|
372
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
373
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
375
|
+
entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
|
|
376
|
+
] });
|
|
377
|
+
}
|
|
340
378
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3", children: [
|
|
341
379
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
342
380
|
"img",
|
|
343
381
|
{
|
|
344
|
-
src
|
|
382
|
+
src,
|
|
345
383
|
alt: entry.alt ?? "",
|
|
346
384
|
loading: "lazy",
|
|
347
385
|
className: "max-w-full rounded-md border border-border"
|
|
@@ -354,6 +392,13 @@ function DocScreenshot({ entry }) {
|
|
|
354
392
|
// src/components/doc/DocVideo.tsx
|
|
355
393
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
356
394
|
function DocVideo({ entry }) {
|
|
395
|
+
if (isLocalFsPath(entry.path)) {
|
|
396
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
397
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
|
|
398
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
399
|
+
entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
|
|
400
|
+
] });
|
|
401
|
+
}
|
|
357
402
|
const src = safeUrl(entry.path);
|
|
358
403
|
const poster = safeUrl(entry.poster);
|
|
359
404
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3", children: [
|
|
@@ -529,7 +574,7 @@ function ReportStepItem({ step }) {
|
|
|
529
574
|
}
|
|
530
575
|
),
|
|
531
576
|
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
532
|
-
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children:
|
|
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
|
|
533
578
|
] }),
|
|
534
579
|
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
535
580
|
"pre",
|
|
@@ -587,9 +632,6 @@ function ReportAttachments({ attachments }) {
|
|
|
587
632
|
);
|
|
588
633
|
}
|
|
589
634
|
|
|
590
|
-
// src/components/ReportTrace.tsx
|
|
591
|
-
var import_duration2 = require("executable-stories-core/utils/duration");
|
|
592
|
-
|
|
593
635
|
// src/lib/trace.ts
|
|
594
636
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
595
637
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -684,7 +726,7 @@ function ReportTrace({ spans }) {
|
|
|
684
726
|
" span",
|
|
685
727
|
rows.length === 1 ? "" : "s",
|
|
686
728
|
", ",
|
|
687
|
-
|
|
729
|
+
formatDuration(totalMs),
|
|
688
730
|
")"
|
|
689
731
|
] }),
|
|
690
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) => {
|
|
@@ -707,33 +749,128 @@ function ReportTrace({ spans }) {
|
|
|
707
749
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
708
750
|
}
|
|
709
751
|
) }),
|
|
710
|
-
/* @__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) })
|
|
711
753
|
] }, `${row.spanId}-${i}`);
|
|
712
754
|
}) })
|
|
713
755
|
] });
|
|
714
756
|
}
|
|
715
757
|
|
|
716
|
-
// src/interactive/
|
|
758
|
+
// src/interactive/scenario-history-context.ts
|
|
717
759
|
var import_react5 = require("react");
|
|
718
|
-
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);
|
|
719
856
|
var CollapseProvider = CollapseContext.Provider;
|
|
720
857
|
function useCollapse() {
|
|
721
|
-
return (0,
|
|
858
|
+
return (0, import_react6.useContext)(CollapseContext);
|
|
722
859
|
}
|
|
723
860
|
|
|
724
861
|
// src/interactive/scenario-actions.tsx
|
|
725
|
-
var
|
|
726
|
-
var ScenarioActionsContext = (0,
|
|
862
|
+
var import_react7 = require("react");
|
|
863
|
+
var ScenarioActionsContext = (0, import_react7.createContext)(null);
|
|
727
864
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
728
865
|
function useScenarioActions() {
|
|
729
|
-
return (0,
|
|
866
|
+
return (0, import_react7.useContext)(ScenarioActionsContext);
|
|
730
867
|
}
|
|
731
868
|
|
|
732
869
|
// src/components/ui/card.tsx
|
|
733
870
|
var React2 = require("react");
|
|
734
|
-
var
|
|
871
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
735
872
|
function Card({ className, ...props }) {
|
|
736
|
-
return /* @__PURE__ */ (0,
|
|
873
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
737
874
|
"div",
|
|
738
875
|
{
|
|
739
876
|
"data-slot": "card",
|
|
@@ -746,7 +883,7 @@ function Card({ className, ...props }) {
|
|
|
746
883
|
);
|
|
747
884
|
}
|
|
748
885
|
function CardHeader({ className, ...props }) {
|
|
749
|
-
return /* @__PURE__ */ (0,
|
|
886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
750
887
|
"div",
|
|
751
888
|
{
|
|
752
889
|
"data-slot": "card-header",
|
|
@@ -759,7 +896,7 @@ function CardHeader({ className, ...props }) {
|
|
|
759
896
|
);
|
|
760
897
|
}
|
|
761
898
|
function CardContent({ className, ...props }) {
|
|
762
|
-
return /* @__PURE__ */ (0,
|
|
899
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
763
900
|
"div",
|
|
764
901
|
{
|
|
765
902
|
"data-slot": "card-content",
|
|
@@ -770,7 +907,7 @@ function CardContent({ className, ...props }) {
|
|
|
770
907
|
}
|
|
771
908
|
|
|
772
909
|
// src/components/ReportScenario.tsx
|
|
773
|
-
var
|
|
910
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
774
911
|
var STATUS_LABEL = {
|
|
775
912
|
passed: "Passed",
|
|
776
913
|
failed: "Failed",
|
|
@@ -798,12 +935,12 @@ var STATUS_BADGE = {
|
|
|
798
935
|
function ReportScenario({ scenario, hideTitle = false }) {
|
|
799
936
|
const titleId = `${scenario.id}-title`;
|
|
800
937
|
const bodyId = `${scenario.id}-body`;
|
|
801
|
-
const label = STATUS_LABEL[scenario.status];
|
|
938
|
+
const label = scenario.planned ? "Planned" : STATUS_LABEL[scenario.status];
|
|
802
939
|
const collapse = useCollapse();
|
|
803
940
|
const collapsible = collapse !== null && !hideTitle;
|
|
804
941
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
805
942
|
const actions = useScenarioActions();
|
|
806
|
-
return /* @__PURE__ */ (0,
|
|
943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
807
944
|
Card,
|
|
808
945
|
{
|
|
809
946
|
id: scenario.id,
|
|
@@ -811,10 +948,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
811
948
|
...hideTitle ? { "aria-label": `${scenario.title} \u2014 ${label}` } : { "aria-labelledby": titleId },
|
|
812
949
|
className: "gap-0 py-0",
|
|
813
950
|
children: [
|
|
814
|
-
/* @__PURE__ */ (0,
|
|
815
|
-
/* @__PURE__ */ (0,
|
|
816
|
-
hideTitle ? null : /* @__PURE__ */ (0,
|
|
817
|
-
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)(
|
|
818
955
|
"button",
|
|
819
956
|
{
|
|
820
957
|
type: "button",
|
|
@@ -823,17 +960,18 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
823
960
|
"aria-controls": bodyId,
|
|
824
961
|
"aria-label": `Toggle ${scenario.title}`,
|
|
825
962
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
826
|
-
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" })
|
|
827
964
|
}
|
|
828
965
|
) : null,
|
|
829
|
-
/* @__PURE__ */ (0,
|
|
830
|
-
/* @__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 })
|
|
831
968
|
] }),
|
|
832
|
-
/* @__PURE__ */ (0,
|
|
833
|
-
|
|
834
|
-
/* @__PURE__ */ (0,
|
|
835
|
-
|
|
836
|
-
|
|
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)(
|
|
837
975
|
"button",
|
|
838
976
|
{
|
|
839
977
|
type: "button",
|
|
@@ -844,7 +982,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
844
982
|
children: "link"
|
|
845
983
|
}
|
|
846
984
|
),
|
|
847
|
-
/* @__PURE__ */ (0,
|
|
985
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
848
986
|
"button",
|
|
849
987
|
{
|
|
850
988
|
type: "button",
|
|
@@ -855,7 +993,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
855
993
|
children: "md"
|
|
856
994
|
}
|
|
857
995
|
),
|
|
858
|
-
/* @__PURE__ */ (0,
|
|
996
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
859
997
|
"button",
|
|
860
998
|
{
|
|
861
999
|
type: "button",
|
|
@@ -869,13 +1007,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
869
1007
|
] }) : null
|
|
870
1008
|
] })
|
|
871
1009
|
] }),
|
|
872
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0,
|
|
873
|
-
scenario.tags.map((t) => /* @__PURE__ */ (0,
|
|
874
|
-
(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}`))
|
|
875
1013
|
] }) : null
|
|
876
1014
|
] }),
|
|
877
|
-
/* @__PURE__ */ (0,
|
|
878
|
-
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)(
|
|
879
1017
|
"pre",
|
|
880
1018
|
{
|
|
881
1019
|
role: "alert",
|
|
@@ -883,10 +1021,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
883
1021
|
children: scenario.errorMessage
|
|
884
1022
|
}
|
|
885
1023
|
) : null,
|
|
886
|
-
/* @__PURE__ */ (0,
|
|
887
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
888
|
-
/* @__PURE__ */ (0,
|
|
889
|
-
/* @__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 })
|
|
890
1028
|
] })
|
|
891
1029
|
]
|
|
892
1030
|
}
|
|
@@ -894,13 +1032,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
894
1032
|
}
|
|
895
1033
|
|
|
896
1034
|
// src/components/ReportScenarioList.tsx
|
|
897
|
-
var
|
|
1035
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
898
1036
|
function ReportScenarioList({ feature }) {
|
|
899
|
-
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)) });
|
|
900
1038
|
}
|
|
901
1039
|
|
|
902
1040
|
// src/components/ReportFeature.tsx
|
|
903
|
-
var
|
|
1041
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
904
1042
|
function ReportFeature({ feature }) {
|
|
905
1043
|
const titleId = `${feature.id}-title`;
|
|
906
1044
|
const bodyId = `${feature.id}-body`;
|
|
@@ -908,7 +1046,7 @@ function ReportFeature({ feature }) {
|
|
|
908
1046
|
const skipped = s.skipped + s.pending;
|
|
909
1047
|
const collapse = useCollapse();
|
|
910
1048
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
911
|
-
return /* @__PURE__ */ (0,
|
|
1049
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
912
1050
|
"section",
|
|
913
1051
|
{
|
|
914
1052
|
id: feature.id,
|
|
@@ -916,9 +1054,9 @@ function ReportFeature({ feature }) {
|
|
|
916
1054
|
"aria-labelledby": titleId,
|
|
917
1055
|
className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm",
|
|
918
1056
|
children: [
|
|
919
|
-
/* @__PURE__ */ (0,
|
|
920
|
-
/* @__PURE__ */ (0,
|
|
921
|
-
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)(
|
|
922
1060
|
"button",
|
|
923
1061
|
{
|
|
924
1062
|
type: "button",
|
|
@@ -927,30 +1065,30 @@ function ReportFeature({ feature }) {
|
|
|
927
1065
|
"aria-controls": bodyId,
|
|
928
1066
|
"aria-label": `Toggle ${feature.title}`,
|
|
929
1067
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
930
|
-
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" })
|
|
931
1069
|
}
|
|
932
1070
|
) : null,
|
|
933
|
-
/* @__PURE__ */ (0,
|
|
934
|
-
/* @__PURE__ */ (0,
|
|
935
|
-
/* @__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 })
|
|
936
1074
|
] })
|
|
937
1075
|
] }),
|
|
938
|
-
/* @__PURE__ */ (0,
|
|
1076
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
939
1077
|
"div",
|
|
940
1078
|
{
|
|
941
1079
|
role: "img",
|
|
942
1080
|
className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
|
|
943
1081
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
944
1082
|
children: [
|
|
945
|
-
/* @__PURE__ */ (0,
|
|
1083
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-pass", children: [
|
|
946
1084
|
"\u2713",
|
|
947
1085
|
s.passed
|
|
948
1086
|
] }),
|
|
949
|
-
/* @__PURE__ */ (0,
|
|
1087
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-fail", children: [
|
|
950
1088
|
"\u2717",
|
|
951
1089
|
s.failed
|
|
952
1090
|
] }),
|
|
953
|
-
/* @__PURE__ */ (0,
|
|
1091
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-skip", children: [
|
|
954
1092
|
"\u25CB",
|
|
955
1093
|
skipped
|
|
956
1094
|
] })
|
|
@@ -958,24 +1096,24 @@ function ReportFeature({ feature }) {
|
|
|
958
1096
|
}
|
|
959
1097
|
)
|
|
960
1098
|
] }),
|
|
961
|
-
/* @__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 }) })
|
|
962
1100
|
]
|
|
963
1101
|
}
|
|
964
1102
|
);
|
|
965
1103
|
}
|
|
966
1104
|
|
|
967
1105
|
// src/components/ReportFeatureList.tsx
|
|
968
|
-
var
|
|
1106
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
969
1107
|
function ReportFeatureList() {
|
|
970
1108
|
const report = useReport();
|
|
971
|
-
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)) });
|
|
972
1110
|
}
|
|
973
1111
|
|
|
974
1112
|
// src/components/ui/empty.tsx
|
|
975
1113
|
var import_class_variance_authority2 = require("class-variance-authority");
|
|
976
|
-
var
|
|
1114
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
977
1115
|
function Empty({ className, ...props }) {
|
|
978
|
-
return /* @__PURE__ */ (0,
|
|
1116
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
979
1117
|
"div",
|
|
980
1118
|
{
|
|
981
1119
|
"data-slot": "empty",
|
|
@@ -988,7 +1126,7 @@ function Empty({ className, ...props }) {
|
|
|
988
1126
|
);
|
|
989
1127
|
}
|
|
990
1128
|
function EmptyHeader({ className, ...props }) {
|
|
991
|
-
return /* @__PURE__ */ (0,
|
|
1129
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
992
1130
|
"div",
|
|
993
1131
|
{
|
|
994
1132
|
"data-slot": "empty-header",
|
|
@@ -1015,7 +1153,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
|
1015
1153
|
}
|
|
1016
1154
|
);
|
|
1017
1155
|
function EmptyTitle({ className, ...props }) {
|
|
1018
|
-
return /* @__PURE__ */ (0,
|
|
1156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1019
1157
|
"div",
|
|
1020
1158
|
{
|
|
1021
1159
|
"data-slot": "empty-title",
|
|
@@ -1026,16 +1164,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1026
1164
|
}
|
|
1027
1165
|
|
|
1028
1166
|
// src/components/ReportEmpty.tsx
|
|
1029
|
-
var
|
|
1167
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1030
1168
|
function ReportEmpty({ message }) {
|
|
1031
|
-
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." }) }) });
|
|
1032
1170
|
}
|
|
1033
1171
|
|
|
1034
1172
|
// src/components/ReportSummary.tsx
|
|
1035
|
-
var
|
|
1173
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1036
1174
|
function ReportSummary({ className }) {
|
|
1037
1175
|
const report = useReport();
|
|
1038
|
-
return /* @__PURE__ */ (0,
|
|
1176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1039
1177
|
ReportSummaryView,
|
|
1040
1178
|
{
|
|
1041
1179
|
summary: report.summary,
|
|
@@ -1045,32 +1183,32 @@ function ReportSummary({ className }) {
|
|
|
1045
1183
|
);
|
|
1046
1184
|
}
|
|
1047
1185
|
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
1048
|
-
return /* @__PURE__ */ (0,
|
|
1049
|
-
/* @__PURE__ */ (0,
|
|
1050
|
-
/* @__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 }),
|
|
1051
1189
|
" scenario",
|
|
1052
1190
|
summary.total === 1 ? "" : "s"
|
|
1053
1191
|
] }),
|
|
1054
1192
|
" \xB7 ",
|
|
1055
|
-
/* @__PURE__ */ (0,
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "passed", className: "text-pass", children: [
|
|
1056
1194
|
summary.passed,
|
|
1057
1195
|
" passed"
|
|
1058
1196
|
] }),
|
|
1059
1197
|
" \xB7 ",
|
|
1060
|
-
/* @__PURE__ */ (0,
|
|
1198
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "failed", className: "text-fail", children: [
|
|
1061
1199
|
summary.failed,
|
|
1062
1200
|
" failed"
|
|
1063
1201
|
] }),
|
|
1064
|
-
summary.skipped > 0 ? /* @__PURE__ */ (0,
|
|
1202
|
+
summary.skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
1065
1203
|
" \xB7 ",
|
|
1066
|
-
/* @__PURE__ */ (0,
|
|
1204
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "skipped", className: "text-skip", children: [
|
|
1067
1205
|
summary.skipped,
|
|
1068
1206
|
" skipped"
|
|
1069
1207
|
] })
|
|
1070
1208
|
] }) : null,
|
|
1071
|
-
summary.pending > 0 ? /* @__PURE__ */ (0,
|
|
1209
|
+
summary.pending > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
|
|
1072
1210
|
" \xB7 ",
|
|
1073
|
-
/* @__PURE__ */ (0,
|
|
1211
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "pending", className: "text-pend", children: [
|
|
1074
1212
|
summary.pending,
|
|
1075
1213
|
" pending"
|
|
1076
1214
|
] })
|
|
@@ -1078,69 +1216,135 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1078
1216
|
] });
|
|
1079
1217
|
}
|
|
1080
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
|
+
|
|
1081
1268
|
// src/components/ReportMeta.tsx
|
|
1082
|
-
var
|
|
1083
|
-
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1269
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1084
1270
|
function ReportMeta() {
|
|
1085
1271
|
const report = useReport();
|
|
1086
1272
|
const items = [];
|
|
1273
|
+
const linkClass = "text-primary underline underline-offset-2";
|
|
1087
1274
|
if (report.startedAtMs) {
|
|
1088
1275
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1089
1276
|
}
|
|
1090
1277
|
if (report.durationMs) {
|
|
1091
|
-
items.push({ label: "Duration", value:
|
|
1278
|
+
items.push({ label: "Duration", value: formatDuration(report.durationMs) });
|
|
1092
1279
|
}
|
|
1093
1280
|
if (report.packageVersion) {
|
|
1094
1281
|
items.push({ label: "Version", value: report.packageVersion });
|
|
1095
1282
|
}
|
|
1096
|
-
if (report.
|
|
1097
|
-
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
|
+
});
|
|
1098
1294
|
}
|
|
1099
1295
|
if (report.ci) {
|
|
1100
1296
|
items.push({
|
|
1101
1297
|
label: "CI",
|
|
1102
|
-
value: report.ci.url ? /* @__PURE__ */ (0,
|
|
1103
|
-
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),
|
|
1104
1300
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1105
|
-
] }) : `${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
|
|
1106
1310
|
});
|
|
1107
1311
|
}
|
|
1108
1312
|
if (items.length === 0) return null;
|
|
1109
|
-
return /* @__PURE__ */ (0,
|
|
1110
|
-
/* @__PURE__ */ (0,
|
|
1111
|
-
/* @__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 })
|
|
1112
1316
|
] }, item.label)) });
|
|
1113
1317
|
}
|
|
1114
1318
|
|
|
1115
1319
|
// src/components/ReportSchemaError.tsx
|
|
1116
|
-
var
|
|
1320
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1117
1321
|
function ReportSchemaError({ error }) {
|
|
1118
|
-
return /* @__PURE__ */ (0,
|
|
1119
|
-
/* @__PURE__ */ (0,
|
|
1120
|
-
/* @__PURE__ */ (0,
|
|
1121
|
-
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: [
|
|
1122
1326
|
"The report bundle is newer than this version of ",
|
|
1123
|
-
/* @__PURE__ */ (0,
|
|
1327
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("code", { children: "executable-stories-react" }),
|
|
1124
1328
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1125
1329
|
] }) : null,
|
|
1126
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0,
|
|
1127
|
-
/* @__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: [
|
|
1128
1332
|
error.issues.length,
|
|
1129
1333
|
" validation issue",
|
|
1130
1334
|
error.issues.length === 1 ? "" : "s"
|
|
1131
1335
|
] }),
|
|
1132
|
-
/* @__PURE__ */ (0,
|
|
1336
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1133
1337
|
] }) : null
|
|
1134
1338
|
] });
|
|
1135
1339
|
}
|
|
1136
1340
|
|
|
1137
1341
|
// src/components/ReportShell.tsx
|
|
1138
|
-
var
|
|
1342
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1139
1343
|
function ReportTitleBlock({ title }) {
|
|
1140
|
-
return /* @__PURE__ */ (0,
|
|
1141
|
-
/* @__PURE__ */ (0,
|
|
1142
|
-
/* @__PURE__ */ (0,
|
|
1143
|
-
/* @__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, {})
|
|
1144
1348
|
] });
|
|
1145
1349
|
}
|
|
1146
1350
|
function ReportErrorShell({
|
|
@@ -1149,28 +1353,28 @@ function ReportErrorShell({
|
|
|
1149
1353
|
title,
|
|
1150
1354
|
dataTheme
|
|
1151
1355
|
}) {
|
|
1152
|
-
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 }) });
|
|
1153
1357
|
}
|
|
1154
1358
|
|
|
1155
1359
|
// src/components/Report.tsx
|
|
1156
|
-
var
|
|
1360
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1157
1361
|
function Report(props) {
|
|
1158
1362
|
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1159
1363
|
const result = unwrapReport(report);
|
|
1160
1364
|
if (!result.ok) {
|
|
1161
|
-
return /* @__PURE__ */ (0,
|
|
1365
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1162
1366
|
}
|
|
1163
1367
|
const data = result.data;
|
|
1164
1368
|
const hasContent = data.features.length > 0;
|
|
1165
|
-
return /* @__PURE__ */ (0,
|
|
1166
|
-
/* @__PURE__ */ (0,
|
|
1167
|
-
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, {})
|
|
1168
1372
|
] }) });
|
|
1169
1373
|
}
|
|
1170
1374
|
|
|
1171
1375
|
// src/components/doc/MermaidView.tsx
|
|
1172
|
-
var
|
|
1173
|
-
var
|
|
1376
|
+
var import_react8 = require("react");
|
|
1377
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1174
1378
|
function prefersDark(el) {
|
|
1175
1379
|
const themed = el?.closest("[data-theme]");
|
|
1176
1380
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -1178,9 +1382,9 @@ function prefersDark(el) {
|
|
|
1178
1382
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
1179
1383
|
}
|
|
1180
1384
|
function MermaidView({ entry, load }) {
|
|
1181
|
-
const ref = (0,
|
|
1182
|
-
const [svg, setSvg] = (0,
|
|
1183
|
-
(0,
|
|
1385
|
+
const ref = (0, import_react8.useRef)(null);
|
|
1386
|
+
const [svg, setSvg] = (0, import_react8.useState)(null);
|
|
1387
|
+
(0, import_react8.useEffect)(() => {
|
|
1184
1388
|
let cancelled = false;
|
|
1185
1389
|
const draw = async () => {
|
|
1186
1390
|
try {
|
|
@@ -1207,9 +1411,9 @@ function MermaidView({ entry, load }) {
|
|
|
1207
1411
|
};
|
|
1208
1412
|
}, [entry.code, load]);
|
|
1209
1413
|
if (svg) {
|
|
1210
|
-
return /* @__PURE__ */ (0,
|
|
1211
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
1212
|
-
/* @__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)(
|
|
1213
1417
|
"div",
|
|
1214
1418
|
{
|
|
1215
1419
|
role: "img",
|
|
@@ -1220,33 +1424,33 @@ function MermaidView({ entry, load }) {
|
|
|
1220
1424
|
)
|
|
1221
1425
|
] });
|
|
1222
1426
|
}
|
|
1223
|
-
return /* @__PURE__ */ (0,
|
|
1427
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MermaidSource, { entry, ref });
|
|
1224
1428
|
}
|
|
1225
1429
|
|
|
1226
1430
|
// src/components/doc/MermaidDiagram.tsx
|
|
1227
|
-
var
|
|
1431
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1228
1432
|
var mermaidLoader;
|
|
1229
1433
|
function loadMermaid() {
|
|
1230
1434
|
mermaidLoader ??= import("mermaid").then((m) => m.default);
|
|
1231
1435
|
return mermaidLoader;
|
|
1232
1436
|
}
|
|
1233
1437
|
function MermaidDiagram({ entry, load = loadMermaid }) {
|
|
1234
|
-
return /* @__PURE__ */ (0,
|
|
1438
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(MermaidView, { entry, load });
|
|
1235
1439
|
}
|
|
1236
1440
|
|
|
1237
1441
|
// src/components/ScenarioExplorer.tsx
|
|
1238
|
-
var
|
|
1442
|
+
var import_react9 = require("react");
|
|
1239
1443
|
|
|
1240
1444
|
// src/components/ui/command.tsx
|
|
1241
1445
|
var React3 = require("react");
|
|
1242
1446
|
var import_cmdk = require("cmdk");
|
|
1243
1447
|
var import_lucide_react = require("lucide-react");
|
|
1244
|
-
var
|
|
1448
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1245
1449
|
function Command({
|
|
1246
1450
|
className,
|
|
1247
1451
|
...props
|
|
1248
1452
|
}) {
|
|
1249
|
-
return /* @__PURE__ */ (0,
|
|
1453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1250
1454
|
import_cmdk.Command,
|
|
1251
1455
|
{
|
|
1252
1456
|
"data-slot": "command",
|
|
@@ -1262,14 +1466,14 @@ function CommandInput({
|
|
|
1262
1466
|
className,
|
|
1263
1467
|
...props
|
|
1264
1468
|
}) {
|
|
1265
|
-
return /* @__PURE__ */ (0,
|
|
1469
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
1266
1470
|
"div",
|
|
1267
1471
|
{
|
|
1268
1472
|
"data-slot": "command-input-wrapper",
|
|
1269
1473
|
className: "flex h-9 items-center gap-2 border-b px-3",
|
|
1270
1474
|
children: [
|
|
1271
|
-
/* @__PURE__ */ (0,
|
|
1272
|
-
/* @__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)(
|
|
1273
1477
|
import_cmdk.Command.Input,
|
|
1274
1478
|
{
|
|
1275
1479
|
"data-slot": "command-input",
|
|
@@ -1288,7 +1492,7 @@ function CommandList({
|
|
|
1288
1492
|
className,
|
|
1289
1493
|
...props
|
|
1290
1494
|
}) {
|
|
1291
|
-
return /* @__PURE__ */ (0,
|
|
1495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1292
1496
|
import_cmdk.Command.List,
|
|
1293
1497
|
{
|
|
1294
1498
|
"data-slot": "command-list",
|
|
@@ -1303,7 +1507,7 @@ function CommandList({
|
|
|
1303
1507
|
function CommandEmpty({
|
|
1304
1508
|
...props
|
|
1305
1509
|
}) {
|
|
1306
|
-
return /* @__PURE__ */ (0,
|
|
1510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1307
1511
|
import_cmdk.Command.Empty,
|
|
1308
1512
|
{
|
|
1309
1513
|
"data-slot": "command-empty",
|
|
@@ -1316,7 +1520,7 @@ function CommandGroup({
|
|
|
1316
1520
|
className,
|
|
1317
1521
|
...props
|
|
1318
1522
|
}) {
|
|
1319
|
-
return /* @__PURE__ */ (0,
|
|
1523
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1320
1524
|
import_cmdk.Command.Group,
|
|
1321
1525
|
{
|
|
1322
1526
|
"data-slot": "command-group",
|
|
@@ -1332,7 +1536,7 @@ function CommandItem({
|
|
|
1332
1536
|
className,
|
|
1333
1537
|
...props
|
|
1334
1538
|
}) {
|
|
1335
|
-
return /* @__PURE__ */ (0,
|
|
1539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1336
1540
|
import_cmdk.Command.Item,
|
|
1337
1541
|
{
|
|
1338
1542
|
"data-slot": "command-item",
|
|
@@ -1346,7 +1550,7 @@ function CommandItem({
|
|
|
1346
1550
|
}
|
|
1347
1551
|
|
|
1348
1552
|
// src/components/ScenarioExplorer.tsx
|
|
1349
|
-
var
|
|
1553
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1350
1554
|
var STATUS_FILTERS = [
|
|
1351
1555
|
{ value: "all", label: "All" },
|
|
1352
1556
|
{ value: "passed", label: "Passed" },
|
|
@@ -1355,24 +1559,24 @@ var STATUS_FILTERS = [
|
|
|
1355
1559
|
{ value: "pending", label: "Pending" }
|
|
1356
1560
|
];
|
|
1357
1561
|
function ScenarioExplorer({ scenarios, summary }) {
|
|
1358
|
-
const [status, setStatus] = (0,
|
|
1562
|
+
const [status, setStatus] = (0, import_react9.useState)("all");
|
|
1359
1563
|
const visible = status === "all" ? scenarios : scenarios.filter((s) => s.status === status);
|
|
1360
|
-
return /* @__PURE__ */ (0,
|
|
1361
|
-
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: [
|
|
1362
1566
|
summary.total,
|
|
1363
1567
|
" scenarios \xB7 ",
|
|
1364
|
-
/* @__PURE__ */ (0,
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-pass", children: [
|
|
1365
1569
|
summary.passed,
|
|
1366
1570
|
" passed"
|
|
1367
1571
|
] }),
|
|
1368
1572
|
" \xB7",
|
|
1369
1573
|
" ",
|
|
1370
|
-
/* @__PURE__ */ (0,
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-fail", children: [
|
|
1371
1575
|
summary.failed,
|
|
1372
1576
|
" failed"
|
|
1373
1577
|
] })
|
|
1374
1578
|
] }) : null,
|
|
1375
|
-
/* @__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)(
|
|
1376
1580
|
"button",
|
|
1377
1581
|
{
|
|
1378
1582
|
type: "button",
|
|
@@ -1387,26 +1591,26 @@ function ScenarioExplorer({ scenarios, summary }) {
|
|
|
1387
1591
|
},
|
|
1388
1592
|
f.value
|
|
1389
1593
|
)) }),
|
|
1390
|
-
/* @__PURE__ */ (0,
|
|
1594
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
1391
1595
|
Command,
|
|
1392
1596
|
{
|
|
1393
1597
|
className: "rounded-lg border border-border bg-card",
|
|
1394
1598
|
filter: (value, search) => value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0,
|
|
1395
1599
|
children: [
|
|
1396
|
-
/* @__PURE__ */ (0,
|
|
1397
|
-
/* @__PURE__ */ (0,
|
|
1398
|
-
/* @__PURE__ */ (0,
|
|
1399
|
-
/* @__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)(
|
|
1400
1604
|
CommandItem,
|
|
1401
1605
|
{
|
|
1402
1606
|
value: `${s.title} ${s.feature} ${s.tags.join(" ")}`,
|
|
1403
1607
|
asChild: true,
|
|
1404
1608
|
className: "cursor-pointer",
|
|
1405
|
-
children: /* @__PURE__ */ (0,
|
|
1406
|
-
/* @__PURE__ */ (0,
|
|
1407
|
-
/* @__PURE__ */ (0,
|
|
1408
|
-
s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0,
|
|
1409
|
-
/* @__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 })
|
|
1410
1614
|
] })
|
|
1411
1615
|
},
|
|
1412
1616
|
s.id
|
|
@@ -1596,6 +1800,10 @@ var story_report_v1_default = {
|
|
|
1596
1800
|
status: {
|
|
1597
1801
|
$ref: "#/$defs/TestStatus"
|
|
1598
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
|
+
},
|
|
1599
1807
|
durationMs: {
|
|
1600
1808
|
type: "number",
|
|
1601
1809
|
minimum: 0
|
|
@@ -2428,9 +2636,18 @@ function parseStoryReport(input) {
|
|
|
2428
2636
|
ReportTrace,
|
|
2429
2637
|
STORY_REPORT_SCHEMA_MAJOR,
|
|
2430
2638
|
ScenarioExplorer,
|
|
2639
|
+
ScenarioRunHistory,
|
|
2640
|
+
ciDisplayName,
|
|
2641
|
+
commitUrl,
|
|
2642
|
+
currentStreak,
|
|
2643
|
+
describeRunHistory,
|
|
2431
2644
|
err,
|
|
2645
|
+
formatRelativeAge,
|
|
2646
|
+
isReportStale,
|
|
2432
2647
|
ok,
|
|
2433
2648
|
parseStoryReport,
|
|
2649
|
+
prUrl,
|
|
2650
|
+
reportLastRunMs,
|
|
2434
2651
|
storyReportSchema,
|
|
2435
2652
|
useBuiltinRenderers,
|
|
2436
2653
|
useCustomRenderers,
|