executable-stories-react 0.1.15 → 0.2.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 +1743 -371
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +136 -14
- package/dist/index.d.ts +136 -14
- package/dist/index.js +1724 -370
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +1461 -265
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +55 -4
- package/dist/interactive.d.ts +55 -4
- package/dist/interactive.js +1450 -255
- package/dist/interactive.js.map +1 -1
- package/dist/parse.cjs +631 -154
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +12 -4
- package/dist/parse.d.ts +12 -4
- package/dist/parse.js +631 -154
- package/dist/parse.js.map +1 -1
- package/dist/report-island.global.js +132 -0
- package/dist/ssr.cjs +1211 -0
- package/dist/ssr.cjs.map +1 -0
- package/dist/ssr.d.cts +72 -0
- package/dist/ssr.d.ts +72 -0
- package/dist/ssr.js +1186 -0
- package/dist/ssr.js.map +1 -0
- package/dist/styles.css +21 -6
- package/dist/tailwind.css +2 -0
- package/package.json +43 -8
package/dist/ssr.cjs
ADDED
|
@@ -0,0 +1,1211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ssr-entry.ts
|
|
21
|
+
var ssr_entry_exports = {};
|
|
22
|
+
__export(ssr_entry_exports, {
|
|
23
|
+
renderReportToHtml: () => renderReportToHtml
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(ssr_entry_exports);
|
|
26
|
+
var import_react7 = require("react");
|
|
27
|
+
var import_server = require("react-dom/server");
|
|
28
|
+
|
|
29
|
+
// src/result.ts
|
|
30
|
+
var ok = (data) => ({ ok: true, data });
|
|
31
|
+
function unwrapReport(value) {
|
|
32
|
+
if (typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean") {
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
return ok(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/context/ReportRoot.tsx
|
|
39
|
+
var import_react2 = require("react");
|
|
40
|
+
|
|
41
|
+
// src/context/ReportContext.ts
|
|
42
|
+
var import_react = require("react");
|
|
43
|
+
var ReportContext = (0, import_react.createContext)(null);
|
|
44
|
+
|
|
45
|
+
// src/context/ReportRoot.tsx
|
|
46
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
47
|
+
var EMPTY_CUSTOM = {};
|
|
48
|
+
var EMPTY_RENDERERS = {};
|
|
49
|
+
function ReportRoot({
|
|
50
|
+
report,
|
|
51
|
+
customRenderers,
|
|
52
|
+
renderers,
|
|
53
|
+
children
|
|
54
|
+
}) {
|
|
55
|
+
const value = (0, import_react2.useMemo)(
|
|
56
|
+
() => ({
|
|
57
|
+
report,
|
|
58
|
+
customRenderers: customRenderers ?? EMPTY_CUSTOM,
|
|
59
|
+
renderers: renderers ?? EMPTY_RENDERERS
|
|
60
|
+
}),
|
|
61
|
+
[report, customRenderers, renderers]
|
|
62
|
+
);
|
|
63
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReportContext.Provider, { value, children });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/hooks/useReport.ts
|
|
67
|
+
var import_react3 = require("react");
|
|
68
|
+
function useReport() {
|
|
69
|
+
const ctx = (0, import_react3.useContext)(ReportContext);
|
|
70
|
+
if (!ctx) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return ctx.report;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/components/ReportScenario.tsx
|
|
79
|
+
var import_duration3 = require("executable-stories-core/utils/duration");
|
|
80
|
+
|
|
81
|
+
// src/components/ReportSteps.tsx
|
|
82
|
+
var import_duration = require("executable-stories-core/utils/duration");
|
|
83
|
+
|
|
84
|
+
// src/components/doc/DocNote.tsx
|
|
85
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
86
|
+
function DocNote({ entry }) {
|
|
87
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/components/ui/badge.tsx
|
|
91
|
+
var React = require("react");
|
|
92
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
93
|
+
var import_use_render = require("@base-ui/react/use-render");
|
|
94
|
+
|
|
95
|
+
// src/lib/utils.ts
|
|
96
|
+
var import_clsx = require("clsx");
|
|
97
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
98
|
+
function cn(...inputs) {
|
|
99
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/components/ui/badge.tsx
|
|
103
|
+
var badgeVariants = (0, import_class_variance_authority.cva)(
|
|
104
|
+
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
|
|
105
|
+
{
|
|
106
|
+
variants: {
|
|
107
|
+
variant: {
|
|
108
|
+
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
109
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
110
|
+
destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
|
|
111
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
112
|
+
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
113
|
+
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
114
|
+
passed: "border-pass-border bg-pass-bg text-pass font-mono uppercase",
|
|
115
|
+
failed: "border-fail-border bg-fail-bg text-fail font-mono uppercase",
|
|
116
|
+
skipped: "border-skip-border bg-skip-bg text-skip font-mono uppercase",
|
|
117
|
+
pending: "border-pend-border bg-pend-bg text-pend font-mono uppercase",
|
|
118
|
+
tag: "border-pass-border bg-pass-bg text-pass font-mono font-medium"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
defaultVariants: {
|
|
122
|
+
variant: "default"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
function Badge({
|
|
127
|
+
className,
|
|
128
|
+
variant = "default",
|
|
129
|
+
render,
|
|
130
|
+
...props
|
|
131
|
+
}) {
|
|
132
|
+
return (0, import_use_render.useRender)({
|
|
133
|
+
defaultTagName: "span",
|
|
134
|
+
render,
|
|
135
|
+
props: {
|
|
136
|
+
"data-slot": "badge",
|
|
137
|
+
"data-variant": variant,
|
|
138
|
+
className: cn(badgeVariants({ variant }), className),
|
|
139
|
+
...props
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/components/doc/DocTag.tsx
|
|
145
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
146
|
+
function DocTag({ entry }) {
|
|
147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Badge, { variant: "tag", children: n }) }, n)) });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/components/doc/DocKv.tsx
|
|
151
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
152
|
+
function formatValue(value) {
|
|
153
|
+
if (value === null) return "null";
|
|
154
|
+
if (typeof value === "string") return value;
|
|
155
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
156
|
+
try {
|
|
157
|
+
return JSON.stringify(value);
|
|
158
|
+
} catch {
|
|
159
|
+
return String(value);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function DocKv({ entry }) {
|
|
163
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
|
|
164
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
|
|
165
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/hooks/useRenderers.ts
|
|
170
|
+
var import_react4 = require("react");
|
|
171
|
+
var EMPTY_CUSTOM2 = {};
|
|
172
|
+
var EMPTY_RENDERERS2 = {};
|
|
173
|
+
function useCustomRenderers() {
|
|
174
|
+
const ctx = (0, import_react4.useContext)(ReportContext);
|
|
175
|
+
return ctx?.customRenderers ?? EMPTY_CUSTOM2;
|
|
176
|
+
}
|
|
177
|
+
function useBuiltinRenderers() {
|
|
178
|
+
const ctx = (0, import_react4.useContext)(ReportContext);
|
|
179
|
+
return ctx?.renderers ?? EMPTY_RENDERERS2;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/components/doc/DocCode.tsx
|
|
183
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
184
|
+
function CodeFigure({ label, children }) {
|
|
185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("figure", { className: "my-3", children: [
|
|
186
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
|
|
187
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
|
|
188
|
+
] });
|
|
189
|
+
}
|
|
190
|
+
function DocCode({ entry }) {
|
|
191
|
+
const renderers = useBuiltinRenderers();
|
|
192
|
+
if (renderers.code) {
|
|
193
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: renderers.code(entry) });
|
|
194
|
+
}
|
|
195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CodeFigure, { label: entry.label, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/components/doc/DocTable.tsx
|
|
199
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
200
|
+
function DocTable({ entry }) {
|
|
201
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("figure", { className: "my-3", children: [
|
|
202
|
+
entry.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
|
|
204
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
|
|
206
|
+
] }) })
|
|
207
|
+
] });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/components/doc/DocLink.tsx
|
|
211
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
212
|
+
function DocLink({ entry }) {
|
|
213
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
214
|
+
"a",
|
|
215
|
+
{
|
|
216
|
+
className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
|
|
217
|
+
href: entry.url,
|
|
218
|
+
rel: "noreferrer noopener",
|
|
219
|
+
target: "_blank",
|
|
220
|
+
children: entry.label
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/components/doc/DocSection.tsx
|
|
226
|
+
var import_marked = require("marked");
|
|
227
|
+
|
|
228
|
+
// src/lib/url.ts
|
|
229
|
+
function safeUrl(value) {
|
|
230
|
+
if (!value) return void 0;
|
|
231
|
+
const trimmed = value.trim();
|
|
232
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
|
|
233
|
+
if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
|
|
234
|
+
return trimmed;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/components/doc/DocSection.tsx
|
|
238
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
239
|
+
function neutralizeUrl(value) {
|
|
240
|
+
return safeUrl(value) ?? "#";
|
|
241
|
+
}
|
|
242
|
+
function safeMarkdownHtml(markdown) {
|
|
243
|
+
const raw = import_marked.marked.parse(markdown, { async: false });
|
|
244
|
+
return raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<\/?(?:iframe|object|embed|form|base)\b[^>]*>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, "").replace(/(href|src)\s*=\s*"([^"]*)"/gi, (_m, attr, val) => `${attr}="${neutralizeUrl(val)}"`).replace(/(href|src)\s*=\s*'([^']*)'/gi, (_m, attr, val) => `${attr}='${neutralizeUrl(val)}'`);
|
|
245
|
+
}
|
|
246
|
+
function DocSection({ entry }) {
|
|
247
|
+
const renderers = useBuiltinRenderers();
|
|
248
|
+
if (renderers.section) {
|
|
249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: renderers.section(entry) });
|
|
250
|
+
}
|
|
251
|
+
const html = safeMarkdownHtml(entry.markdown);
|
|
252
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
|
|
253
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
|
|
254
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
255
|
+
"div",
|
|
256
|
+
{
|
|
257
|
+
className: "es-doc-prose text-muted-foreground",
|
|
258
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
] });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/components/doc/DocMermaid.tsx
|
|
265
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
266
|
+
function MermaidSource({ entry, ref }) {
|
|
267
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
|
|
268
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
269
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
270
|
+
"pre",
|
|
271
|
+
{
|
|
272
|
+
"data-mermaid": true,
|
|
273
|
+
tabIndex: 0,
|
|
274
|
+
className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
|
|
275
|
+
children: entry.code
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
] });
|
|
279
|
+
}
|
|
280
|
+
function DocMermaid({ entry }) {
|
|
281
|
+
const renderers = useBuiltinRenderers();
|
|
282
|
+
if (renderers.mermaid) {
|
|
283
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: renderers.mermaid(entry) });
|
|
284
|
+
}
|
|
285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MermaidSource, { entry });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/components/doc/DocScreenshot.tsx
|
|
289
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
290
|
+
function DocScreenshot({ entry }) {
|
|
291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3", children: [
|
|
292
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
293
|
+
"img",
|
|
294
|
+
{
|
|
295
|
+
src: entry.path,
|
|
296
|
+
alt: entry.alt ?? "",
|
|
297
|
+
loading: "lazy",
|
|
298
|
+
className: "max-w-full rounded-md border border-border"
|
|
299
|
+
}
|
|
300
|
+
),
|
|
301
|
+
entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
|
|
302
|
+
] });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/components/doc/DocVideo.tsx
|
|
306
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
307
|
+
function DocVideo({ entry }) {
|
|
308
|
+
const src = safeUrl(entry.path);
|
|
309
|
+
const poster = safeUrl(entry.poster);
|
|
310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3", children: [
|
|
311
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
312
|
+
"video",
|
|
313
|
+
{
|
|
314
|
+
controls: true,
|
|
315
|
+
preload: "metadata",
|
|
316
|
+
...src ? { src } : {},
|
|
317
|
+
...poster ? { poster } : {},
|
|
318
|
+
className: "max-w-full rounded-md border border-border"
|
|
319
|
+
}
|
|
320
|
+
),
|
|
321
|
+
entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
|
|
322
|
+
] });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/components/doc/DocHtml.tsx
|
|
326
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
327
|
+
function DocHtml({ entry }) {
|
|
328
|
+
const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
|
|
329
|
+
const title = entry.title ?? "Embedded HTML";
|
|
330
|
+
const safeHref = safeUrl(entry.url ?? entry.path);
|
|
331
|
+
const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
|
|
332
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
|
|
333
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("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: [
|
|
334
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: entry.title ?? "HTML" }),
|
|
335
|
+
safeHref ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
336
|
+
"a",
|
|
337
|
+
{
|
|
338
|
+
href: safeHref,
|
|
339
|
+
target: "_blank",
|
|
340
|
+
rel: "noreferrer noopener",
|
|
341
|
+
"aria-label": "Open in new tab",
|
|
342
|
+
title: "Open in new tab",
|
|
343
|
+
className: "text-primary",
|
|
344
|
+
children: "\u2197"
|
|
345
|
+
}
|
|
346
|
+
) : null
|
|
347
|
+
] }),
|
|
348
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
349
|
+
"iframe",
|
|
350
|
+
{
|
|
351
|
+
...frameProps,
|
|
352
|
+
sandbox: "allow-scripts",
|
|
353
|
+
loading: "lazy",
|
|
354
|
+
title,
|
|
355
|
+
className: "block w-full border-0",
|
|
356
|
+
style: { height }
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
] });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/components/doc/DocCustom.tsx
|
|
363
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
364
|
+
function DocCustom({ entry }) {
|
|
365
|
+
const renderers = useCustomRenderers();
|
|
366
|
+
const renderer = renderers[entry.type];
|
|
367
|
+
if (renderer) {
|
|
368
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: renderer(entry) });
|
|
369
|
+
}
|
|
370
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
|
|
371
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
|
|
372
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
|
|
373
|
+
] });
|
|
374
|
+
}
|
|
375
|
+
function safeStringify(value) {
|
|
376
|
+
try {
|
|
377
|
+
return JSON.stringify(value, null, 2);
|
|
378
|
+
} catch {
|
|
379
|
+
return String(value);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/components/doc/DocEntry.tsx
|
|
384
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
385
|
+
function DocEntry({ entry }) {
|
|
386
|
+
switch (entry.kind) {
|
|
387
|
+
case "note":
|
|
388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocNote, { entry });
|
|
389
|
+
case "tag":
|
|
390
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocTag, { entry });
|
|
391
|
+
case "kv":
|
|
392
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocKv, { entry });
|
|
393
|
+
case "code":
|
|
394
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocCode, { entry });
|
|
395
|
+
case "table":
|
|
396
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocTable, { entry });
|
|
397
|
+
case "link":
|
|
398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocLink, { entry });
|
|
399
|
+
case "section":
|
|
400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocSection, { entry });
|
|
401
|
+
case "mermaid":
|
|
402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocMermaid, { entry });
|
|
403
|
+
case "screenshot":
|
|
404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocScreenshot, { entry });
|
|
405
|
+
case "video":
|
|
406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocVideo, { entry });
|
|
407
|
+
case "html":
|
|
408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocHtml, { entry });
|
|
409
|
+
case "custom":
|
|
410
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocCustom, { entry });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// src/components/ReportDocEntries.tsx
|
|
415
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
416
|
+
function ReportDocEntries({ entries }) {
|
|
417
|
+
if (entries.length === 0) return null;
|
|
418
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocEntry, { entry }, i)) });
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/lib/step-params.tsx
|
|
422
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
423
|
+
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
|
|
424
|
+
function highlightStepParams(text) {
|
|
425
|
+
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
426
|
+
if (matches.length === 0) return text;
|
|
427
|
+
const nodes = [];
|
|
428
|
+
let lastIndex = 0;
|
|
429
|
+
matches.forEach((match, i) => {
|
|
430
|
+
const start = match.index;
|
|
431
|
+
const end = start + match[0].length;
|
|
432
|
+
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
433
|
+
nodes.push(
|
|
434
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
|
|
435
|
+
);
|
|
436
|
+
lastIndex = end;
|
|
437
|
+
});
|
|
438
|
+
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
439
|
+
return nodes;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/components/ReportSteps.tsx
|
|
443
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
444
|
+
var STEP_GLYPH = {
|
|
445
|
+
passed: "\u2713",
|
|
446
|
+
failed: "\u2717",
|
|
447
|
+
skipped: "\u25CB",
|
|
448
|
+
pending: "\u25CB"
|
|
449
|
+
};
|
|
450
|
+
var STEP_GLYPH_COLOR = {
|
|
451
|
+
passed: "text-pass",
|
|
452
|
+
failed: "text-fail",
|
|
453
|
+
skipped: "text-skip",
|
|
454
|
+
pending: "text-pend"
|
|
455
|
+
};
|
|
456
|
+
function ReportSteps({ scenario }) {
|
|
457
|
+
if (scenario.steps.length === 0) return null;
|
|
458
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReportStepItem, { step }, step.id)) });
|
|
459
|
+
}
|
|
460
|
+
function ReportStepItem({ step }) {
|
|
461
|
+
const kw = step.keyword.toLowerCase();
|
|
462
|
+
const continuation = kw === "and" || kw === "but";
|
|
463
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
464
|
+
"li",
|
|
465
|
+
{
|
|
466
|
+
id: step.id,
|
|
467
|
+
"data-status": step.status,
|
|
468
|
+
className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
|
|
469
|
+
children: [
|
|
470
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
471
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("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" }),
|
|
472
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
473
|
+
"span",
|
|
474
|
+
{
|
|
475
|
+
className: cn(
|
|
476
|
+
"min-w-[52px] shrink-0 font-mono text-xs font-semibold capitalize",
|
|
477
|
+
continuation ? "font-medium text-muted-foreground" : "text-keyword"
|
|
478
|
+
),
|
|
479
|
+
children: step.keyword
|
|
480
|
+
}
|
|
481
|
+
),
|
|
482
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
483
|
+
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration.formatDuration)(step.durationMs) }) : null
|
|
484
|
+
] }),
|
|
485
|
+
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
486
|
+
"pre",
|
|
487
|
+
{
|
|
488
|
+
role: "alert",
|
|
489
|
+
className: "mt-2.5 ml-6 overflow-x-auto rounded-md border border-fail-border bg-fail-bg px-4 py-3.5 font-mono text-xs leading-relaxed whitespace-pre-wrap text-fail",
|
|
490
|
+
children: step.errorMessage
|
|
491
|
+
}
|
|
492
|
+
) : null,
|
|
493
|
+
step.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReportDocEntries, { entries: step.docEntries }) }) : null
|
|
494
|
+
]
|
|
495
|
+
}
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/components/ReportAttachments.tsx
|
|
500
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
501
|
+
function ReportAttachments({ attachments }) {
|
|
502
|
+
if (attachments.length === 0) return null;
|
|
503
|
+
return (
|
|
504
|
+
// role="group" (not a labelled <section>) so multiple scenarios' attachment
|
|
505
|
+
// blocks don't create duplicate "Attachments" region landmarks.
|
|
506
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
|
|
507
|
+
const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
|
|
508
|
+
const isImage = att.mediaType.startsWith("image/");
|
|
509
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
|
|
510
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
511
|
+
"img",
|
|
512
|
+
{
|
|
513
|
+
src: dataUri,
|
|
514
|
+
alt: att.name,
|
|
515
|
+
loading: "lazy",
|
|
516
|
+
className: "max-w-full rounded-md border border-border"
|
|
517
|
+
}
|
|
518
|
+
),
|
|
519
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
|
|
520
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
521
|
+
"a",
|
|
522
|
+
{
|
|
523
|
+
href: dataUri,
|
|
524
|
+
download: att.name,
|
|
525
|
+
className: "text-xs text-link underline underline-offset-2",
|
|
526
|
+
children: [
|
|
527
|
+
att.name,
|
|
528
|
+
" ",
|
|
529
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
530
|
+
"(",
|
|
531
|
+
att.mediaType,
|
|
532
|
+
")"
|
|
533
|
+
] })
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
) }, `${att.name}-${i}`);
|
|
537
|
+
}) })
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// src/components/ReportTrace.tsx
|
|
542
|
+
var import_duration2 = require("executable-stories-core/utils/duration");
|
|
543
|
+
|
|
544
|
+
// src/lib/trace.ts
|
|
545
|
+
var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
|
|
546
|
+
var safeStatus = (s) => VALID.has(s) ? s : "unset";
|
|
547
|
+
function normalizeSpans(spans) {
|
|
548
|
+
const out = [];
|
|
549
|
+
for (const span of spans) {
|
|
550
|
+
if (!span || typeof span.spanId !== "string" || typeof span.name !== "string") continue;
|
|
551
|
+
let startTimeMs;
|
|
552
|
+
let durationMs;
|
|
553
|
+
if (span.startTimeMs != null && span.durationMs != null) {
|
|
554
|
+
startTimeMs = span.startTimeMs;
|
|
555
|
+
durationMs = span.durationMs;
|
|
556
|
+
} else if (span.startTimeUnixNano != null && span.endTimeUnixNano != null) {
|
|
557
|
+
startTimeMs = span.startTimeUnixNano / 1e6;
|
|
558
|
+
durationMs = (span.endTimeUnixNano - span.startTimeUnixNano) / 1e6;
|
|
559
|
+
} else {
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
durationMs = Math.max(0, durationMs);
|
|
563
|
+
if (!isFinite(startTimeMs) || !isFinite(durationMs)) continue;
|
|
564
|
+
out.push({
|
|
565
|
+
spanId: span.spanId,
|
|
566
|
+
parentSpanId: span.parentSpanId,
|
|
567
|
+
name: span.name,
|
|
568
|
+
startTimeMs,
|
|
569
|
+
durationMs,
|
|
570
|
+
status: safeStatus(span.status),
|
|
571
|
+
statusMessage: span.statusMessage
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
return out;
|
|
575
|
+
}
|
|
576
|
+
function toTraceRows(spans) {
|
|
577
|
+
const normalized = normalizeSpans(spans);
|
|
578
|
+
if (normalized.length === 0) return [];
|
|
579
|
+
const byId = /* @__PURE__ */ new Map();
|
|
580
|
+
for (const span of normalized) {
|
|
581
|
+
let key = span.spanId;
|
|
582
|
+
let suffix = 2;
|
|
583
|
+
while (byId.has(key)) key = `${span.spanId}__dup${suffix++}`;
|
|
584
|
+
byId.set(key, { span: { ...span, spanId: key }, children: [], depth: 0 });
|
|
585
|
+
}
|
|
586
|
+
const roots = [];
|
|
587
|
+
for (const node of byId.values()) {
|
|
588
|
+
const parent = node.span.parentSpanId ? byId.get(node.span.parentSpanId) : void 0;
|
|
589
|
+
if (parent && parent !== node) parent.children.push(node);
|
|
590
|
+
else roots.push(node);
|
|
591
|
+
}
|
|
592
|
+
for (const node of byId.values()) node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
593
|
+
roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
594
|
+
const rows = [];
|
|
595
|
+
const seen = /* @__PURE__ */ new Set();
|
|
596
|
+
const walk = (node, depth) => {
|
|
597
|
+
if (seen.has(node.span.spanId)) return;
|
|
598
|
+
seen.add(node.span.spanId);
|
|
599
|
+
rows.push({ ...node.span, depth });
|
|
600
|
+
for (const child of node.children) walk(child, depth + 1);
|
|
601
|
+
};
|
|
602
|
+
for (const root of roots) walk(root, 0);
|
|
603
|
+
for (const node of byId.values()) {
|
|
604
|
+
if (!seen.has(node.span.spanId)) walk(node, 0);
|
|
605
|
+
}
|
|
606
|
+
return rows;
|
|
607
|
+
}
|
|
608
|
+
function traceWindow(rows) {
|
|
609
|
+
let min = Infinity;
|
|
610
|
+
let max = -Infinity;
|
|
611
|
+
for (const r of rows) {
|
|
612
|
+
min = Math.min(min, r.startTimeMs);
|
|
613
|
+
max = Math.max(max, r.startTimeMs + r.durationMs);
|
|
614
|
+
}
|
|
615
|
+
const total = max - min;
|
|
616
|
+
return { minStartMs: isFinite(min) ? min : 0, totalMs: total > 0 ? total : 1 };
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// src/components/ReportTrace.tsx
|
|
620
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
621
|
+
var BAR_COLOR = {
|
|
622
|
+
ok: "bg-pass",
|
|
623
|
+
error: "bg-fail",
|
|
624
|
+
unset: "bg-skip"
|
|
625
|
+
};
|
|
626
|
+
function ReportTrace({ spans }) {
|
|
627
|
+
if (!spans || spans.length === 0) return null;
|
|
628
|
+
const rows = toTraceRows(spans);
|
|
629
|
+
if (rows.length === 0) return null;
|
|
630
|
+
const { minStartMs, totalMs } = traceWindow(rows);
|
|
631
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
|
|
633
|
+
"Trace (",
|
|
634
|
+
rows.length,
|
|
635
|
+
" span",
|
|
636
|
+
rows.length === 1 ? "" : "s",
|
|
637
|
+
", ",
|
|
638
|
+
(0, import_duration2.formatDuration)(totalMs),
|
|
639
|
+
")"
|
|
640
|
+
] }),
|
|
641
|
+
/* @__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) => {
|
|
642
|
+
const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
|
|
643
|
+
const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
|
|
644
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("li", { className: "flex items-center gap-2", children: [
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
646
|
+
"span",
|
|
647
|
+
{
|
|
648
|
+
className: "min-w-0 flex-1 truncate text-foreground",
|
|
649
|
+
style: { paddingLeft: `${row.depth * 12}px` },
|
|
650
|
+
title: row.statusMessage ? `${row.name} \u2014 ${row.statusMessage}` : row.name,
|
|
651
|
+
children: row.name
|
|
652
|
+
}
|
|
653
|
+
),
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
655
|
+
"span",
|
|
656
|
+
{
|
|
657
|
+
className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
|
|
658
|
+
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
659
|
+
}
|
|
660
|
+
) }),
|
|
661
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: (0, import_duration2.formatDuration)(row.durationMs) })
|
|
662
|
+
] }, `${row.spanId}-${i}`);
|
|
663
|
+
}) })
|
|
664
|
+
] });
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// src/interactive/collapse-context.ts
|
|
668
|
+
var import_react5 = require("react");
|
|
669
|
+
var CollapseContext = (0, import_react5.createContext)(null);
|
|
670
|
+
var CollapseProvider = CollapseContext.Provider;
|
|
671
|
+
function useCollapse() {
|
|
672
|
+
return (0, import_react5.useContext)(CollapseContext);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// src/interactive/scenario-actions.tsx
|
|
676
|
+
var import_react6 = require("react");
|
|
677
|
+
var ScenarioActionsContext = (0, import_react6.createContext)(null);
|
|
678
|
+
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
679
|
+
function useScenarioActions() {
|
|
680
|
+
return (0, import_react6.useContext)(ScenarioActionsContext);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// src/components/ui/card.tsx
|
|
684
|
+
var React2 = require("react");
|
|
685
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
686
|
+
function Card({ className, ...props }) {
|
|
687
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
688
|
+
"div",
|
|
689
|
+
{
|
|
690
|
+
"data-slot": "card",
|
|
691
|
+
className: cn(
|
|
692
|
+
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
|
|
693
|
+
className
|
|
694
|
+
),
|
|
695
|
+
...props
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
function CardHeader({ className, ...props }) {
|
|
700
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
701
|
+
"div",
|
|
702
|
+
{
|
|
703
|
+
"data-slot": "card-header",
|
|
704
|
+
className: cn(
|
|
705
|
+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
706
|
+
className
|
|
707
|
+
),
|
|
708
|
+
...props
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
function CardContent({ className, ...props }) {
|
|
713
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
714
|
+
"div",
|
|
715
|
+
{
|
|
716
|
+
"data-slot": "card-content",
|
|
717
|
+
className: cn("px-6", className),
|
|
718
|
+
...props
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// src/components/ReportScenario.tsx
|
|
724
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
725
|
+
var STATUS_LABEL = {
|
|
726
|
+
passed: "Passed",
|
|
727
|
+
failed: "Failed",
|
|
728
|
+
skipped: "Skipped",
|
|
729
|
+
pending: "Pending"
|
|
730
|
+
};
|
|
731
|
+
var STATUS_GLYPH = {
|
|
732
|
+
passed: "\u2713",
|
|
733
|
+
failed: "\u2717",
|
|
734
|
+
skipped: "\u25CB",
|
|
735
|
+
pending: "\u25CB"
|
|
736
|
+
};
|
|
737
|
+
var STATUS_GLYPH_COLOR = {
|
|
738
|
+
passed: "text-pass",
|
|
739
|
+
failed: "text-fail",
|
|
740
|
+
skipped: "text-skip",
|
|
741
|
+
pending: "text-pend"
|
|
742
|
+
};
|
|
743
|
+
var STATUS_BADGE = {
|
|
744
|
+
passed: "passed",
|
|
745
|
+
failed: "failed",
|
|
746
|
+
skipped: "skipped",
|
|
747
|
+
pending: "pending"
|
|
748
|
+
};
|
|
749
|
+
function ReportScenario({ scenario, hideTitle = false }) {
|
|
750
|
+
const titleId = `${scenario.id}-title`;
|
|
751
|
+
const bodyId = `${scenario.id}-body`;
|
|
752
|
+
const label = STATUS_LABEL[scenario.status];
|
|
753
|
+
const collapse = useCollapse();
|
|
754
|
+
const collapsible = collapse !== null && !hideTitle;
|
|
755
|
+
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
756
|
+
const actions = useScenarioActions();
|
|
757
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
758
|
+
Card,
|
|
759
|
+
{
|
|
760
|
+
id: scenario.id,
|
|
761
|
+
"data-status": scenario.status,
|
|
762
|
+
...hideTitle ? { "aria-label": `${scenario.title} \u2014 ${label}` } : { "aria-labelledby": titleId },
|
|
763
|
+
className: "gap-0 py-0",
|
|
764
|
+
children: [
|
|
765
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(CardHeader, { className: "px-4 py-3", children: [
|
|
766
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
767
|
+
hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
768
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
769
|
+
"button",
|
|
770
|
+
{
|
|
771
|
+
type: "button",
|
|
772
|
+
onClick: () => collapse.toggle(scenario.id),
|
|
773
|
+
"aria-expanded": !collapsed,
|
|
774
|
+
"aria-controls": bodyId,
|
|
775
|
+
"aria-label": `Toggle ${scenario.title}`,
|
|
776
|
+
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
777
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
778
|
+
}
|
|
779
|
+
) : null,
|
|
780
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", STATUS_GLYPH_COLOR[scenario.status]), children: STATUS_GLYPH[scenario.status] }),
|
|
781
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: scenario.title })
|
|
782
|
+
] }),
|
|
783
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
784
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration3.formatDuration)(scenario.durationMs) }) : null,
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: STATUS_BADGE[scenario.status], "aria-label": `Status: ${label}`, children: label }),
|
|
786
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-0.5", children: [
|
|
787
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
788
|
+
"button",
|
|
789
|
+
{
|
|
790
|
+
type: "button",
|
|
791
|
+
onClick: () => actions.copyLink(scenario),
|
|
792
|
+
"aria-label": `Copy link to ${scenario.title}`,
|
|
793
|
+
title: "Copy permalink",
|
|
794
|
+
className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
795
|
+
children: "link"
|
|
796
|
+
}
|
|
797
|
+
),
|
|
798
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
799
|
+
"button",
|
|
800
|
+
{
|
|
801
|
+
type: "button",
|
|
802
|
+
onClick: () => actions.copyMarkdown(scenario),
|
|
803
|
+
"aria-label": `Copy ${scenario.title} as Markdown`,
|
|
804
|
+
title: "Copy as Markdown",
|
|
805
|
+
className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
806
|
+
children: "md"
|
|
807
|
+
}
|
|
808
|
+
),
|
|
809
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
810
|
+
"button",
|
|
811
|
+
{
|
|
812
|
+
type: "button",
|
|
813
|
+
onClick: () => actions.copyPrompt(scenario),
|
|
814
|
+
"aria-label": `Copy ${scenario.title} as an AI investigation prompt`,
|
|
815
|
+
title: "Copy as AI prompt",
|
|
816
|
+
className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
817
|
+
children: "ai"
|
|
818
|
+
}
|
|
819
|
+
)
|
|
820
|
+
] }) : null
|
|
821
|
+
] })
|
|
822
|
+
] }),
|
|
823
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
824
|
+
scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
825
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
826
|
+
] }) : null
|
|
827
|
+
] }),
|
|
828
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
829
|
+
scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
830
|
+
"pre",
|
|
831
|
+
{
|
|
832
|
+
role: "alert",
|
|
833
|
+
className: "mb-1 overflow-x-auto rounded-md border border-fail-border bg-fail-bg px-4 py-3.5 font-mono text-xs leading-relaxed whitespace-pre-wrap text-fail",
|
|
834
|
+
children: scenario.errorMessage
|
|
835
|
+
}
|
|
836
|
+
) : null,
|
|
837
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ReportSteps, { scenario }),
|
|
838
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ReportAttachments, { attachments: scenario.attachments }),
|
|
840
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ReportTrace, { spans: scenario.otelSpans })
|
|
841
|
+
] })
|
|
842
|
+
]
|
|
843
|
+
}
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// src/components/ReportScenarioList.tsx
|
|
848
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
849
|
+
function ReportScenarioList({ feature }) {
|
|
850
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_jsx_runtime22.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportScenario, { scenario }, scenario.id)) });
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// src/components/ReportFeature.tsx
|
|
854
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
855
|
+
function ReportFeature({ feature }) {
|
|
856
|
+
const titleId = `${feature.id}-title`;
|
|
857
|
+
const bodyId = `${feature.id}-body`;
|
|
858
|
+
const s = feature.summary;
|
|
859
|
+
const skipped = s.skipped + s.pending;
|
|
860
|
+
const collapse = useCollapse();
|
|
861
|
+
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
862
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
863
|
+
"section",
|
|
864
|
+
{
|
|
865
|
+
id: feature.id,
|
|
866
|
+
"data-slot": "feature",
|
|
867
|
+
"aria-labelledby": titleId,
|
|
868
|
+
className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm",
|
|
869
|
+
children: [
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex items-center justify-between gap-4 border-b border-border bg-muted/40 px-4 py-3", children: [
|
|
871
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
872
|
+
collapse ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
873
|
+
"button",
|
|
874
|
+
{
|
|
875
|
+
type: "button",
|
|
876
|
+
onClick: () => collapse.toggle(feature.id),
|
|
877
|
+
"aria-expanded": !collapsed,
|
|
878
|
+
"aria-controls": bodyId,
|
|
879
|
+
"aria-label": `Toggle ${feature.title}`,
|
|
880
|
+
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
881
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
882
|
+
}
|
|
883
|
+
) : null,
|
|
884
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "min-w-0", children: [
|
|
885
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("h2", { id: titleId, className: "text-sm font-semibold tracking-tight text-foreground", children: feature.title }),
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "mt-0.5 truncate font-mono text-xs text-muted-foreground", children: feature.sourceFile })
|
|
887
|
+
] })
|
|
888
|
+
] }),
|
|
889
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
890
|
+
"div",
|
|
891
|
+
{
|
|
892
|
+
role: "img",
|
|
893
|
+
className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
|
|
894
|
+
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
895
|
+
children: [
|
|
896
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "aria-hidden": true, className: "text-pass", children: [
|
|
897
|
+
"\u2713",
|
|
898
|
+
s.passed
|
|
899
|
+
] }),
|
|
900
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "aria-hidden": true, className: "text-fail", children: [
|
|
901
|
+
"\u2717",
|
|
902
|
+
s.failed
|
|
903
|
+
] }),
|
|
904
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "aria-hidden": true, className: "text-skip", children: [
|
|
905
|
+
"\u25CB",
|
|
906
|
+
skipped
|
|
907
|
+
] })
|
|
908
|
+
]
|
|
909
|
+
}
|
|
910
|
+
)
|
|
911
|
+
] }),
|
|
912
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-2 p-3", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportScenarioList, { feature }) })
|
|
913
|
+
]
|
|
914
|
+
}
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/components/ReportFeatureList.tsx
|
|
919
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
920
|
+
function ReportFeatureList() {
|
|
921
|
+
const report = useReport();
|
|
922
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportFeature, { feature }, feature.id)) });
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// src/components/ui/empty.tsx
|
|
926
|
+
var import_class_variance_authority2 = require("class-variance-authority");
|
|
927
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
928
|
+
function Empty({ className, ...props }) {
|
|
929
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
930
|
+
"div",
|
|
931
|
+
{
|
|
932
|
+
"data-slot": "empty",
|
|
933
|
+
className: cn(
|
|
934
|
+
"flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
|
|
935
|
+
className
|
|
936
|
+
),
|
|
937
|
+
...props
|
|
938
|
+
}
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
function EmptyHeader({ className, ...props }) {
|
|
942
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
943
|
+
"div",
|
|
944
|
+
{
|
|
945
|
+
"data-slot": "empty-header",
|
|
946
|
+
className: cn(
|
|
947
|
+
"flex max-w-sm flex-col items-center gap-2 text-center",
|
|
948
|
+
className
|
|
949
|
+
),
|
|
950
|
+
...props
|
|
951
|
+
}
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
955
|
+
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
956
|
+
{
|
|
957
|
+
variants: {
|
|
958
|
+
variant: {
|
|
959
|
+
default: "bg-transparent",
|
|
960
|
+
icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6"
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
defaultVariants: {
|
|
964
|
+
variant: "default"
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
);
|
|
968
|
+
function EmptyTitle({ className, ...props }) {
|
|
969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
970
|
+
"div",
|
|
971
|
+
{
|
|
972
|
+
"data-slot": "empty-title",
|
|
973
|
+
className: cn("text-lg font-medium tracking-tight", className),
|
|
974
|
+
...props
|
|
975
|
+
}
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// src/components/ReportEmpty.tsx
|
|
980
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
981
|
+
function ReportEmpty({ message }) {
|
|
982
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/components/ReportSummary.tsx
|
|
986
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
987
|
+
function ReportSummary({ className }) {
|
|
988
|
+
const report = useReport();
|
|
989
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
990
|
+
ReportSummaryView,
|
|
991
|
+
{
|
|
992
|
+
summary: report.summary,
|
|
993
|
+
...className !== void 0 && { className },
|
|
994
|
+
ariaLabel: "Run summary"
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("p", { className: cn("text-sm text-muted-foreground", className), "aria-label": ariaLabel, children: [
|
|
1000
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
|
|
1001
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("strong", { className: "font-semibold text-foreground", children: summary.total }),
|
|
1002
|
+
" scenario",
|
|
1003
|
+
summary.total === 1 ? "" : "s"
|
|
1004
|
+
] }),
|
|
1005
|
+
" \xB7 ",
|
|
1006
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { "data-status": "passed", className: "text-pass", children: [
|
|
1007
|
+
summary.passed,
|
|
1008
|
+
" passed"
|
|
1009
|
+
] }),
|
|
1010
|
+
" \xB7 ",
|
|
1011
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { "data-status": "failed", className: "text-fail", children: [
|
|
1012
|
+
summary.failed,
|
|
1013
|
+
" failed"
|
|
1014
|
+
] }),
|
|
1015
|
+
summary.skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
1016
|
+
" \xB7 ",
|
|
1017
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { "data-status": "skipped", className: "text-skip", children: [
|
|
1018
|
+
summary.skipped,
|
|
1019
|
+
" skipped"
|
|
1020
|
+
] })
|
|
1021
|
+
] }) : null,
|
|
1022
|
+
summary.pending > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
1023
|
+
" \xB7 ",
|
|
1024
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { "data-status": "pending", className: "text-pend", children: [
|
|
1025
|
+
summary.pending,
|
|
1026
|
+
" pending"
|
|
1027
|
+
] })
|
|
1028
|
+
] }) : null
|
|
1029
|
+
] });
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// src/components/ReportMeta.tsx
|
|
1033
|
+
var import_duration4 = require("executable-stories-core/utils/duration");
|
|
1034
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1035
|
+
function ReportMeta() {
|
|
1036
|
+
const report = useReport();
|
|
1037
|
+
const items = [];
|
|
1038
|
+
if (report.startedAtMs) {
|
|
1039
|
+
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1040
|
+
}
|
|
1041
|
+
if (report.durationMs) {
|
|
1042
|
+
items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
|
|
1043
|
+
}
|
|
1044
|
+
if (report.packageVersion) {
|
|
1045
|
+
items.push({ label: "Version", value: report.packageVersion });
|
|
1046
|
+
}
|
|
1047
|
+
if (report.gitSha) {
|
|
1048
|
+
items.push({ label: "Commit", value: report.gitSha.slice(0, 8) });
|
|
1049
|
+
}
|
|
1050
|
+
if (report.ci) {
|
|
1051
|
+
items.push({
|
|
1052
|
+
label: "CI",
|
|
1053
|
+
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: "text-primary underline underline-offset-2", children: [
|
|
1054
|
+
report.ci.name,
|
|
1055
|
+
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1056
|
+
] }) : `${report.ci.name}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
if (items.length === 0) return null;
|
|
1060
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.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_runtime28.jsxs)("div", { className: "flex gap-1.5", children: [
|
|
1061
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1062
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1063
|
+
] }, item.label)) });
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/components/ReportSchemaError.tsx
|
|
1067
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1068
|
+
function ReportSchemaError({ error }) {
|
|
1069
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1070
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1071
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { children: error.message }),
|
|
1072
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { children: [
|
|
1073
|
+
"The report bundle is newer than this version of ",
|
|
1074
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("code", { children: "executable-stories-react" }),
|
|
1075
|
+
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1076
|
+
] }) : null,
|
|
1077
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("details", { children: [
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("summary", { children: [
|
|
1079
|
+
error.issues.length,
|
|
1080
|
+
" validation issue",
|
|
1081
|
+
error.issues.length === 1 ? "" : "s"
|
|
1082
|
+
] }),
|
|
1083
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1084
|
+
] }) : null
|
|
1085
|
+
] });
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// src/components/ReportShell.tsx
|
|
1089
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1090
|
+
function ReportTitleBlock({ title }) {
|
|
1091
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1092
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h1", { children: title ?? "Story Report" }),
|
|
1093
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportSummary, {}),
|
|
1094
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportMeta, {})
|
|
1095
|
+
] });
|
|
1096
|
+
}
|
|
1097
|
+
function ReportErrorShell({
|
|
1098
|
+
error,
|
|
1099
|
+
className,
|
|
1100
|
+
title,
|
|
1101
|
+
dataTheme
|
|
1102
|
+
}) {
|
|
1103
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportSchemaError, { error }) });
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// src/components/Report.tsx
|
|
1107
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1108
|
+
function Report(props) {
|
|
1109
|
+
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1110
|
+
const result = unwrapReport(report);
|
|
1111
|
+
if (!result.ok) {
|
|
1112
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1113
|
+
}
|
|
1114
|
+
const data = result.data;
|
|
1115
|
+
const hasContent = data.features.length > 0;
|
|
1116
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
|
|
1117
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("header", { className: "es-report-header", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportTitleBlock, { title }) }),
|
|
1118
|
+
hasContent ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportFeatureList, {}) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportEmpty, {})
|
|
1119
|
+
] }) });
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// src/ssr-entry.ts
|
|
1123
|
+
var ROOT_ID = "es-report-root";
|
|
1124
|
+
var DATA_ID = "es-report-data";
|
|
1125
|
+
function escapeJsonForScript(json) {
|
|
1126
|
+
return json.replace(/[<\u2028\u2029]/g, (c) => "\\u" + c.charCodeAt(0).toString(16).padStart(4, "0"));
|
|
1127
|
+
}
|
|
1128
|
+
var HLJS_URL = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
1129
|
+
var HLJS_CSS_LIGHT = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css";
|
|
1130
|
+
var HLJS_CSS_DARK = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css";
|
|
1131
|
+
var MERMAID_URL = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
1132
|
+
function cdnAssets(syntaxHighlighting, mermaid) {
|
|
1133
|
+
const headParts = [];
|
|
1134
|
+
const imports = [];
|
|
1135
|
+
const init = [];
|
|
1136
|
+
if (syntaxHighlighting) {
|
|
1137
|
+
headParts.push(`<link rel="stylesheet" href="${HLJS_CSS_LIGHT}">`);
|
|
1138
|
+
headParts.push(`<link rel="stylesheet" href="${HLJS_CSS_DARK}" media="(prefers-color-scheme: dark)">`);
|
|
1139
|
+
imports.push(`import hljs from "${HLJS_URL}";`);
|
|
1140
|
+
init.push("document.querySelectorAll('pre code').forEach((el) => hljs.highlightElement(el));");
|
|
1141
|
+
}
|
|
1142
|
+
if (mermaid) {
|
|
1143
|
+
imports.push(`import mermaid from "${MERMAID_URL}";`);
|
|
1144
|
+
init.push("mermaid.initialize({ startOnLoad: false, theme: 'neutral' });");
|
|
1145
|
+
init.push('await mermaid.run({ querySelector: "pre[data-mermaid]" });');
|
|
1146
|
+
}
|
|
1147
|
+
const body = imports.length ? `<script type="module">
|
|
1148
|
+
${imports.join("\n")}
|
|
1149
|
+
${init.join("\n")}
|
|
1150
|
+
</script>` : "";
|
|
1151
|
+
return { head: headParts.join("\n"), body };
|
|
1152
|
+
}
|
|
1153
|
+
var HTML_ESCAPES = {
|
|
1154
|
+
"&": "&",
|
|
1155
|
+
"<": "<",
|
|
1156
|
+
">": ">",
|
|
1157
|
+
'"': """,
|
|
1158
|
+
"'": "'"
|
|
1159
|
+
};
|
|
1160
|
+
function escapeHtml(value) {
|
|
1161
|
+
return value.replace(/[&<>"']/g, (ch) => HTML_ESCAPES[ch] ?? ch);
|
|
1162
|
+
}
|
|
1163
|
+
function renderReportToHtml(report, options = {}) {
|
|
1164
|
+
const {
|
|
1165
|
+
title = "Story Report",
|
|
1166
|
+
css = "",
|
|
1167
|
+
theme = "light",
|
|
1168
|
+
headExtra = "",
|
|
1169
|
+
bodyExtra = "",
|
|
1170
|
+
lang = "en",
|
|
1171
|
+
syntaxHighlighting = true,
|
|
1172
|
+
mermaid = true,
|
|
1173
|
+
islandScript = ""
|
|
1174
|
+
} = options;
|
|
1175
|
+
const markup = (0, import_server.renderToStaticMarkup)(
|
|
1176
|
+
(0, import_react7.createElement)(Report, { report, title })
|
|
1177
|
+
);
|
|
1178
|
+
const cdn = cdnAssets(syntaxHighlighting, mermaid);
|
|
1179
|
+
const parsed = unwrapReport(report);
|
|
1180
|
+
const rawReport = parsed.ok ? parsed.data : void 0;
|
|
1181
|
+
const interactive = islandScript.length > 0 && rawReport !== void 0;
|
|
1182
|
+
const dataScript = interactive ? `<script type="application/json" id="${DATA_ID}">${escapeJsonForScript(JSON.stringify(rawReport))}</script>` : "";
|
|
1183
|
+
const islandTag = interactive ? `<script>${islandScript}</script>` : "";
|
|
1184
|
+
const cdnBody = interactive ? "" : cdn.body;
|
|
1185
|
+
const islandConfigAttrs = interactive ? ` data-es-syntax="${syntaxHighlighting ? "true" : "false"}" data-es-mermaid="${mermaid ? "true" : "false"}"` : "";
|
|
1186
|
+
const rootAttrs = interactive ? ` id="${ROOT_ID}" data-title="${escapeHtml(title)}"${islandConfigAttrs}` : "";
|
|
1187
|
+
return `<!DOCTYPE html>
|
|
1188
|
+
<html lang="${escapeHtml(lang)}" data-theme="${theme === "dark" ? "dark" : "light"}">
|
|
1189
|
+
<head>
|
|
1190
|
+
<meta charset="UTF-8">
|
|
1191
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1192
|
+
<meta name="color-scheme" content="light dark">
|
|
1193
|
+
<title>${escapeHtml(title)}</title>
|
|
1194
|
+
${cdn.head}
|
|
1195
|
+
${headExtra}
|
|
1196
|
+
<style>${css}</style>
|
|
1197
|
+
</head>
|
|
1198
|
+
<body>
|
|
1199
|
+
<div${rootAttrs} class="es-report-island font-sans text-foreground">${markup}</div>
|
|
1200
|
+
${dataScript}
|
|
1201
|
+
${bodyExtra}
|
|
1202
|
+
${cdnBody}
|
|
1203
|
+
${islandTag}
|
|
1204
|
+
</body>
|
|
1205
|
+
</html>`;
|
|
1206
|
+
}
|
|
1207
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1208
|
+
0 && (module.exports = {
|
|
1209
|
+
renderReportToHtml
|
|
1210
|
+
});
|
|
1211
|
+
//# sourceMappingURL=ssr.cjs.map
|