executable-stories-react 0.9.0 → 0.10.1
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 +540 -483
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -235
- package/dist/index.d.ts +50 -235
- package/dist/index.js +525 -475
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +615 -689
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +1 -233
- package/dist/interactive.d.ts +1 -233
- package/dist/interactive.js +607 -681
- package/dist/interactive.js.map +1 -1
- package/dist/parse.d.cts +3 -234
- package/dist/parse.d.ts +3 -234
- package/dist/report-island.global.js +28 -28
- package/dist/ssr.cjs +492 -449
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.d.cts +1 -232
- package/dist/ssr.d.ts +1 -232
- package/dist/ssr.js +484 -441
- package/dist/ssr.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +4 -4
package/dist/ssr.js
CHANGED
|
@@ -18,63 +18,6 @@ import { useMemo } from "react";
|
|
|
18
18
|
import { createContext } from "react";
|
|
19
19
|
var ReportContext = createContext(null);
|
|
20
20
|
|
|
21
|
-
// src/context/ReportRoot.tsx
|
|
22
|
-
import { jsx } from "react/jsx-runtime";
|
|
23
|
-
var EMPTY_CUSTOM = {};
|
|
24
|
-
var EMPTY_RENDERERS = {};
|
|
25
|
-
function ReportRoot({
|
|
26
|
-
report,
|
|
27
|
-
customRenderers,
|
|
28
|
-
renderers,
|
|
29
|
-
children
|
|
30
|
-
}) {
|
|
31
|
-
const value = useMemo(
|
|
32
|
-
() => ({
|
|
33
|
-
report,
|
|
34
|
-
customRenderers: customRenderers ?? EMPTY_CUSTOM,
|
|
35
|
-
renderers: renderers ?? EMPTY_RENDERERS
|
|
36
|
-
}),
|
|
37
|
-
[report, customRenderers, renderers]
|
|
38
|
-
);
|
|
39
|
-
return /* @__PURE__ */ jsx(ReportContext.Provider, { value, children });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// src/hooks/useReport.ts
|
|
43
|
-
import { useContext } from "react";
|
|
44
|
-
function useReport() {
|
|
45
|
-
const ctx = useContext(ReportContext);
|
|
46
|
-
if (!ctx) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
"useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
return ctx.report;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ../executable-stories-core/dist/utils/duration.js
|
|
55
|
-
function formatDuration(ms) {
|
|
56
|
-
if (ms < 1e3) {
|
|
57
|
-
return `${Math.round(ms)} ms`;
|
|
58
|
-
}
|
|
59
|
-
if (ms < 6e4) {
|
|
60
|
-
return `${(ms / 1e3).toFixed(2)} s`;
|
|
61
|
-
}
|
|
62
|
-
const minutes = Math.floor(ms / 6e4);
|
|
63
|
-
const seconds = Math.floor(ms % 6e4 / 1e3);
|
|
64
|
-
if (minutes < 60) {
|
|
65
|
-
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
66
|
-
}
|
|
67
|
-
const hours = Math.floor(minutes / 60);
|
|
68
|
-
const remainingMinutes = minutes % 60;
|
|
69
|
-
return `${hours}h ${remainingMinutes}m`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// src/components/doc/DocNote.tsx
|
|
73
|
-
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
74
|
-
function DocNote({ entry }) {
|
|
75
|
-
return /* @__PURE__ */ jsx2("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
21
|
// src/components/ui/badge.tsx
|
|
79
22
|
import "react";
|
|
80
23
|
import { cva } from "class-variance-authority";
|
|
@@ -133,14 +76,237 @@ function Badge({
|
|
|
133
76
|
});
|
|
134
77
|
}
|
|
135
78
|
|
|
136
|
-
// src/components/doc/
|
|
79
|
+
// src/components/doc/NarrativeBlocks.tsx
|
|
80
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
81
|
+
var FILE_TREE_TYPE = "file-tree";
|
|
82
|
+
var DATA_MODEL_TYPE = "data-model";
|
|
83
|
+
function isChangeKind(value) {
|
|
84
|
+
return value === "added" || value === "modified" || value === "removed" || value === "renamed";
|
|
85
|
+
}
|
|
86
|
+
function isRecord(value) {
|
|
87
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
88
|
+
}
|
|
89
|
+
function str(value) {
|
|
90
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
91
|
+
}
|
|
92
|
+
function AuthoredMarker({ authored }) {
|
|
93
|
+
if (authored !== "agent") return null;
|
|
94
|
+
return /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "border-border text-[10px] text-muted-foreground", children: "AI-authored, not verified by a run" });
|
|
95
|
+
}
|
|
96
|
+
function BlockFrame({
|
|
97
|
+
title,
|
|
98
|
+
authored,
|
|
99
|
+
children
|
|
100
|
+
}) {
|
|
101
|
+
return /* @__PURE__ */ jsxs("figure", { className: "my-3", children: [
|
|
102
|
+
title || authored === "agent" ? /* @__PURE__ */ jsxs("figcaption", { className: "mb-1.5 flex flex-wrap items-center gap-2", children: [
|
|
103
|
+
title ? /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }) : null,
|
|
104
|
+
/* @__PURE__ */ jsx(AuthoredMarker, { authored })
|
|
105
|
+
] }) : null,
|
|
106
|
+
children
|
|
107
|
+
] });
|
|
108
|
+
}
|
|
109
|
+
function ChangeBadge({ change }) {
|
|
110
|
+
return /* @__PURE__ */ jsx(Badge, { variant: "tag", className: "text-[10px]", children: change });
|
|
111
|
+
}
|
|
112
|
+
function parseFileTree(data) {
|
|
113
|
+
if (!isRecord(data) || !Array.isArray(data.files)) return void 0;
|
|
114
|
+
const files = [];
|
|
115
|
+
for (const raw of data.files) {
|
|
116
|
+
if (typeof raw === "string") {
|
|
117
|
+
if (raw) files.push({ path: raw });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (!isRecord(raw)) continue;
|
|
121
|
+
const path = str(raw.path);
|
|
122
|
+
if (!path) continue;
|
|
123
|
+
files.push({
|
|
124
|
+
path,
|
|
125
|
+
change: isChangeKind(raw.change) ? raw.change : void 0,
|
|
126
|
+
note: str(raw.note)
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (files.length === 0) return void 0;
|
|
130
|
+
return { title: str(data.title), authored: str(data.authored), files };
|
|
131
|
+
}
|
|
132
|
+
function buildTree(files) {
|
|
133
|
+
const root = { name: "", children: /* @__PURE__ */ new Map() };
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
const segments = file.path.split("/").filter(Boolean);
|
|
136
|
+
let node = root;
|
|
137
|
+
segments.forEach((segment, index) => {
|
|
138
|
+
let child = node.children.get(segment);
|
|
139
|
+
if (!child) {
|
|
140
|
+
child = { name: segment, children: /* @__PURE__ */ new Map() };
|
|
141
|
+
node.children.set(segment, child);
|
|
142
|
+
}
|
|
143
|
+
if (index === segments.length - 1) child.file = file;
|
|
144
|
+
node = child;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return root;
|
|
148
|
+
}
|
|
149
|
+
function sortedChildren(node) {
|
|
150
|
+
return [...node.children.values()].sort((a, b) => {
|
|
151
|
+
const aDir = a.children.size > 0;
|
|
152
|
+
const bDir = b.children.size > 0;
|
|
153
|
+
if (aDir !== bDir) return aDir ? -1 : 1;
|
|
154
|
+
return a.name.localeCompare(b.name);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function TreeBranch({ node, depth }) {
|
|
158
|
+
const children = sortedChildren(node);
|
|
159
|
+
return /* @__PURE__ */ jsx("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
|
|
160
|
+
const isDir = child.children.size > 0;
|
|
161
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-2", children: [
|
|
163
|
+
/* @__PURE__ */ jsx(
|
|
164
|
+
"span",
|
|
165
|
+
{
|
|
166
|
+
className: cn(
|
|
167
|
+
"font-mono text-xs",
|
|
168
|
+
isDir ? "text-muted-foreground" : "text-foreground"
|
|
169
|
+
),
|
|
170
|
+
children: isDir ? `${child.name}/` : child.name
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
child.file?.change ? /* @__PURE__ */ jsx(ChangeBadge, { change: child.file.change }) : null,
|
|
174
|
+
child.file?.note ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
|
|
175
|
+
] }),
|
|
176
|
+
isDir ? /* @__PURE__ */ jsx(TreeBranch, { node: child, depth: depth + 1 }) : null
|
|
177
|
+
] }, child.name);
|
|
178
|
+
}) });
|
|
179
|
+
}
|
|
180
|
+
function FileTreeBlock({ entry }) {
|
|
181
|
+
const data = parseFileTree(entry.data);
|
|
182
|
+
if (!data) return /* @__PURE__ */ jsx(UnreadableBlock, { entry });
|
|
183
|
+
return /* @__PURE__ */ jsx(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ jsx("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ jsx(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
|
|
184
|
+
}
|
|
185
|
+
function parseDataModel(data) {
|
|
186
|
+
if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
|
|
187
|
+
const fields = [];
|
|
188
|
+
for (const raw of data.fields) {
|
|
189
|
+
if (!isRecord(raw)) continue;
|
|
190
|
+
const name = str(raw.name);
|
|
191
|
+
if (!name) continue;
|
|
192
|
+
fields.push({
|
|
193
|
+
name,
|
|
194
|
+
type: str(raw.type),
|
|
195
|
+
note: str(raw.note),
|
|
196
|
+
change: isChangeKind(raw.change) ? raw.change : void 0
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (fields.length === 0) return void 0;
|
|
200
|
+
return {
|
|
201
|
+
title: str(data.title),
|
|
202
|
+
authored: str(data.authored),
|
|
203
|
+
name: str(data.name),
|
|
204
|
+
fields
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function DataModelBlock({ entry }) {
|
|
208
|
+
const data = parseDataModel(entry.data);
|
|
209
|
+
if (!data) return /* @__PURE__ */ jsx(UnreadableBlock, { entry });
|
|
210
|
+
const hasNotes = data.fields.some((f) => f.note);
|
|
211
|
+
return /* @__PURE__ */ jsx(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ jsx("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
|
|
212
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "bg-muted/50", children: [
|
|
213
|
+
/* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
|
|
214
|
+
/* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
|
|
215
|
+
hasNotes ? /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
|
|
216
|
+
] }) }),
|
|
217
|
+
/* @__PURE__ */ jsx("tbody", { children: data.fields.map((field) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
218
|
+
/* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [
|
|
219
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-foreground", children: field.name }),
|
|
220
|
+
field.change ? /* @__PURE__ */ jsx(ChangeBadge, { change: field.change }) : null
|
|
221
|
+
] }) }),
|
|
222
|
+
/* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
|
|
223
|
+
hasNotes ? /* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
|
|
224
|
+
] }, field.name)) })
|
|
225
|
+
] }) }) });
|
|
226
|
+
}
|
|
227
|
+
function UnreadableBlock({ entry }) {
|
|
228
|
+
return /* @__PURE__ */ jsxs("div", { className: "my-2", "data-type": entry.type, children: [
|
|
229
|
+
/* @__PURE__ */ jsxs("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
|
|
230
|
+
entry.type,
|
|
231
|
+
" (unrecognised shape)"
|
|
232
|
+
] }),
|
|
233
|
+
/* @__PURE__ */ jsx(
|
|
234
|
+
"pre",
|
|
235
|
+
{
|
|
236
|
+
tabIndex: 0,
|
|
237
|
+
className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
|
|
238
|
+
children: safeStringify(entry.data)
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
] });
|
|
242
|
+
}
|
|
243
|
+
function safeStringify(value) {
|
|
244
|
+
try {
|
|
245
|
+
return JSON.stringify(value, null, 2);
|
|
246
|
+
} catch {
|
|
247
|
+
return String(value);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
var narrativeBlockRenderers = {
|
|
251
|
+
[FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ jsx(FileTreeBlock, { entry }),
|
|
252
|
+
[DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ jsx(DataModelBlock, { entry })
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/context/ReportRoot.tsx
|
|
256
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
257
|
+
var EMPTY_RENDERERS = {};
|
|
258
|
+
function ReportRoot({
|
|
259
|
+
report,
|
|
260
|
+
customRenderers,
|
|
261
|
+
renderers,
|
|
262
|
+
children
|
|
263
|
+
}) {
|
|
264
|
+
const value = useMemo(
|
|
265
|
+
() => ({
|
|
266
|
+
report,
|
|
267
|
+
// Narrative blocks render everywhere the report does (static SSR, the
|
|
268
|
+
// island, Astro) without every host wiring them up. A host renderer for
|
|
269
|
+
// the same type still wins.
|
|
270
|
+
customRenderers: { ...narrativeBlockRenderers, ...customRenderers },
|
|
271
|
+
renderers: renderers ?? EMPTY_RENDERERS
|
|
272
|
+
}),
|
|
273
|
+
[report, customRenderers, renderers]
|
|
274
|
+
);
|
|
275
|
+
return /* @__PURE__ */ jsx2(ReportContext.Provider, { value, children });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/hooks/useReport.ts
|
|
279
|
+
import { useContext } from "react";
|
|
280
|
+
function useReport() {
|
|
281
|
+
const ctx = useContext(ReportContext);
|
|
282
|
+
if (!ctx) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
"useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return ctx.report;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// src/components/ReportScenario.tsx
|
|
291
|
+
import { formatDuration as formatDuration3 } from "executable-stories-core/utils/duration";
|
|
292
|
+
|
|
293
|
+
// src/components/ReportSteps.tsx
|
|
294
|
+
import { formatDuration } from "executable-stories-core/utils/duration";
|
|
295
|
+
|
|
296
|
+
// src/components/doc/DocNote.tsx
|
|
137
297
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
298
|
+
function DocNote({ entry }) {
|
|
299
|
+
return /* @__PURE__ */ jsx3("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/components/doc/DocTag.tsx
|
|
303
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
138
304
|
function DocTag({ entry }) {
|
|
139
|
-
return /* @__PURE__ */
|
|
305
|
+
return /* @__PURE__ */ jsx4("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(Badge, { variant: "tag", children: n }) }, n)) });
|
|
140
306
|
}
|
|
141
307
|
|
|
142
308
|
// src/components/doc/DocKv.tsx
|
|
143
|
-
import { jsx as
|
|
309
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
144
310
|
function formatValue(value) {
|
|
145
311
|
if (value === null) return "null";
|
|
146
312
|
if (typeof value === "string") return value;
|
|
@@ -152,19 +318,19 @@ function formatValue(value) {
|
|
|
152
318
|
}
|
|
153
319
|
}
|
|
154
320
|
function DocKv({ entry }) {
|
|
155
|
-
return /* @__PURE__ */
|
|
156
|
-
/* @__PURE__ */
|
|
157
|
-
/* @__PURE__ */
|
|
321
|
+
return /* @__PURE__ */ jsxs2("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
|
|
322
|
+
/* @__PURE__ */ jsx5("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
|
|
323
|
+
/* @__PURE__ */ jsx5("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
|
|
158
324
|
] });
|
|
159
325
|
}
|
|
160
326
|
|
|
161
327
|
// src/hooks/useRenderers.ts
|
|
162
328
|
import { useContext as useContext2 } from "react";
|
|
163
|
-
var
|
|
329
|
+
var EMPTY_CUSTOM = {};
|
|
164
330
|
var EMPTY_RENDERERS2 = {};
|
|
165
331
|
function useCustomRenderers() {
|
|
166
332
|
const ctx = useContext2(ReportContext);
|
|
167
|
-
return ctx?.customRenderers ??
|
|
333
|
+
return ctx?.customRenderers ?? EMPTY_CUSTOM;
|
|
168
334
|
}
|
|
169
335
|
function useBuiltinRenderers() {
|
|
170
336
|
const ctx = useContext2(ReportContext);
|
|
@@ -172,37 +338,37 @@ function useBuiltinRenderers() {
|
|
|
172
338
|
}
|
|
173
339
|
|
|
174
340
|
// src/components/doc/DocCode.tsx
|
|
175
|
-
import { Fragment, jsx as
|
|
341
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
176
342
|
function CodeFigure({ label, children }) {
|
|
177
|
-
return /* @__PURE__ */
|
|
178
|
-
label ? /* @__PURE__ */
|
|
179
|
-
/* @__PURE__ */
|
|
343
|
+
return /* @__PURE__ */ jsxs3("figure", { className: "my-3", children: [
|
|
344
|
+
label ? /* @__PURE__ */ jsx6("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
|
|
345
|
+
/* @__PURE__ */ jsx6("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
|
|
180
346
|
] });
|
|
181
347
|
}
|
|
182
348
|
function DocCode({ entry }) {
|
|
183
349
|
const renderers = useBuiltinRenderers();
|
|
184
350
|
if (renderers.code) {
|
|
185
|
-
return /* @__PURE__ */
|
|
351
|
+
return /* @__PURE__ */ jsx6(Fragment, { children: renderers.code(entry) });
|
|
186
352
|
}
|
|
187
|
-
return /* @__PURE__ */
|
|
353
|
+
return /* @__PURE__ */ jsx6(CodeFigure, { label: entry.label, children: /* @__PURE__ */ jsx6("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
188
354
|
}
|
|
189
355
|
|
|
190
356
|
// src/components/doc/DocTable.tsx
|
|
191
|
-
import { jsx as
|
|
357
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
192
358
|
function DocTable({ entry }) {
|
|
193
|
-
return /* @__PURE__ */
|
|
194
|
-
entry.label ? /* @__PURE__ */
|
|
195
|
-
/* @__PURE__ */
|
|
196
|
-
/* @__PURE__ */
|
|
197
|
-
/* @__PURE__ */
|
|
359
|
+
return /* @__PURE__ */ jsxs4("figure", { className: "my-3", children: [
|
|
360
|
+
entry.label ? /* @__PURE__ */ jsx7("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
|
|
361
|
+
/* @__PURE__ */ jsx7("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs4("table", { className: "w-full border-collapse text-sm", children: [
|
|
362
|
+
/* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsx7("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ jsx7("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
|
|
363
|
+
/* @__PURE__ */ jsx7("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ jsx7("tr", { children: row.map((cell, j) => /* @__PURE__ */ jsx7("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
|
|
198
364
|
] }) })
|
|
199
365
|
] });
|
|
200
366
|
}
|
|
201
367
|
|
|
202
368
|
// src/components/doc/DocLink.tsx
|
|
203
|
-
import { jsx as
|
|
369
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
204
370
|
function DocLink({ entry }) {
|
|
205
|
-
return /* @__PURE__ */
|
|
371
|
+
return /* @__PURE__ */ jsx8(
|
|
206
372
|
"a",
|
|
207
373
|
{
|
|
208
374
|
className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
|
|
@@ -217,26 +383,11 @@ function DocLink({ entry }) {
|
|
|
217
383
|
// src/components/doc/DocSection.tsx
|
|
218
384
|
import { marked } from "marked";
|
|
219
385
|
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
if (!value) return void 0;
|
|
223
|
-
const trimmed = value.trim();
|
|
224
|
-
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
|
|
225
|
-
if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
|
|
226
|
-
return trimmed;
|
|
227
|
-
}
|
|
228
|
-
function safeImageUrl(value) {
|
|
229
|
-
if (!value) return void 0;
|
|
230
|
-
const trimmed = value.trim();
|
|
231
|
-
if (/^data:image\//i.test(trimmed)) return trimmed;
|
|
232
|
-
return safeUrl(trimmed);
|
|
233
|
-
}
|
|
234
|
-
function isLocalFsPath(value) {
|
|
235
|
-
return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
|
|
236
|
-
}
|
|
386
|
+
// src/lib/url.ts
|
|
387
|
+
import { safeUrl, safeImageUrl, isLocalFsPath } from "executable-stories-core/utils/url";
|
|
237
388
|
|
|
238
389
|
// src/components/doc/DocSection.tsx
|
|
239
|
-
import { Fragment as Fragment2, jsx as
|
|
390
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
240
391
|
function neutralizeUrl(value) {
|
|
241
392
|
return safeUrl(value) ?? "#";
|
|
242
393
|
}
|
|
@@ -247,12 +398,12 @@ function safeMarkdownHtml(markdown) {
|
|
|
247
398
|
function DocSection({ entry }) {
|
|
248
399
|
const renderers = useBuiltinRenderers();
|
|
249
400
|
if (renderers.section) {
|
|
250
|
-
return /* @__PURE__ */
|
|
401
|
+
return /* @__PURE__ */ jsx9(Fragment2, { children: renderers.section(entry) });
|
|
251
402
|
}
|
|
252
403
|
const html = safeMarkdownHtml(entry.markdown);
|
|
253
|
-
return /* @__PURE__ */
|
|
254
|
-
entry.title ? /* @__PURE__ */
|
|
255
|
-
/* @__PURE__ */
|
|
404
|
+
return /* @__PURE__ */ jsxs5("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
|
|
405
|
+
entry.title ? /* @__PURE__ */ jsx9("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
|
|
406
|
+
/* @__PURE__ */ jsx9(
|
|
256
407
|
"div",
|
|
257
408
|
{
|
|
258
409
|
className: "es-doc-prose prose prose-sm max-w-none",
|
|
@@ -263,11 +414,11 @@ function DocSection({ entry }) {
|
|
|
263
414
|
}
|
|
264
415
|
|
|
265
416
|
// src/components/doc/DocMermaid.tsx
|
|
266
|
-
import { Fragment as Fragment3, jsx as
|
|
417
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
267
418
|
function MermaidSource({ entry, ref }) {
|
|
268
|
-
return /* @__PURE__ */
|
|
269
|
-
entry.title ? /* @__PURE__ */
|
|
270
|
-
/* @__PURE__ */
|
|
419
|
+
return /* @__PURE__ */ jsxs6("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
|
|
420
|
+
entry.title ? /* @__PURE__ */ jsx10("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
421
|
+
/* @__PURE__ */ jsx10(
|
|
271
422
|
"pre",
|
|
272
423
|
{
|
|
273
424
|
"data-mermaid": true,
|
|
@@ -281,24 +432,24 @@ function MermaidSource({ entry, ref }) {
|
|
|
281
432
|
function DocMermaid({ entry }) {
|
|
282
433
|
const renderers = useBuiltinRenderers();
|
|
283
434
|
if (renderers.mermaid) {
|
|
284
|
-
return /* @__PURE__ */
|
|
435
|
+
return /* @__PURE__ */ jsx10(Fragment3, { children: renderers.mermaid(entry) });
|
|
285
436
|
}
|
|
286
|
-
return /* @__PURE__ */
|
|
437
|
+
return /* @__PURE__ */ jsx10(MermaidSource, { entry });
|
|
287
438
|
}
|
|
288
439
|
|
|
289
440
|
// src/components/doc/DocScreenshot.tsx
|
|
290
|
-
import { jsx as
|
|
441
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
291
442
|
function DocScreenshot({ entry }) {
|
|
292
443
|
const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
|
|
293
444
|
if (!src) {
|
|
294
|
-
return /* @__PURE__ */
|
|
295
|
-
/* @__PURE__ */
|
|
296
|
-
/* @__PURE__ */
|
|
297
|
-
entry.alt ? /* @__PURE__ */
|
|
445
|
+
return /* @__PURE__ */ jsxs7("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
446
|
+
/* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Screenshot unavailable" }),
|
|
447
|
+
/* @__PURE__ */ jsx11("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
448
|
+
entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
|
|
298
449
|
] });
|
|
299
450
|
}
|
|
300
|
-
return /* @__PURE__ */
|
|
301
|
-
/* @__PURE__ */
|
|
451
|
+
return /* @__PURE__ */ jsxs7("figure", { className: "my-3", children: [
|
|
452
|
+
/* @__PURE__ */ jsx11(
|
|
302
453
|
"img",
|
|
303
454
|
{
|
|
304
455
|
src,
|
|
@@ -307,24 +458,24 @@ function DocScreenshot({ entry }) {
|
|
|
307
458
|
className: "max-w-full rounded-md border border-border"
|
|
308
459
|
}
|
|
309
460
|
),
|
|
310
|
-
entry.alt ? /* @__PURE__ */
|
|
461
|
+
entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
|
|
311
462
|
] });
|
|
312
463
|
}
|
|
313
464
|
|
|
314
465
|
// src/components/doc/DocVideo.tsx
|
|
315
|
-
import { jsx as
|
|
466
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
316
467
|
function DocVideo({ entry }) {
|
|
317
468
|
if (isLocalFsPath(entry.path)) {
|
|
318
|
-
return /* @__PURE__ */
|
|
319
|
-
/* @__PURE__ */
|
|
320
|
-
/* @__PURE__ */
|
|
321
|
-
entry.caption ? /* @__PURE__ */
|
|
469
|
+
return /* @__PURE__ */ jsxs8("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
470
|
+
/* @__PURE__ */ jsx12("div", { className: "font-medium", children: "Video unavailable" }),
|
|
471
|
+
/* @__PURE__ */ jsx12("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
472
|
+
entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
|
|
322
473
|
] });
|
|
323
474
|
}
|
|
324
475
|
const src = safeUrl(entry.path);
|
|
325
476
|
const poster = safeUrl(entry.poster);
|
|
326
|
-
return /* @__PURE__ */
|
|
327
|
-
/* @__PURE__ */
|
|
477
|
+
return /* @__PURE__ */ jsxs8("figure", { className: "my-3", children: [
|
|
478
|
+
/* @__PURE__ */ jsx12(
|
|
328
479
|
"video",
|
|
329
480
|
{
|
|
330
481
|
controls: true,
|
|
@@ -334,21 +485,21 @@ function DocVideo({ entry }) {
|
|
|
334
485
|
className: "max-w-full rounded-md border border-border"
|
|
335
486
|
}
|
|
336
487
|
),
|
|
337
|
-
entry.caption ? /* @__PURE__ */
|
|
488
|
+
entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
|
|
338
489
|
] });
|
|
339
490
|
}
|
|
340
491
|
|
|
341
492
|
// src/components/doc/DocHtml.tsx
|
|
342
|
-
import { jsx as
|
|
493
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
343
494
|
function DocHtml({ entry }) {
|
|
344
495
|
const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
|
|
345
496
|
const title = entry.title ?? "Embedded HTML";
|
|
346
497
|
const safeHref = safeUrl(entry.url ?? entry.path);
|
|
347
498
|
const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
|
|
348
|
-
return /* @__PURE__ */
|
|
349
|
-
/* @__PURE__ */
|
|
350
|
-
/* @__PURE__ */
|
|
351
|
-
safeHref ? /* @__PURE__ */
|
|
499
|
+
return /* @__PURE__ */ jsxs9("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
|
|
500
|
+
/* @__PURE__ */ jsxs9("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
|
|
501
|
+
/* @__PURE__ */ jsx13("span", { children: entry.title ?? "HTML" }),
|
|
502
|
+
safeHref ? /* @__PURE__ */ jsx13(
|
|
352
503
|
"a",
|
|
353
504
|
{
|
|
354
505
|
href: safeHref,
|
|
@@ -361,7 +512,7 @@ function DocHtml({ entry }) {
|
|
|
361
512
|
}
|
|
362
513
|
) : null
|
|
363
514
|
] }),
|
|
364
|
-
/* @__PURE__ */
|
|
515
|
+
/* @__PURE__ */ jsx13(
|
|
365
516
|
"iframe",
|
|
366
517
|
{
|
|
367
518
|
...frameProps,
|
|
@@ -376,7 +527,7 @@ function DocHtml({ entry }) {
|
|
|
376
527
|
}
|
|
377
528
|
|
|
378
529
|
// src/components/doc/DocState.tsx
|
|
379
|
-
import { jsx as
|
|
530
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
380
531
|
function DocState({ entry }) {
|
|
381
532
|
let json;
|
|
382
533
|
try {
|
|
@@ -384,23 +535,23 @@ function DocState({ entry }) {
|
|
|
384
535
|
} catch {
|
|
385
536
|
json = String(entry.value);
|
|
386
537
|
}
|
|
387
|
-
return /* @__PURE__ */
|
|
538
|
+
return /* @__PURE__ */ jsx14(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx14("code", { className: "font-mono text-xs language-json", children: json }) });
|
|
388
539
|
}
|
|
389
540
|
|
|
390
541
|
// src/components/doc/DocCustom.tsx
|
|
391
|
-
import { Fragment as Fragment4, jsx as
|
|
542
|
+
import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
392
543
|
function DocCustom({ entry }) {
|
|
393
544
|
const renderers = useCustomRenderers();
|
|
394
545
|
const renderer = renderers[entry.type];
|
|
395
546
|
if (renderer) {
|
|
396
|
-
return /* @__PURE__ */
|
|
547
|
+
return /* @__PURE__ */ jsx15(Fragment4, { children: renderer(entry) });
|
|
397
548
|
}
|
|
398
|
-
return /* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
400
|
-
/* @__PURE__ */
|
|
549
|
+
return /* @__PURE__ */ jsxs10("div", { className: "my-2", "data-type": entry.type, children: [
|
|
550
|
+
/* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
|
|
551
|
+
/* @__PURE__ */ jsx15("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify2(entry.data) })
|
|
401
552
|
] });
|
|
402
553
|
}
|
|
403
|
-
function
|
|
554
|
+
function safeStringify2(value) {
|
|
404
555
|
try {
|
|
405
556
|
return JSON.stringify(value, null, 2);
|
|
406
557
|
} catch {
|
|
@@ -409,47 +560,47 @@ function safeStringify(value) {
|
|
|
409
560
|
}
|
|
410
561
|
|
|
411
562
|
// src/components/doc/DocEntry.tsx
|
|
412
|
-
import { jsx as
|
|
563
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
413
564
|
function DocEntry({ entry }) {
|
|
414
565
|
switch (entry.kind) {
|
|
415
566
|
case "note":
|
|
416
|
-
return /* @__PURE__ */
|
|
567
|
+
return /* @__PURE__ */ jsx16(DocNote, { entry });
|
|
417
568
|
case "tag":
|
|
418
|
-
return /* @__PURE__ */
|
|
569
|
+
return /* @__PURE__ */ jsx16(DocTag, { entry });
|
|
419
570
|
case "kv":
|
|
420
|
-
return /* @__PURE__ */
|
|
571
|
+
return /* @__PURE__ */ jsx16(DocKv, { entry });
|
|
421
572
|
case "code":
|
|
422
|
-
return /* @__PURE__ */
|
|
573
|
+
return /* @__PURE__ */ jsx16(DocCode, { entry });
|
|
423
574
|
case "table":
|
|
424
|
-
return /* @__PURE__ */
|
|
575
|
+
return /* @__PURE__ */ jsx16(DocTable, { entry });
|
|
425
576
|
case "link":
|
|
426
|
-
return /* @__PURE__ */
|
|
577
|
+
return /* @__PURE__ */ jsx16(DocLink, { entry });
|
|
427
578
|
case "section":
|
|
428
|
-
return /* @__PURE__ */
|
|
579
|
+
return /* @__PURE__ */ jsx16(DocSection, { entry });
|
|
429
580
|
case "mermaid":
|
|
430
|
-
return /* @__PURE__ */
|
|
581
|
+
return /* @__PURE__ */ jsx16(DocMermaid, { entry });
|
|
431
582
|
case "screenshot":
|
|
432
|
-
return /* @__PURE__ */
|
|
583
|
+
return /* @__PURE__ */ jsx16(DocScreenshot, { entry });
|
|
433
584
|
case "video":
|
|
434
|
-
return /* @__PURE__ */
|
|
585
|
+
return /* @__PURE__ */ jsx16(DocVideo, { entry });
|
|
435
586
|
case "html":
|
|
436
|
-
return /* @__PURE__ */
|
|
587
|
+
return /* @__PURE__ */ jsx16(DocHtml, { entry });
|
|
437
588
|
case "state":
|
|
438
|
-
return /* @__PURE__ */
|
|
589
|
+
return /* @__PURE__ */ jsx16(DocState, { entry });
|
|
439
590
|
case "custom":
|
|
440
|
-
return /* @__PURE__ */
|
|
591
|
+
return /* @__PURE__ */ jsx16(DocCustom, { entry });
|
|
441
592
|
}
|
|
442
593
|
}
|
|
443
594
|
|
|
444
595
|
// src/components/ReportDocEntries.tsx
|
|
445
|
-
import { Fragment as Fragment5, jsx as
|
|
596
|
+
import { Fragment as Fragment5, jsx as jsx17 } from "react/jsx-runtime";
|
|
446
597
|
function ReportDocEntries({ entries }) {
|
|
447
598
|
if (entries.length === 0) return null;
|
|
448
|
-
return /* @__PURE__ */
|
|
599
|
+
return /* @__PURE__ */ jsx17(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx17(DocEntry, { entry }, i)) });
|
|
449
600
|
}
|
|
450
601
|
|
|
451
602
|
// src/lib/step-params.tsx
|
|
452
|
-
import { jsx as
|
|
603
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
453
604
|
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
|
|
454
605
|
function highlightStepParams(text) {
|
|
455
606
|
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
@@ -461,7 +612,7 @@ function highlightStepParams(text) {
|
|
|
461
612
|
const end = start + match[0].length;
|
|
462
613
|
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
463
614
|
nodes.push(
|
|
464
|
-
/* @__PURE__ */
|
|
615
|
+
/* @__PURE__ */ jsx18("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
|
|
465
616
|
);
|
|
466
617
|
lastIndex = end;
|
|
467
618
|
});
|
|
@@ -470,7 +621,7 @@ function highlightStepParams(text) {
|
|
|
470
621
|
}
|
|
471
622
|
|
|
472
623
|
// src/components/ReportSteps.tsx
|
|
473
|
-
import { jsx as
|
|
624
|
+
import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
474
625
|
var STEP_GLYPH = {
|
|
475
626
|
passed: "\u2713",
|
|
476
627
|
failed: "\u2717",
|
|
@@ -485,21 +636,21 @@ var STEP_GLYPH_COLOR = {
|
|
|
485
636
|
};
|
|
486
637
|
function ReportSteps({ scenario }) {
|
|
487
638
|
if (scenario.steps.length === 0) return null;
|
|
488
|
-
return /* @__PURE__ */
|
|
639
|
+
return /* @__PURE__ */ jsx19("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx19(ReportStepItem, { step }, step.id)) });
|
|
489
640
|
}
|
|
490
641
|
function ReportStepItem({ step }) {
|
|
491
642
|
const kw = step.keyword.toLowerCase();
|
|
492
643
|
const continuation = kw === "and" || kw === "but";
|
|
493
|
-
return /* @__PURE__ */
|
|
644
|
+
return /* @__PURE__ */ jsxs11(
|
|
494
645
|
"li",
|
|
495
646
|
{
|
|
496
647
|
id: step.id,
|
|
497
648
|
"data-status": step.status,
|
|
498
649
|
className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
|
|
499
650
|
children: [
|
|
500
|
-
/* @__PURE__ */
|
|
501
|
-
/* @__PURE__ */
|
|
502
|
-
/* @__PURE__ */
|
|
651
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-baseline gap-2", children: [
|
|
652
|
+
/* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
|
|
653
|
+
/* @__PURE__ */ jsx19(
|
|
503
654
|
"span",
|
|
504
655
|
{
|
|
505
656
|
className: cn(
|
|
@@ -509,10 +660,10 @@ function ReportStepItem({ step }) {
|
|
|
509
660
|
children: step.keyword
|
|
510
661
|
}
|
|
511
662
|
),
|
|
512
|
-
/* @__PURE__ */
|
|
513
|
-
step.durationMs > 0 ? /* @__PURE__ */
|
|
663
|
+
/* @__PURE__ */ jsx19("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
664
|
+
step.durationMs > 0 ? /* @__PURE__ */ jsx19("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
|
|
514
665
|
] }),
|
|
515
|
-
step.errorMessage ? /* @__PURE__ */
|
|
666
|
+
step.errorMessage ? /* @__PURE__ */ jsx19(
|
|
516
667
|
"pre",
|
|
517
668
|
{
|
|
518
669
|
role: "alert",
|
|
@@ -520,132 +671,17 @@ function ReportStepItem({ step }) {
|
|
|
520
671
|
children: step.errorMessage
|
|
521
672
|
}
|
|
522
673
|
) : null,
|
|
523
|
-
step.docEntries.length > 0 ? /* @__PURE__ */
|
|
674
|
+
step.docEntries.length > 0 ? /* @__PURE__ */ jsx19("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx19(ReportDocEntries, { entries: step.docEntries }) }) : null
|
|
524
675
|
]
|
|
525
676
|
}
|
|
526
677
|
);
|
|
527
678
|
}
|
|
528
679
|
|
|
529
|
-
// ../executable-stories-core/dist/state-diff.js
|
|
530
|
-
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
531
|
-
function deepEqual(a, b) {
|
|
532
|
-
if (Object.is(a, b)) return true;
|
|
533
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
534
|
-
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
535
|
-
}
|
|
536
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
537
|
-
const ak = Object.keys(a);
|
|
538
|
-
const bk = Object.keys(b);
|
|
539
|
-
return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
|
|
540
|
-
}
|
|
541
|
-
return false;
|
|
542
|
-
}
|
|
543
|
-
var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
|
|
544
|
-
function walk(path, before, after, out) {
|
|
545
|
-
if (Object.is(before, after)) return;
|
|
546
|
-
if (Array.isArray(before) && Array.isArray(after)) {
|
|
547
|
-
const shared = Math.min(before.length, after.length);
|
|
548
|
-
for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
|
|
549
|
-
for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
|
|
550
|
-
for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
if (isPlainObject(before) && isPlainObject(after)) {
|
|
554
|
-
for (const key of Object.keys(after)) {
|
|
555
|
-
const p = joinKey(path, key);
|
|
556
|
-
if (key in before) walk(p, before[key], after[key], out);
|
|
557
|
-
else out.push({ path: p, kind: "added", after: after[key] });
|
|
558
|
-
}
|
|
559
|
-
for (const key of Object.keys(before)) {
|
|
560
|
-
if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
|
|
561
|
-
}
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
|
|
565
|
-
}
|
|
566
|
-
function diffStateValues(before, after) {
|
|
567
|
-
const out = [];
|
|
568
|
-
walk("", before, after, out);
|
|
569
|
-
return out;
|
|
570
|
-
}
|
|
571
|
-
function formatStateValue(value, maxLen = 60) {
|
|
572
|
-
const s = value === void 0 ? "undefined" : JSON.stringify(value);
|
|
573
|
-
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
574
|
-
}
|
|
575
|
-
function summarizeStateChanges(changes) {
|
|
576
|
-
return changes.map((c) => {
|
|
577
|
-
const at = c.path === "" ? "(root)" : c.path;
|
|
578
|
-
switch (c.kind) {
|
|
579
|
-
case "added":
|
|
580
|
-
return `+ ${at}: ${formatStateValue(c.after)}`;
|
|
581
|
-
case "removed":
|
|
582
|
-
return `\u2212 ${at}`;
|
|
583
|
-
case "changed":
|
|
584
|
-
return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// ../executable-stories-core/dist/storyboard.js
|
|
590
|
-
function findScreenshot(entries) {
|
|
591
|
-
for (const entry of entries) {
|
|
592
|
-
if (entry.kind === "screenshot") return entry;
|
|
593
|
-
if (entry.children) {
|
|
594
|
-
const nested = findScreenshot(entry.children);
|
|
595
|
-
if (nested) return nested;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
return void 0;
|
|
599
|
-
}
|
|
600
|
-
function findStates(entries, out = []) {
|
|
601
|
-
for (const entry of entries) {
|
|
602
|
-
if (entry.kind === "state") out.push(entry);
|
|
603
|
-
if (entry.children) findStates(entry.children, out);
|
|
604
|
-
}
|
|
605
|
-
return out;
|
|
606
|
-
}
|
|
607
|
-
var laneOf = (label) => label ?? "";
|
|
608
|
-
function extractStoryboardFrames(scenario) {
|
|
609
|
-
const frames = [];
|
|
610
|
-
const laneOrder = [];
|
|
611
|
-
const previous = /* @__PURE__ */ new Map();
|
|
612
|
-
for (const step of scenario.steps) {
|
|
613
|
-
const shot = findScreenshot(step.docEntries);
|
|
614
|
-
const stateDocs = findStates(step.docEntries);
|
|
615
|
-
if (!shot && stateDocs.length === 0) continue;
|
|
616
|
-
const byLane = /* @__PURE__ */ new Map();
|
|
617
|
-
for (const doc of stateDocs) {
|
|
618
|
-
const lane = laneOf(doc.label);
|
|
619
|
-
if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
|
|
620
|
-
byLane.set(lane, doc);
|
|
621
|
-
}
|
|
622
|
-
const states = [];
|
|
623
|
-
for (const lane of laneOrder) {
|
|
624
|
-
const doc = byLane.get(lane);
|
|
625
|
-
if (!doc) continue;
|
|
626
|
-
const card = {
|
|
627
|
-
...doc.label !== void 0 && { label: doc.label },
|
|
628
|
-
value: doc.value
|
|
629
|
-
};
|
|
630
|
-
if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
|
|
631
|
-
previous.set(lane, doc.value);
|
|
632
|
-
states.push(card);
|
|
633
|
-
}
|
|
634
|
-
frames.push({
|
|
635
|
-
stepId: step.id,
|
|
636
|
-
stepIndex: step.index,
|
|
637
|
-
keyword: step.keyword,
|
|
638
|
-
text: step.text,
|
|
639
|
-
status: step.status,
|
|
640
|
-
...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
|
|
641
|
-
states
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
return frames;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
680
|
// src/components/ReportStoryboard.tsx
|
|
648
|
-
import {
|
|
681
|
+
import { extractStoryboardFrames } from "executable-stories-core/storyboard";
|
|
682
|
+
import { summarizeStateChanges } from "executable-stories-core/state-diff";
|
|
683
|
+
import { isLocalFsPath as isLocalFsPath2, safeImageUrl as safeImageUrl2 } from "executable-stories-core/utils/url";
|
|
684
|
+
import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
649
685
|
var FRAME_GLYPH = {
|
|
650
686
|
passed: "\u2713",
|
|
651
687
|
failed: "\u2717",
|
|
@@ -661,46 +697,46 @@ var FRAME_GLYPH_COLOR = {
|
|
|
661
697
|
function ReportStoryboard({ scenario }) {
|
|
662
698
|
const frames = extractStoryboardFrames(scenario);
|
|
663
699
|
if (frames.length < 2) return null;
|
|
664
|
-
return /* @__PURE__ */
|
|
665
|
-
/* @__PURE__ */
|
|
666
|
-
/* @__PURE__ */
|
|
700
|
+
return /* @__PURE__ */ jsxs12("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
|
|
701
|
+
/* @__PURE__ */ jsx20("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
|
|
702
|
+
/* @__PURE__ */ jsx20("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx20(StoryboardFrameItem, { frame }, frame.stepId)) })
|
|
667
703
|
] });
|
|
668
704
|
}
|
|
669
705
|
function StoryboardFrameItem({ frame }) {
|
|
670
706
|
const label = `${frame.keyword} ${frame.text}`;
|
|
671
707
|
const hasStates = frame.states.length > 0;
|
|
672
|
-
const caption = /* @__PURE__ */
|
|
673
|
-
/* @__PURE__ */
|
|
674
|
-
/* @__PURE__ */
|
|
675
|
-
/* @__PURE__ */
|
|
708
|
+
const caption = /* @__PURE__ */ jsxs12("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
|
|
709
|
+
/* @__PURE__ */ jsx20("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
|
|
710
|
+
/* @__PURE__ */ jsxs12("span", { className: "min-w-0", children: [
|
|
711
|
+
/* @__PURE__ */ jsx20("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
|
|
676
712
|
" ",
|
|
677
|
-
/* @__PURE__ */
|
|
713
|
+
/* @__PURE__ */ jsx20("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
|
|
678
714
|
] })
|
|
679
715
|
] });
|
|
680
716
|
return (
|
|
681
717
|
// State cards need room for diff rows; screenshot-only frames keep the
|
|
682
718
|
// original narrow film-cell width (and DOM) unchanged.
|
|
683
|
-
/* @__PURE__ */
|
|
684
|
-
frame.screenshot ? /* @__PURE__ */
|
|
685
|
-
hasStates ? /* @__PURE__ */
|
|
719
|
+
/* @__PURE__ */ jsxs12("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
|
|
720
|
+
frame.screenshot ? /* @__PURE__ */ jsx20(FrameScreenshot, { frame, label }) : null,
|
|
721
|
+
hasStates ? /* @__PURE__ */ jsx20("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ jsx20(StateCardView, { card }, card.label ?? i)) }) : null,
|
|
686
722
|
frame.screenshot ? caption : (
|
|
687
723
|
// No screenshot means no image anchor, so the caption carries the
|
|
688
724
|
// jump-to-step link instead.
|
|
689
|
-
/* @__PURE__ */
|
|
725
|
+
/* @__PURE__ */ jsx20("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
|
|
690
726
|
)
|
|
691
727
|
] })
|
|
692
728
|
);
|
|
693
729
|
}
|
|
694
730
|
function FrameScreenshot({ frame, label }) {
|
|
695
731
|
const path = frame.screenshot?.path ?? "";
|
|
696
|
-
const src =
|
|
697
|
-
return /* @__PURE__ */
|
|
732
|
+
const src = isLocalFsPath2(path) ? void 0 : safeImageUrl2(path);
|
|
733
|
+
return /* @__PURE__ */ jsx20(
|
|
698
734
|
"a",
|
|
699
735
|
{
|
|
700
736
|
href: `#${frame.stepId}`,
|
|
701
737
|
"aria-label": `Jump to step: ${label}`,
|
|
702
738
|
className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
|
|
703
|
-
children: src ? /* @__PURE__ */
|
|
739
|
+
children: src ? /* @__PURE__ */ jsx20(
|
|
704
740
|
"img",
|
|
705
741
|
{
|
|
706
742
|
src,
|
|
@@ -708,7 +744,7 @@ function FrameScreenshot({ frame, label }) {
|
|
|
708
744
|
loading: "lazy",
|
|
709
745
|
className: "h-24 w-full bg-muted object-cover object-top"
|
|
710
746
|
}
|
|
711
|
-
) : /* @__PURE__ */
|
|
747
|
+
) : /* @__PURE__ */ jsx20("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
|
|
712
748
|
}
|
|
713
749
|
);
|
|
714
750
|
}
|
|
@@ -727,12 +763,12 @@ function prettyJson(value) {
|
|
|
727
763
|
function StateCardView({ card }) {
|
|
728
764
|
const json = prettyJson(card.value);
|
|
729
765
|
const firstCapture = card.changes === void 0;
|
|
730
|
-
return /* @__PURE__ */
|
|
731
|
-
/* @__PURE__ */
|
|
732
|
-
/* @__PURE__ */
|
|
733
|
-
firstCapture ? /* @__PURE__ */
|
|
766
|
+
return /* @__PURE__ */ jsxs12("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
|
|
767
|
+
/* @__PURE__ */ jsxs12("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
|
|
768
|
+
/* @__PURE__ */ jsx20("span", { className: "inline-flex rounded-full border border-border px-1.5 font-mono text-[0.625rem] font-medium text-muted-foreground", children: card.label ?? "State" }),
|
|
769
|
+
firstCapture ? /* @__PURE__ */ jsx20("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
|
|
734
770
|
] }),
|
|
735
|
-
!firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */
|
|
771
|
+
!firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ jsx20("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ jsx20(
|
|
736
772
|
"li",
|
|
737
773
|
{
|
|
738
774
|
className: cn(
|
|
@@ -742,15 +778,15 @@ function StateCardView({ card }) {
|
|
|
742
778
|
children: summarizeStateChanges([change])[0]
|
|
743
779
|
},
|
|
744
780
|
i
|
|
745
|
-
)) }) : /* @__PURE__ */
|
|
746
|
-
/* @__PURE__ */
|
|
781
|
+
)) }) : /* @__PURE__ */ jsx20("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
|
|
782
|
+
/* @__PURE__ */ jsxs12(
|
|
747
783
|
"details",
|
|
748
784
|
{
|
|
749
785
|
open: firstCapture && json.length <= 240,
|
|
750
786
|
className: cn(firstCapture ? "mt-1.5" : "mt-1"),
|
|
751
787
|
children: [
|
|
752
|
-
/* @__PURE__ */
|
|
753
|
-
/* @__PURE__ */
|
|
788
|
+
/* @__PURE__ */ jsx20("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
|
|
789
|
+
/* @__PURE__ */ jsx20("pre", { tabIndex: 0, className: "mt-1 max-h-48 overflow-auto rounded-sm bg-muted p-1.5 font-mono text-[0.625rem] leading-snug", children: json })
|
|
754
790
|
]
|
|
755
791
|
}
|
|
756
792
|
)
|
|
@@ -758,17 +794,17 @@ function StateCardView({ card }) {
|
|
|
758
794
|
}
|
|
759
795
|
|
|
760
796
|
// src/components/ReportAttachments.tsx
|
|
761
|
-
import { Fragment as Fragment6, jsx as
|
|
797
|
+
import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
762
798
|
function ReportAttachments({ attachments }) {
|
|
763
799
|
if (attachments.length === 0) return null;
|
|
764
800
|
return (
|
|
765
801
|
// role="group" (not a labelled <section>) so multiple scenarios' attachment
|
|
766
802
|
// blocks don't create duplicate "Attachments" region landmarks.
|
|
767
|
-
/* @__PURE__ */
|
|
803
|
+
/* @__PURE__ */ jsx21("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
|
|
768
804
|
const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
|
|
769
805
|
const isImage = att.mediaType.startsWith("image/");
|
|
770
|
-
return /* @__PURE__ */
|
|
771
|
-
/* @__PURE__ */
|
|
806
|
+
return /* @__PURE__ */ jsx21("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
807
|
+
/* @__PURE__ */ jsx21(
|
|
772
808
|
"img",
|
|
773
809
|
{
|
|
774
810
|
src: dataUri,
|
|
@@ -777,8 +813,8 @@ function ReportAttachments({ attachments }) {
|
|
|
777
813
|
className: "max-w-full rounded-md border border-border"
|
|
778
814
|
}
|
|
779
815
|
),
|
|
780
|
-
/* @__PURE__ */
|
|
781
|
-
] }) : /* @__PURE__ */
|
|
816
|
+
/* @__PURE__ */ jsx21("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
|
|
817
|
+
] }) : /* @__PURE__ */ jsxs13(
|
|
782
818
|
"a",
|
|
783
819
|
{
|
|
784
820
|
href: dataUri,
|
|
@@ -787,7 +823,7 @@ function ReportAttachments({ attachments }) {
|
|
|
787
823
|
children: [
|
|
788
824
|
att.name,
|
|
789
825
|
" ",
|
|
790
|
-
/* @__PURE__ */
|
|
826
|
+
/* @__PURE__ */ jsxs13("span", { className: "text-muted-foreground", children: [
|
|
791
827
|
"(",
|
|
792
828
|
att.mediaType,
|
|
793
829
|
")"
|
|
@@ -799,6 +835,9 @@ function ReportAttachments({ attachments }) {
|
|
|
799
835
|
);
|
|
800
836
|
}
|
|
801
837
|
|
|
838
|
+
// src/components/ReportTrace.tsx
|
|
839
|
+
import { formatDuration as formatDuration2 } from "executable-stories-core/utils/duration";
|
|
840
|
+
|
|
802
841
|
// src/lib/trace.ts
|
|
803
842
|
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
804
843
|
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
@@ -851,15 +890,15 @@ function toTraceRows(spans) {
|
|
|
851
890
|
roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
852
891
|
const rows = [];
|
|
853
892
|
const seen = /* @__PURE__ */ new Set();
|
|
854
|
-
const
|
|
893
|
+
const walk = (node, depth) => {
|
|
855
894
|
if (seen.has(node.span.spanId)) return;
|
|
856
895
|
seen.add(node.span.spanId);
|
|
857
896
|
rows.push({ ...node.span, depth });
|
|
858
|
-
for (const child of node.children)
|
|
897
|
+
for (const child of node.children) walk(child, depth + 1);
|
|
859
898
|
};
|
|
860
|
-
for (const root of roots)
|
|
899
|
+
for (const root of roots) walk(root, 0);
|
|
861
900
|
for (const node of byId.values()) {
|
|
862
|
-
if (!seen.has(node.span.spanId))
|
|
901
|
+
if (!seen.has(node.span.spanId)) walk(node, 0);
|
|
863
902
|
}
|
|
864
903
|
return rows;
|
|
865
904
|
}
|
|
@@ -875,7 +914,7 @@ function traceWindow(rows) {
|
|
|
875
914
|
}
|
|
876
915
|
|
|
877
916
|
// src/components/ReportTrace.tsx
|
|
878
|
-
import { jsx as
|
|
917
|
+
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
879
918
|
var BAR_COLOR = {
|
|
880
919
|
ok: "bg-pass",
|
|
881
920
|
error: "bg-fail",
|
|
@@ -886,21 +925,21 @@ function ReportTrace({ spans }) {
|
|
|
886
925
|
const rows = toTraceRows(spans);
|
|
887
926
|
if (rows.length === 0) return null;
|
|
888
927
|
const { minStartMs, totalMs } = traceWindow(rows);
|
|
889
|
-
return /* @__PURE__ */
|
|
890
|
-
/* @__PURE__ */
|
|
928
|
+
return /* @__PURE__ */ jsxs14("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
|
|
929
|
+
/* @__PURE__ */ jsxs14("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
|
|
891
930
|
"Trace (",
|
|
892
931
|
rows.length,
|
|
893
932
|
" span",
|
|
894
933
|
rows.length === 1 ? "" : "s",
|
|
895
934
|
", ",
|
|
896
|
-
|
|
935
|
+
formatDuration2(totalMs),
|
|
897
936
|
")"
|
|
898
937
|
] }),
|
|
899
|
-
/* @__PURE__ */
|
|
938
|
+
/* @__PURE__ */ jsx22("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
|
|
900
939
|
const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
|
|
901
940
|
const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
|
|
902
|
-
return /* @__PURE__ */
|
|
903
|
-
/* @__PURE__ */
|
|
941
|
+
return /* @__PURE__ */ jsxs14("li", { className: "flex items-center gap-2", children: [
|
|
942
|
+
/* @__PURE__ */ jsx22(
|
|
904
943
|
"span",
|
|
905
944
|
{
|
|
906
945
|
className: "min-w-0 flex-1 truncate text-foreground",
|
|
@@ -909,14 +948,14 @@ function ReportTrace({ spans }) {
|
|
|
909
948
|
children: row.name
|
|
910
949
|
}
|
|
911
950
|
),
|
|
912
|
-
/* @__PURE__ */
|
|
951
|
+
/* @__PURE__ */ jsx22("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx22(
|
|
913
952
|
"span",
|
|
914
953
|
{
|
|
915
954
|
className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
|
|
916
955
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
917
956
|
}
|
|
918
957
|
) }),
|
|
919
|
-
/* @__PURE__ */
|
|
958
|
+
/* @__PURE__ */ jsx22("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration2(row.durationMs) })
|
|
920
959
|
] }, `${row.spanId}-${i}`);
|
|
921
960
|
}) })
|
|
922
961
|
] });
|
|
@@ -967,7 +1006,7 @@ function flakinessOf(entries) {
|
|
|
967
1006
|
}
|
|
968
1007
|
|
|
969
1008
|
// src/components/ScenarioRunHistory.tsx
|
|
970
|
-
import { Fragment as Fragment7, jsx as
|
|
1009
|
+
import { Fragment as Fragment7, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
971
1010
|
var MAX_DOTS = 10;
|
|
972
1011
|
var DOT_COLOR = {
|
|
973
1012
|
passed: "bg-pass",
|
|
@@ -987,16 +1026,16 @@ function ScenarioRunHistory({ scenarioId }) {
|
|
|
987
1026
|
const recent = entries.slice(-MAX_DOTS);
|
|
988
1027
|
const flaky = flakinessOf(recent) === "flaky";
|
|
989
1028
|
const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
|
|
990
|
-
return /* @__PURE__ */
|
|
991
|
-
flaky ? /* @__PURE__ */
|
|
992
|
-
/* @__PURE__ */
|
|
1029
|
+
return /* @__PURE__ */ jsxs15(Fragment7, { children: [
|
|
1030
|
+
flaky ? /* @__PURE__ */ jsx23(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
|
|
1031
|
+
/* @__PURE__ */ jsx23(
|
|
993
1032
|
"span",
|
|
994
1033
|
{
|
|
995
1034
|
role: "img",
|
|
996
1035
|
"aria-label": `Run history: ${summary}`,
|
|
997
1036
|
title: summary,
|
|
998
1037
|
className: "flex shrink-0 items-center gap-[3px]",
|
|
999
|
-
children: recent.map((e, i) => /* @__PURE__ */
|
|
1038
|
+
children: recent.map((e, i) => /* @__PURE__ */ jsx23(
|
|
1000
1039
|
"span",
|
|
1001
1040
|
{
|
|
1002
1041
|
"aria-hidden": true,
|
|
@@ -1027,6 +1066,7 @@ function useCollapse() {
|
|
|
1027
1066
|
|
|
1028
1067
|
// src/interactive/scenario-actions.tsx
|
|
1029
1068
|
import { createContext as createContext4, useContext as useContext5 } from "react";
|
|
1069
|
+
import { scenarioToMarkdown as coreScenarioToMarkdown } from "executable-stories-core/utils/scenario-markdown";
|
|
1030
1070
|
var ScenarioActionsContext = createContext4(null);
|
|
1031
1071
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
1032
1072
|
function useScenarioActions() {
|
|
@@ -1035,9 +1075,9 @@ function useScenarioActions() {
|
|
|
1035
1075
|
|
|
1036
1076
|
// src/components/ui/card.tsx
|
|
1037
1077
|
import "react";
|
|
1038
|
-
import { jsx as
|
|
1078
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1039
1079
|
function Card({ className, ...props }) {
|
|
1040
|
-
return /* @__PURE__ */
|
|
1080
|
+
return /* @__PURE__ */ jsx24(
|
|
1041
1081
|
"div",
|
|
1042
1082
|
{
|
|
1043
1083
|
"data-slot": "card",
|
|
@@ -1050,7 +1090,7 @@ function Card({ className, ...props }) {
|
|
|
1050
1090
|
);
|
|
1051
1091
|
}
|
|
1052
1092
|
function CardHeader({ className, ...props }) {
|
|
1053
|
-
return /* @__PURE__ */
|
|
1093
|
+
return /* @__PURE__ */ jsx24(
|
|
1054
1094
|
"div",
|
|
1055
1095
|
{
|
|
1056
1096
|
"data-slot": "card-header",
|
|
@@ -1063,7 +1103,7 @@ function CardHeader({ className, ...props }) {
|
|
|
1063
1103
|
);
|
|
1064
1104
|
}
|
|
1065
1105
|
function CardContent({ className, ...props }) {
|
|
1066
|
-
return /* @__PURE__ */
|
|
1106
|
+
return /* @__PURE__ */ jsx24(
|
|
1067
1107
|
"div",
|
|
1068
1108
|
{
|
|
1069
1109
|
"data-slot": "card-content",
|
|
@@ -1077,13 +1117,13 @@ function CardContent({ className, ...props }) {
|
|
|
1077
1117
|
import "react";
|
|
1078
1118
|
import { Menu as MenuPrimitive } from "@base-ui/react/menu";
|
|
1079
1119
|
import { CheckIcon } from "lucide-react";
|
|
1080
|
-
import { jsx as
|
|
1120
|
+
import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1081
1121
|
var itemClasses = "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground";
|
|
1082
1122
|
function DropdownMenu(props) {
|
|
1083
|
-
return /* @__PURE__ */
|
|
1123
|
+
return /* @__PURE__ */ jsx25(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
|
|
1084
1124
|
}
|
|
1085
1125
|
function DropdownMenuTrigger(props) {
|
|
1086
|
-
return /* @__PURE__ */
|
|
1126
|
+
return /* @__PURE__ */ jsx25(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
1087
1127
|
}
|
|
1088
1128
|
function DropdownMenuContent({
|
|
1089
1129
|
className,
|
|
@@ -1091,14 +1131,14 @@ function DropdownMenuContent({
|
|
|
1091
1131
|
sideOffset = 4,
|
|
1092
1132
|
...props
|
|
1093
1133
|
}) {
|
|
1094
|
-
return /* @__PURE__ */
|
|
1134
|
+
return /* @__PURE__ */ jsx25(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx25(
|
|
1095
1135
|
MenuPrimitive.Positioner,
|
|
1096
1136
|
{
|
|
1097
1137
|
"data-slot": "dropdown-menu-positioner",
|
|
1098
1138
|
className: "z-50",
|
|
1099
1139
|
align,
|
|
1100
1140
|
sideOffset,
|
|
1101
|
-
children: /* @__PURE__ */
|
|
1141
|
+
children: /* @__PURE__ */ jsx25(
|
|
1102
1142
|
MenuPrimitive.Popup,
|
|
1103
1143
|
{
|
|
1104
1144
|
"data-slot": "dropdown-menu-content",
|
|
@@ -1116,7 +1156,7 @@ function DropdownMenuItem({
|
|
|
1116
1156
|
className,
|
|
1117
1157
|
...props
|
|
1118
1158
|
}) {
|
|
1119
|
-
return /* @__PURE__ */
|
|
1159
|
+
return /* @__PURE__ */ jsx25(
|
|
1120
1160
|
MenuPrimitive.Item,
|
|
1121
1161
|
{
|
|
1122
1162
|
"data-slot": "dropdown-menu-item",
|
|
@@ -1128,7 +1168,7 @@ function DropdownMenuItem({
|
|
|
1128
1168
|
|
|
1129
1169
|
// src/components/ReportScenario.tsx
|
|
1130
1170
|
import { LinkIcon, FileTextIcon, SparklesIcon, MoreHorizontalIcon } from "lucide-react";
|
|
1131
|
-
import { jsx as
|
|
1171
|
+
import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1132
1172
|
var STATUS_META = {
|
|
1133
1173
|
passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
|
|
1134
1174
|
failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
|
|
@@ -1144,7 +1184,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1144
1184
|
const collapsible = collapse !== null && !hideTitle;
|
|
1145
1185
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
1146
1186
|
const actions = useScenarioActions();
|
|
1147
|
-
return /* @__PURE__ */
|
|
1187
|
+
return /* @__PURE__ */ jsxs17(
|
|
1148
1188
|
Card,
|
|
1149
1189
|
{
|
|
1150
1190
|
id: scenario.id,
|
|
@@ -1152,10 +1192,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1152
1192
|
...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
|
|
1153
1193
|
className: "gap-0 py-0",
|
|
1154
1194
|
children: [
|
|
1155
|
-
/* @__PURE__ */
|
|
1156
|
-
/* @__PURE__ */
|
|
1157
|
-
hideTitle ? null : /* @__PURE__ */
|
|
1158
|
-
collapsible ? /* @__PURE__ */
|
|
1195
|
+
/* @__PURE__ */ jsxs17(CardHeader, { className: "px-4 py-3", children: [
|
|
1196
|
+
/* @__PURE__ */ jsxs17("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
1197
|
+
hideTitle ? null : /* @__PURE__ */ jsxs17("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1198
|
+
collapsible ? /* @__PURE__ */ jsx26(
|
|
1159
1199
|
"button",
|
|
1160
1200
|
{
|
|
1161
1201
|
type: "button",
|
|
@@ -1164,49 +1204,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1164
1204
|
"aria-controls": bodyId,
|
|
1165
1205
|
"aria-label": `Toggle ${scenario.title}`,
|
|
1166
1206
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
1167
|
-
children: /* @__PURE__ */
|
|
1207
|
+
children: /* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
1168
1208
|
}
|
|
1169
1209
|
) : null,
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1210
|
+
/* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
|
|
1211
|
+
/* @__PURE__ */ jsx26("span", { children: scenario.title })
|
|
1172
1212
|
] }),
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
/* @__PURE__ */
|
|
1175
|
-
scenario.durationMs > 0 ? /* @__PURE__ */
|
|
1176
|
-
meta.showPill ? /* @__PURE__ */
|
|
1177
|
-
actions ? /* @__PURE__ */
|
|
1178
|
-
/* @__PURE__ */
|
|
1213
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1214
|
+
/* @__PURE__ */ jsx26(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1215
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ jsx26("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration3(scenario.durationMs) }) : null,
|
|
1216
|
+
meta.showPill ? /* @__PURE__ */ jsx26(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx26("span", { className: "sr-only", children: `Status: ${label}` }),
|
|
1217
|
+
actions ? /* @__PURE__ */ jsxs17(DropdownMenu, { children: [
|
|
1218
|
+
/* @__PURE__ */ jsx26(
|
|
1179
1219
|
DropdownMenuTrigger,
|
|
1180
1220
|
{
|
|
1181
1221
|
"aria-label": `Actions for ${scenario.title}`,
|
|
1182
1222
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
1183
|
-
children: /* @__PURE__ */
|
|
1223
|
+
children: /* @__PURE__ */ jsx26(MoreHorizontalIcon, { "aria-hidden": true })
|
|
1184
1224
|
}
|
|
1185
1225
|
),
|
|
1186
|
-
/* @__PURE__ */
|
|
1187
|
-
/* @__PURE__ */
|
|
1188
|
-
/* @__PURE__ */
|
|
1226
|
+
/* @__PURE__ */ jsxs17(DropdownMenuContent, { align: "end", children: [
|
|
1227
|
+
/* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
|
|
1228
|
+
/* @__PURE__ */ jsx26(LinkIcon, {}),
|
|
1189
1229
|
" Copy link"
|
|
1190
1230
|
] }),
|
|
1191
|
-
/* @__PURE__ */
|
|
1192
|
-
/* @__PURE__ */
|
|
1231
|
+
/* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
|
|
1232
|
+
/* @__PURE__ */ jsx26(FileTextIcon, {}),
|
|
1193
1233
|
" Copy as Markdown"
|
|
1194
1234
|
] }),
|
|
1195
|
-
/* @__PURE__ */
|
|
1196
|
-
/* @__PURE__ */
|
|
1235
|
+
/* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
|
|
1236
|
+
/* @__PURE__ */ jsx26(SparklesIcon, {}),
|
|
1197
1237
|
" Explain with AI"
|
|
1198
1238
|
] })
|
|
1199
1239
|
] })
|
|
1200
1240
|
] }) : null
|
|
1201
1241
|
] })
|
|
1202
1242
|
] }),
|
|
1203
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */
|
|
1204
|
-
scenario.tags.map((t) => /* @__PURE__ */
|
|
1205
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */
|
|
1243
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs17("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
1244
|
+
scenario.tags.map((t) => /* @__PURE__ */ jsx26("li", { children: /* @__PURE__ */ jsx26(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
1245
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx26("li", { children: ticket.url ? /* @__PURE__ */ jsx26("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx26(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx26(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
1206
1246
|
] }) : null
|
|
1207
1247
|
] }),
|
|
1208
|
-
/* @__PURE__ */
|
|
1209
|
-
scenario.errorMessage ? /* @__PURE__ */
|
|
1248
|
+
/* @__PURE__ */ jsxs17(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1249
|
+
scenario.errorMessage ? /* @__PURE__ */ jsx26(
|
|
1210
1250
|
"pre",
|
|
1211
1251
|
{
|
|
1212
1252
|
role: "alert",
|
|
@@ -1214,11 +1254,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1214
1254
|
children: scenario.errorMessage
|
|
1215
1255
|
}
|
|
1216
1256
|
) : null,
|
|
1217
|
-
/* @__PURE__ */
|
|
1218
|
-
/* @__PURE__ */
|
|
1219
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */
|
|
1220
|
-
/* @__PURE__ */
|
|
1221
|
-
/* @__PURE__ */
|
|
1257
|
+
/* @__PURE__ */ jsx26(ReportStoryboard, { scenario }),
|
|
1258
|
+
/* @__PURE__ */ jsx26(ReportSteps, { scenario }),
|
|
1259
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx26("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx26(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
1260
|
+
/* @__PURE__ */ jsx26(ReportAttachments, { attachments: scenario.attachments }),
|
|
1261
|
+
/* @__PURE__ */ jsx26(ReportTrace, { spans: scenario.otelSpans })
|
|
1222
1262
|
] })
|
|
1223
1263
|
]
|
|
1224
1264
|
}
|
|
@@ -1226,13 +1266,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1226
1266
|
}
|
|
1227
1267
|
|
|
1228
1268
|
// src/components/ReportScenarioList.tsx
|
|
1229
|
-
import { Fragment as Fragment8, jsx as
|
|
1269
|
+
import { Fragment as Fragment8, jsx as jsx27 } from "react/jsx-runtime";
|
|
1230
1270
|
function ReportScenarioList({ feature }) {
|
|
1231
|
-
return /* @__PURE__ */
|
|
1271
|
+
return /* @__PURE__ */ jsx27(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx27(ReportScenario, { scenario }, scenario.id)) });
|
|
1232
1272
|
}
|
|
1233
1273
|
|
|
1234
1274
|
// src/components/ReportFeature.tsx
|
|
1235
|
-
import { Fragment as Fragment9, jsx as
|
|
1275
|
+
import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1236
1276
|
function ReportFeature({ feature }) {
|
|
1237
1277
|
const titleId = `${feature.id}-title`;
|
|
1238
1278
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1243,7 +1283,7 @@ function ReportFeature({ feature }) {
|
|
|
1243
1283
|
return (
|
|
1244
1284
|
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1245
1285
|
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1246
|
-
/* @__PURE__ */
|
|
1286
|
+
/* @__PURE__ */ jsxs18(
|
|
1247
1287
|
"section",
|
|
1248
1288
|
{
|
|
1249
1289
|
id: feature.id,
|
|
@@ -1251,9 +1291,9 @@ function ReportFeature({ feature }) {
|
|
|
1251
1291
|
"aria-labelledby": titleId,
|
|
1252
1292
|
className: "flex flex-col gap-3",
|
|
1253
1293
|
children: [
|
|
1254
|
-
/* @__PURE__ */
|
|
1255
|
-
/* @__PURE__ */
|
|
1256
|
-
/* @__PURE__ */
|
|
1294
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
|
|
1295
|
+
/* @__PURE__ */ jsxs18("div", { className: "min-w-0", children: [
|
|
1296
|
+
/* @__PURE__ */ jsx28("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs18(
|
|
1257
1297
|
"button",
|
|
1258
1298
|
{
|
|
1259
1299
|
type: "button",
|
|
@@ -1262,31 +1302,31 @@ function ReportFeature({ feature }) {
|
|
|
1262
1302
|
"aria-controls": bodyId,
|
|
1263
1303
|
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1264
1304
|
children: [
|
|
1265
|
-
/* @__PURE__ */
|
|
1266
|
-
/* @__PURE__ */
|
|
1305
|
+
/* @__PURE__ */ jsx28("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
|
|
1306
|
+
/* @__PURE__ */ jsx28("span", { className: "truncate", children: feature.title })
|
|
1267
1307
|
]
|
|
1268
1308
|
}
|
|
1269
1309
|
) : feature.title }),
|
|
1270
|
-
/* @__PURE__ */
|
|
1310
|
+
/* @__PURE__ */ jsx28("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
|
|
1271
1311
|
] }),
|
|
1272
|
-
/* @__PURE__ */
|
|
1312
|
+
/* @__PURE__ */ jsxs18(
|
|
1273
1313
|
"p",
|
|
1274
1314
|
{
|
|
1275
1315
|
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1276
1316
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1277
1317
|
children: [
|
|
1278
|
-
/* @__PURE__ */
|
|
1318
|
+
/* @__PURE__ */ jsxs18("span", { children: [
|
|
1279
1319
|
s.passed,
|
|
1280
1320
|
" passed"
|
|
1281
1321
|
] }),
|
|
1282
|
-
/* @__PURE__ */
|
|
1283
|
-
/* @__PURE__ */
|
|
1322
|
+
/* @__PURE__ */ jsx28("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1323
|
+
/* @__PURE__ */ jsxs18("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
|
|
1284
1324
|
s.failed,
|
|
1285
1325
|
" failed"
|
|
1286
1326
|
] }),
|
|
1287
|
-
skipped > 0 ? /* @__PURE__ */
|
|
1288
|
-
/* @__PURE__ */
|
|
1289
|
-
/* @__PURE__ */
|
|
1327
|
+
skipped > 0 ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
1328
|
+
/* @__PURE__ */ jsx28("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1329
|
+
/* @__PURE__ */ jsxs18("span", { children: [
|
|
1290
1330
|
skipped,
|
|
1291
1331
|
" skipped"
|
|
1292
1332
|
] })
|
|
@@ -1295,7 +1335,7 @@ function ReportFeature({ feature }) {
|
|
|
1295
1335
|
}
|
|
1296
1336
|
)
|
|
1297
1337
|
] }),
|
|
1298
|
-
/* @__PURE__ */
|
|
1338
|
+
/* @__PURE__ */ jsx28("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx28(ReportScenarioList, { feature }) })
|
|
1299
1339
|
]
|
|
1300
1340
|
}
|
|
1301
1341
|
)
|
|
@@ -1303,17 +1343,17 @@ function ReportFeature({ feature }) {
|
|
|
1303
1343
|
}
|
|
1304
1344
|
|
|
1305
1345
|
// src/components/ReportFeatureList.tsx
|
|
1306
|
-
import { jsx as
|
|
1346
|
+
import { jsx as jsx29 } from "react/jsx-runtime";
|
|
1307
1347
|
function ReportFeatureList() {
|
|
1308
1348
|
const report = useReport();
|
|
1309
|
-
return /* @__PURE__ */
|
|
1349
|
+
return /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx29(ReportFeature, { feature }, feature.id)) });
|
|
1310
1350
|
}
|
|
1311
1351
|
|
|
1312
1352
|
// src/components/ui/empty.tsx
|
|
1313
1353
|
import { cva as cva2 } from "class-variance-authority";
|
|
1314
|
-
import { jsx as
|
|
1354
|
+
import { jsx as jsx30 } from "react/jsx-runtime";
|
|
1315
1355
|
function Empty({ className, ...props }) {
|
|
1316
|
-
return /* @__PURE__ */
|
|
1356
|
+
return /* @__PURE__ */ jsx30(
|
|
1317
1357
|
"div",
|
|
1318
1358
|
{
|
|
1319
1359
|
"data-slot": "empty",
|
|
@@ -1326,7 +1366,7 @@ function Empty({ className, ...props }) {
|
|
|
1326
1366
|
);
|
|
1327
1367
|
}
|
|
1328
1368
|
function EmptyHeader({ className, ...props }) {
|
|
1329
|
-
return /* @__PURE__ */
|
|
1369
|
+
return /* @__PURE__ */ jsx30(
|
|
1330
1370
|
"div",
|
|
1331
1371
|
{
|
|
1332
1372
|
"data-slot": "empty-header",
|
|
@@ -1353,7 +1393,7 @@ var emptyMediaVariants = cva2(
|
|
|
1353
1393
|
}
|
|
1354
1394
|
);
|
|
1355
1395
|
function EmptyTitle({ className, ...props }) {
|
|
1356
|
-
return /* @__PURE__ */
|
|
1396
|
+
return /* @__PURE__ */ jsx30(
|
|
1357
1397
|
"div",
|
|
1358
1398
|
{
|
|
1359
1399
|
"data-slot": "empty-title",
|
|
@@ -1364,16 +1404,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1364
1404
|
}
|
|
1365
1405
|
|
|
1366
1406
|
// src/components/ReportEmpty.tsx
|
|
1367
|
-
import { jsx as
|
|
1407
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1368
1408
|
function ReportEmpty({ message }) {
|
|
1369
|
-
return /* @__PURE__ */
|
|
1409
|
+
return /* @__PURE__ */ jsx31(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx31(EmptyHeader, { children: /* @__PURE__ */ jsx31(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1370
1410
|
}
|
|
1371
1411
|
|
|
1372
1412
|
// src/components/ReportSummary.tsx
|
|
1373
|
-
import { jsx as
|
|
1413
|
+
import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1374
1414
|
function ReportSummary({ className }) {
|
|
1375
1415
|
const report = useReport();
|
|
1376
|
-
return /* @__PURE__ */
|
|
1416
|
+
return /* @__PURE__ */ jsx32(
|
|
1377
1417
|
ReportSummaryView,
|
|
1378
1418
|
{
|
|
1379
1419
|
summary: report.summary,
|
|
@@ -1399,7 +1439,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1399
1439
|
if (summary.pending > 0) {
|
|
1400
1440
|
cards.push({ status: "pending", label: "Pending", value: summary.pending });
|
|
1401
1441
|
}
|
|
1402
|
-
return /* @__PURE__ */
|
|
1442
|
+
return /* @__PURE__ */ jsx32(
|
|
1403
1443
|
"dl",
|
|
1404
1444
|
{
|
|
1405
1445
|
className: cn(
|
|
@@ -1412,17 +1452,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1412
1452
|
"aria-label": ariaLabel,
|
|
1413
1453
|
children: cards.map((c) => {
|
|
1414
1454
|
const tone = TONE[c.status];
|
|
1415
|
-
return /* @__PURE__ */
|
|
1455
|
+
return /* @__PURE__ */ jsxs19(
|
|
1416
1456
|
"div",
|
|
1417
1457
|
{
|
|
1418
1458
|
"data-status": c.status,
|
|
1419
1459
|
className: "rounded-lg border bg-card px-4 py-3",
|
|
1420
1460
|
children: [
|
|
1421
|
-
/* @__PURE__ */
|
|
1422
|
-
/* @__PURE__ */
|
|
1461
|
+
/* @__PURE__ */ jsxs19("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
1462
|
+
/* @__PURE__ */ jsx32("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
|
|
1423
1463
|
c.label
|
|
1424
1464
|
] }),
|
|
1425
|
-
/* @__PURE__ */
|
|
1465
|
+
/* @__PURE__ */ jsx32("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
|
|
1426
1466
|
]
|
|
1427
1467
|
},
|
|
1428
1468
|
c.status
|
|
@@ -1432,6 +1472,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1432
1472
|
);
|
|
1433
1473
|
}
|
|
1434
1474
|
|
|
1475
|
+
// src/components/ReportMeta.tsx
|
|
1476
|
+
import { formatDuration as formatDuration4 } from "executable-stories-core/utils/duration";
|
|
1477
|
+
|
|
1435
1478
|
// src/lib/provenance.ts
|
|
1436
1479
|
var CI_DISPLAY_NAMES = {
|
|
1437
1480
|
github: "GitHub Actions",
|
|
@@ -1463,7 +1506,7 @@ function prUrl(ci) {
|
|
|
1463
1506
|
}
|
|
1464
1507
|
|
|
1465
1508
|
// src/components/ReportMeta.tsx
|
|
1466
|
-
import { jsx as
|
|
1509
|
+
import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1467
1510
|
function ReportMeta() {
|
|
1468
1511
|
const report = useReport();
|
|
1469
1512
|
const items = [];
|
|
@@ -1472,7 +1515,7 @@ function ReportMeta() {
|
|
|
1472
1515
|
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1473
1516
|
}
|
|
1474
1517
|
if (report.durationMs) {
|
|
1475
|
-
items.push({ label: "Duration", value:
|
|
1518
|
+
items.push({ label: "Duration", value: formatDuration4(report.durationMs) });
|
|
1476
1519
|
}
|
|
1477
1520
|
if (report.packageVersion) {
|
|
1478
1521
|
items.push({ label: "Version", value: report.packageVersion });
|
|
@@ -1486,13 +1529,13 @@ function ReportMeta() {
|
|
|
1486
1529
|
const short = sha.slice(0, 8);
|
|
1487
1530
|
items.push({
|
|
1488
1531
|
label: "Commit",
|
|
1489
|
-
value: url ? /* @__PURE__ */
|
|
1532
|
+
value: url ? /* @__PURE__ */ jsx33("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1490
1533
|
});
|
|
1491
1534
|
}
|
|
1492
1535
|
if (report.ci) {
|
|
1493
1536
|
items.push({
|
|
1494
1537
|
label: "CI",
|
|
1495
|
-
value: report.ci.url ? /* @__PURE__ */
|
|
1538
|
+
value: report.ci.url ? /* @__PURE__ */ jsxs20("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1496
1539
|
ciDisplayName(report.ci.name),
|
|
1497
1540
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1498
1541
|
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
@@ -1503,7 +1546,7 @@ function ReportMeta() {
|
|
|
1503
1546
|
const label = `#${report.ci.prNumber}`;
|
|
1504
1547
|
items.push({
|
|
1505
1548
|
label: "PR",
|
|
1506
|
-
value: url ? /* @__PURE__ */
|
|
1549
|
+
value: url ? /* @__PURE__ */ jsx33("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1507
1550
|
});
|
|
1508
1551
|
}
|
|
1509
1552
|
if (items.length === 0) return null;
|
|
@@ -1511,48 +1554,48 @@ function ReportMeta() {
|
|
|
1511
1554
|
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1512
1555
|
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1513
1556
|
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1514
|
-
/* @__PURE__ */
|
|
1515
|
-
/* @__PURE__ */
|
|
1516
|
-
/* @__PURE__ */
|
|
1557
|
+
/* @__PURE__ */ jsxs20("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1558
|
+
/* @__PURE__ */ jsxs20("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1559
|
+
/* @__PURE__ */ jsx33("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1517
1560
|
"Run details"
|
|
1518
1561
|
] }),
|
|
1519
|
-
/* @__PURE__ */
|
|
1520
|
-
/* @__PURE__ */
|
|
1521
|
-
/* @__PURE__ */
|
|
1562
|
+
/* @__PURE__ */ jsx33("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ jsxs20("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1563
|
+
/* @__PURE__ */ jsx33("dt", { className: "font-medium", children: item.label }),
|
|
1564
|
+
/* @__PURE__ */ jsx33("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1522
1565
|
] }, item.label)) })
|
|
1523
1566
|
] })
|
|
1524
1567
|
);
|
|
1525
1568
|
}
|
|
1526
1569
|
|
|
1527
1570
|
// src/components/ReportSchemaError.tsx
|
|
1528
|
-
import { jsx as
|
|
1571
|
+
import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1529
1572
|
function ReportSchemaError({ error }) {
|
|
1530
|
-
return /* @__PURE__ */
|
|
1531
|
-
/* @__PURE__ */
|
|
1532
|
-
/* @__PURE__ */
|
|
1533
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */
|
|
1573
|
+
return /* @__PURE__ */ jsxs21("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1574
|
+
/* @__PURE__ */ jsx34("p", { children: /* @__PURE__ */ jsx34("strong", { children: "Report could not be displayed." }) }),
|
|
1575
|
+
/* @__PURE__ */ jsx34("p", { children: error.message }),
|
|
1576
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs21("p", { children: [
|
|
1534
1577
|
"The report bundle is newer than this version of ",
|
|
1535
|
-
/* @__PURE__ */
|
|
1578
|
+
/* @__PURE__ */ jsx34("code", { children: "executable-stories-react" }),
|
|
1536
1579
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1537
1580
|
] }) : null,
|
|
1538
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */
|
|
1539
|
-
/* @__PURE__ */
|
|
1581
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs21("details", { children: [
|
|
1582
|
+
/* @__PURE__ */ jsxs21("summary", { children: [
|
|
1540
1583
|
error.issues.length,
|
|
1541
1584
|
" validation issue",
|
|
1542
1585
|
error.issues.length === 1 ? "" : "s"
|
|
1543
1586
|
] }),
|
|
1544
|
-
/* @__PURE__ */
|
|
1587
|
+
/* @__PURE__ */ jsx34("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1545
1588
|
] }) : null
|
|
1546
1589
|
] });
|
|
1547
1590
|
}
|
|
1548
1591
|
|
|
1549
1592
|
// src/components/ReportShell.tsx
|
|
1550
|
-
import { Fragment as Fragment10, jsx as
|
|
1593
|
+
import { Fragment as Fragment10, jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1551
1594
|
function ReportTitleBlock({ title }) {
|
|
1552
|
-
return /* @__PURE__ */
|
|
1553
|
-
/* @__PURE__ */
|
|
1554
|
-
/* @__PURE__ */
|
|
1555
|
-
/* @__PURE__ */
|
|
1595
|
+
return /* @__PURE__ */ jsxs22(Fragment10, { children: [
|
|
1596
|
+
/* @__PURE__ */ jsx35("h1", { children: title ?? "Story Report" }),
|
|
1597
|
+
/* @__PURE__ */ jsx35(ReportSummary, {}),
|
|
1598
|
+
/* @__PURE__ */ jsx35(ReportMeta, {})
|
|
1556
1599
|
] });
|
|
1557
1600
|
}
|
|
1558
1601
|
function ReportErrorShell({
|
|
@@ -1561,22 +1604,22 @@ function ReportErrorShell({
|
|
|
1561
1604
|
title,
|
|
1562
1605
|
dataTheme
|
|
1563
1606
|
}) {
|
|
1564
|
-
return /* @__PURE__ */
|
|
1607
|
+
return /* @__PURE__ */ jsx35("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx35(ReportSchemaError, { error }) });
|
|
1565
1608
|
}
|
|
1566
1609
|
|
|
1567
1610
|
// src/components/Report.tsx
|
|
1568
|
-
import { jsx as
|
|
1611
|
+
import { jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1569
1612
|
function Report(props) {
|
|
1570
1613
|
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1571
1614
|
const result = unwrapReport(report);
|
|
1572
1615
|
if (!result.ok) {
|
|
1573
|
-
return /* @__PURE__ */
|
|
1616
|
+
return /* @__PURE__ */ jsx36(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1574
1617
|
}
|
|
1575
1618
|
const data = result.data;
|
|
1576
1619
|
const hasContent = data.features.length > 0;
|
|
1577
|
-
return /* @__PURE__ */
|
|
1578
|
-
/* @__PURE__ */
|
|
1579
|
-
hasContent ? /* @__PURE__ */
|
|
1620
|
+
return /* @__PURE__ */ jsx36(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs23("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
|
|
1621
|
+
/* @__PURE__ */ jsx36("header", { className: "es-report-header", children: /* @__PURE__ */ jsx36(ReportTitleBlock, { title }) }),
|
|
1622
|
+
hasContent ? /* @__PURE__ */ jsx36(ReportFeatureList, {}) : /* @__PURE__ */ jsx36(ReportEmpty, {})
|
|
1580
1623
|
] }) });
|
|
1581
1624
|
}
|
|
1582
1625
|
|