executable-stories-react 0.1.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/README.md +238 -0
- package/dist/index.cjs +1021 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +202 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.js +964 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive.cjs +846 -0
- package/dist/interactive.cjs.map +1 -0
- package/dist/interactive.d.cts +116 -0
- package/dist/interactive.d.ts +116 -0
- package/dist/interactive.js +820 -0
- package/dist/interactive.js.map +1 -0
- package/dist/parse.cjs +495 -0
- package/dist/parse.cjs.map +1 -0
- package/dist/parse.d.cts +51 -0
- package/dist/parse.d.ts +51 -0
- package/dist/parse.js +464 -0
- package/dist/parse.js.map +1 -0
- package/dist/styles.css +244 -0
- package/package.json +84 -0
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/interactive/ReportInteractive.tsx
|
|
4
|
+
import {
|
|
5
|
+
useCallback as useCallback2,
|
|
6
|
+
useMemo as useMemo2,
|
|
7
|
+
useRef as useRef3,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
// src/context/ReportRoot.tsx
|
|
12
|
+
import { useMemo } from "react";
|
|
13
|
+
|
|
14
|
+
// src/context/ReportContext.ts
|
|
15
|
+
import { createContext } from "react";
|
|
16
|
+
var ReportContext = createContext(null);
|
|
17
|
+
|
|
18
|
+
// src/context/ReportRoot.tsx
|
|
19
|
+
import { jsx } from "react/jsx-runtime";
|
|
20
|
+
var EMPTY_CUSTOM = {};
|
|
21
|
+
var EMPTY_RENDERERS = {};
|
|
22
|
+
function ReportRoot({
|
|
23
|
+
report,
|
|
24
|
+
customRenderers,
|
|
25
|
+
renderers,
|
|
26
|
+
children
|
|
27
|
+
}) {
|
|
28
|
+
const value = useMemo(
|
|
29
|
+
() => ({
|
|
30
|
+
report,
|
|
31
|
+
customRenderers: customRenderers ?? EMPTY_CUSTOM,
|
|
32
|
+
renderers: renderers ?? EMPTY_RENDERERS
|
|
33
|
+
}),
|
|
34
|
+
[report, customRenderers, renderers]
|
|
35
|
+
);
|
|
36
|
+
return /* @__PURE__ */ jsx(ReportContext.Provider, { value, children });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/hooks/useReport.ts
|
|
40
|
+
import { useContext } from "react";
|
|
41
|
+
function useReport() {
|
|
42
|
+
const ctx = useContext(ReportContext);
|
|
43
|
+
if (!ctx) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return ctx.report;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/components/ReportSummary.tsx
|
|
52
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
53
|
+
function ReportSummary({ className }) {
|
|
54
|
+
const report = useReport();
|
|
55
|
+
return /* @__PURE__ */ jsx2(
|
|
56
|
+
ReportSummaryView,
|
|
57
|
+
{
|
|
58
|
+
summary: report.summary,
|
|
59
|
+
...className !== void 0 && { className },
|
|
60
|
+
ariaLabel: "Run summary"
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
65
|
+
return /* @__PURE__ */ jsxs(
|
|
66
|
+
"p",
|
|
67
|
+
{
|
|
68
|
+
className: ["es-report-summary", className].filter(Boolean).join(" "),
|
|
69
|
+
"aria-label": ariaLabel,
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
72
|
+
/* @__PURE__ */ jsx2("strong", { children: summary.total }),
|
|
73
|
+
" scenario",
|
|
74
|
+
summary.total === 1 ? "" : "s"
|
|
75
|
+
] }),
|
|
76
|
+
" \xB7 ",
|
|
77
|
+
/* @__PURE__ */ jsxs("span", { "data-status": "passed", children: [
|
|
78
|
+
summary.passed,
|
|
79
|
+
" passed"
|
|
80
|
+
] }),
|
|
81
|
+
" \xB7 ",
|
|
82
|
+
/* @__PURE__ */ jsxs("span", { "data-status": "failed", children: [
|
|
83
|
+
summary.failed,
|
|
84
|
+
" failed"
|
|
85
|
+
] }),
|
|
86
|
+
summary.skipped > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
87
|
+
" \xB7 ",
|
|
88
|
+
/* @__PURE__ */ jsxs("span", { "data-status": "skipped", children: [
|
|
89
|
+
summary.skipped,
|
|
90
|
+
" skipped"
|
|
91
|
+
] })
|
|
92
|
+
] }) : null,
|
|
93
|
+
summary.pending > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
94
|
+
" \xB7 ",
|
|
95
|
+
/* @__PURE__ */ jsxs("span", { "data-status": "pending", children: [
|
|
96
|
+
summary.pending,
|
|
97
|
+
" pending"
|
|
98
|
+
] })
|
|
99
|
+
] }) : null
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/components/doc/DocNote.tsx
|
|
106
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
107
|
+
function DocNote({ entry }) {
|
|
108
|
+
return /* @__PURE__ */ jsx3("p", { className: "es-doc es-doc-note", children: entry.text });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/components/doc/DocTag.tsx
|
|
112
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
113
|
+
function DocTag({ entry }) {
|
|
114
|
+
return /* @__PURE__ */ jsx4("ul", { className: "es-doc es-tags", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ jsx4("li", { children: n }, n)) });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/components/doc/DocKv.tsx
|
|
118
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
119
|
+
function formatValue(value) {
|
|
120
|
+
if (value === null) return "null";
|
|
121
|
+
if (typeof value === "string") return value;
|
|
122
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
123
|
+
try {
|
|
124
|
+
return JSON.stringify(value);
|
|
125
|
+
} catch {
|
|
126
|
+
return String(value);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function DocKv({ entry }) {
|
|
130
|
+
return /* @__PURE__ */ jsxs2("dl", { className: "es-doc es-doc-kv", children: [
|
|
131
|
+
/* @__PURE__ */ jsx5("dt", { children: entry.label }),
|
|
132
|
+
/* @__PURE__ */ jsx5("dd", { children: formatValue(entry.value) })
|
|
133
|
+
] });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/hooks/useRenderers.ts
|
|
137
|
+
import { useContext as useContext2 } from "react";
|
|
138
|
+
var EMPTY_CUSTOM2 = {};
|
|
139
|
+
var EMPTY_RENDERERS2 = {};
|
|
140
|
+
function useCustomRenderers() {
|
|
141
|
+
const ctx = useContext2(ReportContext);
|
|
142
|
+
return ctx?.customRenderers ?? EMPTY_CUSTOM2;
|
|
143
|
+
}
|
|
144
|
+
function useBuiltinRenderers() {
|
|
145
|
+
const ctx = useContext2(ReportContext);
|
|
146
|
+
return ctx?.renderers ?? EMPTY_RENDERERS2;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/components/doc/DocCode.tsx
|
|
150
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
151
|
+
function DocCode({ entry }) {
|
|
152
|
+
const renderers = useBuiltinRenderers();
|
|
153
|
+
if (renderers.code) {
|
|
154
|
+
return /* @__PURE__ */ jsx6(Fragment2, { children: renderers.code(entry) });
|
|
155
|
+
}
|
|
156
|
+
return /* @__PURE__ */ jsxs3("figure", { className: "es-doc es-doc-code", children: [
|
|
157
|
+
/* @__PURE__ */ jsx6("figcaption", { children: entry.label }),
|
|
158
|
+
/* @__PURE__ */ jsx6("pre", { children: /* @__PURE__ */ jsx6("code", { className: entry.lang ? `language-${entry.lang}` : void 0, children: entry.content }) })
|
|
159
|
+
] });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/components/doc/DocTable.tsx
|
|
163
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
164
|
+
function DocTable({ entry }) {
|
|
165
|
+
return /* @__PURE__ */ jsxs4("figure", { className: "es-doc es-doc-table", children: [
|
|
166
|
+
/* @__PURE__ */ jsx7("figcaption", { children: entry.label }),
|
|
167
|
+
/* @__PURE__ */ jsxs4("table", { children: [
|
|
168
|
+
/* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsx7("tr", { children: entry.columns.map((c) => /* @__PURE__ */ jsx7("th", { scope: "col", children: c }, c)) }) }),
|
|
169
|
+
/* @__PURE__ */ jsx7("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ jsx7("tr", { children: row.map((cell, j) => /* @__PURE__ */ jsx7("td", { children: cell }, j)) }, i)) })
|
|
170
|
+
] })
|
|
171
|
+
] });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/components/doc/DocLink.tsx
|
|
175
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
176
|
+
function DocLink({ entry }) {
|
|
177
|
+
return /* @__PURE__ */ jsx8(
|
|
178
|
+
"a",
|
|
179
|
+
{
|
|
180
|
+
className: "es-doc es-doc-link",
|
|
181
|
+
href: entry.url,
|
|
182
|
+
rel: "noreferrer noopener",
|
|
183
|
+
target: "_blank",
|
|
184
|
+
children: entry.label
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/components/doc/DocSection.tsx
|
|
190
|
+
import { marked } from "marked";
|
|
191
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
192
|
+
function safeMarkdownHtml(markdown) {
|
|
193
|
+
const raw = marked.parse(markdown, { async: false });
|
|
194
|
+
return raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/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*"\s*javascript:[^"]*"/gi, '$1="#"').replace(/(href|src)\s*=\s*'\s*javascript:[^']*'/gi, "$1='#'");
|
|
195
|
+
}
|
|
196
|
+
function DocSection({ entry }) {
|
|
197
|
+
const renderers = useBuiltinRenderers();
|
|
198
|
+
if (renderers.section) {
|
|
199
|
+
return /* @__PURE__ */ jsx9(Fragment3, { children: renderers.section(entry) });
|
|
200
|
+
}
|
|
201
|
+
const html = safeMarkdownHtml(entry.markdown);
|
|
202
|
+
return /* @__PURE__ */ jsxs5("section", { className: "es-doc es-doc-section", "aria-label": entry.title, children: [
|
|
203
|
+
entry.title ? /* @__PURE__ */ jsx9("h4", { className: "es-doc-section-title", children: entry.title }) : null,
|
|
204
|
+
/* @__PURE__ */ jsx9(
|
|
205
|
+
"div",
|
|
206
|
+
{
|
|
207
|
+
className: "es-doc-section-content",
|
|
208
|
+
dangerouslySetInnerHTML: { __html: html }
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
] });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/components/doc/DocMermaid.tsx
|
|
215
|
+
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
216
|
+
function DocMermaid({ entry }) {
|
|
217
|
+
const renderers = useBuiltinRenderers();
|
|
218
|
+
if (renderers.mermaid) {
|
|
219
|
+
return /* @__PURE__ */ jsx10(Fragment4, { children: renderers.mermaid(entry) });
|
|
220
|
+
}
|
|
221
|
+
return /* @__PURE__ */ jsxs6(
|
|
222
|
+
"figure",
|
|
223
|
+
{
|
|
224
|
+
className: "es-doc es-doc-mermaid",
|
|
225
|
+
"aria-label": entry.title ?? "Diagram",
|
|
226
|
+
children: [
|
|
227
|
+
entry.title ? /* @__PURE__ */ jsx10("figcaption", { children: entry.title }) : null,
|
|
228
|
+
/* @__PURE__ */ jsx10("pre", { "data-mermaid": true, children: entry.code })
|
|
229
|
+
]
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/components/doc/DocScreenshot.tsx
|
|
235
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
236
|
+
function DocScreenshot({ entry }) {
|
|
237
|
+
return /* @__PURE__ */ jsxs7("figure", { className: "es-doc es-doc-screenshot", children: [
|
|
238
|
+
/* @__PURE__ */ jsx11("img", { src: entry.path, alt: entry.alt ?? "", loading: "lazy" }),
|
|
239
|
+
entry.alt ? /* @__PURE__ */ jsx11("figcaption", { children: entry.alt }) : null
|
|
240
|
+
] });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/components/doc/DocCustom.tsx
|
|
244
|
+
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
245
|
+
function DocCustom({ entry }) {
|
|
246
|
+
const renderers = useCustomRenderers();
|
|
247
|
+
const renderer = renderers[entry.type];
|
|
248
|
+
if (renderer) {
|
|
249
|
+
return /* @__PURE__ */ jsx12(Fragment5, { children: renderer(entry) });
|
|
250
|
+
}
|
|
251
|
+
return /* @__PURE__ */ jsxs8("div", { className: "es-doc es-doc-custom", "data-type": entry.type, children: [
|
|
252
|
+
/* @__PURE__ */ jsx12("p", { className: "es-doc-custom-type", children: entry.type }),
|
|
253
|
+
/* @__PURE__ */ jsx12("pre", { children: safeStringify(entry.data) })
|
|
254
|
+
] });
|
|
255
|
+
}
|
|
256
|
+
function safeStringify(value) {
|
|
257
|
+
try {
|
|
258
|
+
return JSON.stringify(value, null, 2);
|
|
259
|
+
} catch {
|
|
260
|
+
return String(value);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/components/doc/DocEntry.tsx
|
|
265
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
266
|
+
function DocEntry({ entry }) {
|
|
267
|
+
switch (entry.kind) {
|
|
268
|
+
case "note":
|
|
269
|
+
return /* @__PURE__ */ jsx13(DocNote, { entry });
|
|
270
|
+
case "tag":
|
|
271
|
+
return /* @__PURE__ */ jsx13(DocTag, { entry });
|
|
272
|
+
case "kv":
|
|
273
|
+
return /* @__PURE__ */ jsx13(DocKv, { entry });
|
|
274
|
+
case "code":
|
|
275
|
+
return /* @__PURE__ */ jsx13(DocCode, { entry });
|
|
276
|
+
case "table":
|
|
277
|
+
return /* @__PURE__ */ jsx13(DocTable, { entry });
|
|
278
|
+
case "link":
|
|
279
|
+
return /* @__PURE__ */ jsx13(DocLink, { entry });
|
|
280
|
+
case "section":
|
|
281
|
+
return /* @__PURE__ */ jsx13(DocSection, { entry });
|
|
282
|
+
case "mermaid":
|
|
283
|
+
return /* @__PURE__ */ jsx13(DocMermaid, { entry });
|
|
284
|
+
case "screenshot":
|
|
285
|
+
return /* @__PURE__ */ jsx13(DocScreenshot, { entry });
|
|
286
|
+
case "custom":
|
|
287
|
+
return /* @__PURE__ */ jsx13(DocCustom, { entry });
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/components/ReportDocEntries.tsx
|
|
292
|
+
import { Fragment as Fragment6, jsx as jsx14 } from "react/jsx-runtime";
|
|
293
|
+
function ReportDocEntries({ entries }) {
|
|
294
|
+
if (entries.length === 0) return null;
|
|
295
|
+
return /* @__PURE__ */ jsx14(Fragment6, { children: entries.map((entry, i) => /* @__PURE__ */ jsx14(DocEntry, { entry }, i)) });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// src/components/ReportSteps.tsx
|
|
299
|
+
import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
300
|
+
function ReportSteps({ scenario }) {
|
|
301
|
+
if (scenario.steps.length === 0) return null;
|
|
302
|
+
return /* @__PURE__ */ jsx15("ol", { className: "es-steps", children: scenario.steps.map((step) => /* @__PURE__ */ jsx15(ReportStepItem, { step }, step.id)) });
|
|
303
|
+
}
|
|
304
|
+
function ReportStepItem({ step }) {
|
|
305
|
+
return /* @__PURE__ */ jsxs9(
|
|
306
|
+
"li",
|
|
307
|
+
{
|
|
308
|
+
id: step.id,
|
|
309
|
+
className: `es-step es-step-${step.status}`,
|
|
310
|
+
"data-status": step.status,
|
|
311
|
+
children: [
|
|
312
|
+
/* @__PURE__ */ jsx15("span", { className: "es-step-keyword", children: step.keyword }),
|
|
313
|
+
/* @__PURE__ */ jsx15("span", { className: "es-step-text", children: step.text }),
|
|
314
|
+
step.errorMessage ? /* @__PURE__ */ jsx15("pre", { className: "es-scenario-error", role: "alert", children: step.errorMessage }) : null,
|
|
315
|
+
step.docEntries.length > 0 ? /* @__PURE__ */ jsx15("div", { className: "es-step-docs", children: /* @__PURE__ */ jsx15(ReportDocEntries, { entries: step.docEntries }) }) : null
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// src/components/ReportScenario.tsx
|
|
322
|
+
import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
323
|
+
var STATUS_LABEL = {
|
|
324
|
+
passed: "Passed",
|
|
325
|
+
failed: "Failed",
|
|
326
|
+
skipped: "Skipped",
|
|
327
|
+
pending: "Pending"
|
|
328
|
+
};
|
|
329
|
+
function ReportScenario({ scenario }) {
|
|
330
|
+
const titleId = `${scenario.id}-title`;
|
|
331
|
+
return /* @__PURE__ */ jsxs10(
|
|
332
|
+
"article",
|
|
333
|
+
{
|
|
334
|
+
id: scenario.id,
|
|
335
|
+
className: `es-scenario es-status-${scenario.status}`,
|
|
336
|
+
"aria-labelledby": titleId,
|
|
337
|
+
"data-status": scenario.status,
|
|
338
|
+
children: [
|
|
339
|
+
/* @__PURE__ */ jsxs10("h3", { id: titleId, className: "es-scenario-title", children: [
|
|
340
|
+
/* @__PURE__ */ jsx16("span", { children: scenario.title }),
|
|
341
|
+
/* @__PURE__ */ jsx16("span", { className: "es-scenario-status", "aria-label": `Status: ${STATUS_LABEL[scenario.status]}`, children: STATUS_LABEL[scenario.status] })
|
|
342
|
+
] }),
|
|
343
|
+
scenario.tags.length > 0 ? /* @__PURE__ */ jsx16("ul", { className: "es-tags", "aria-label": "Tags", children: scenario.tags.map((t) => /* @__PURE__ */ jsx16("li", { children: t }, t)) }) : null,
|
|
344
|
+
scenario.errorMessage ? /* @__PURE__ */ jsx16("pre", { className: "es-scenario-error", role: "alert", children: scenario.errorMessage }) : null,
|
|
345
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx16("div", { className: "es-scenario-docs", children: /* @__PURE__ */ jsx16(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
346
|
+
/* @__PURE__ */ jsx16(ReportSteps, { scenario })
|
|
347
|
+
]
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/components/ReportScenarioList.tsx
|
|
353
|
+
import { Fragment as Fragment7, jsx as jsx17 } from "react/jsx-runtime";
|
|
354
|
+
function ReportScenarioList({ feature }) {
|
|
355
|
+
return /* @__PURE__ */ jsx17(Fragment7, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx17(ReportScenario, { scenario }, scenario.id)) });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/components/ReportFeature.tsx
|
|
359
|
+
import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
360
|
+
function ReportFeature({ feature }) {
|
|
361
|
+
const titleId = `${feature.id}-title`;
|
|
362
|
+
return /* @__PURE__ */ jsxs11(
|
|
363
|
+
"section",
|
|
364
|
+
{
|
|
365
|
+
id: feature.id,
|
|
366
|
+
className: "es-feature",
|
|
367
|
+
"aria-labelledby": titleId,
|
|
368
|
+
children: [
|
|
369
|
+
/* @__PURE__ */ jsx18("h2", { id: titleId, className: "es-feature-title", children: feature.title }),
|
|
370
|
+
/* @__PURE__ */ jsx18("p", { className: "es-feature-source", children: feature.sourceFile }),
|
|
371
|
+
/* @__PURE__ */ jsx18(ReportSummaryView, { summary: feature.summary, className: "es-feature-summary" }),
|
|
372
|
+
/* @__PURE__ */ jsx18(ReportScenarioList, { feature })
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/components/ReportFeatureList.tsx
|
|
379
|
+
import { Fragment as Fragment8, jsx as jsx19 } from "react/jsx-runtime";
|
|
380
|
+
function ReportFeatureList() {
|
|
381
|
+
const report = useReport();
|
|
382
|
+
return /* @__PURE__ */ jsx19(Fragment8, { children: report.features.map((feature) => /* @__PURE__ */ jsx19(ReportFeature, { feature }, feature.id)) });
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// src/components/ReportEmpty.tsx
|
|
386
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
387
|
+
function ReportEmpty({ message }) {
|
|
388
|
+
return /* @__PURE__ */ jsx20("section", { className: "es-empty", "aria-live": "polite", children: /* @__PURE__ */ jsx20("p", { children: message ?? "No scenarios in this report." }) });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/components/ReportSchemaError.tsx
|
|
392
|
+
import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
393
|
+
function ReportSchemaError({ error }) {
|
|
394
|
+
return /* @__PURE__ */ jsxs12("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
395
|
+
/* @__PURE__ */ jsx21("p", { children: /* @__PURE__ */ jsx21("strong", { children: "Report could not be displayed." }) }),
|
|
396
|
+
/* @__PURE__ */ jsx21("p", { children: error.message }),
|
|
397
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs12("p", { children: [
|
|
398
|
+
"The report bundle is newer than this version of ",
|
|
399
|
+
/* @__PURE__ */ jsx21("code", { children: "executable-stories-react" }),
|
|
400
|
+
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
401
|
+
] }) : null,
|
|
402
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs12("details", { children: [
|
|
403
|
+
/* @__PURE__ */ jsxs12("summary", { children: [
|
|
404
|
+
error.issues.length,
|
|
405
|
+
" validation issue",
|
|
406
|
+
error.issues.length === 1 ? "" : "s"
|
|
407
|
+
] }),
|
|
408
|
+
/* @__PURE__ */ jsx21("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
409
|
+
] }) : null
|
|
410
|
+
] });
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// src/interactive/ReportSearch.tsx
|
|
414
|
+
import {
|
|
415
|
+
forwardRef,
|
|
416
|
+
useId
|
|
417
|
+
} from "react";
|
|
418
|
+
import { jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
419
|
+
var ReportSearch = forwardRef(
|
|
420
|
+
function ReportSearch2(props, ref) {
|
|
421
|
+
const {
|
|
422
|
+
value,
|
|
423
|
+
onChange,
|
|
424
|
+
matchedCount,
|
|
425
|
+
totalCount,
|
|
426
|
+
placeholder = "Search scenarios, tags, or step text\u2026",
|
|
427
|
+
className
|
|
428
|
+
} = props;
|
|
429
|
+
const inputId = useId();
|
|
430
|
+
const showCounts = typeof matchedCount === "number" && typeof totalCount === "number";
|
|
431
|
+
function handleChange(e) {
|
|
432
|
+
onChange(e.target.value);
|
|
433
|
+
}
|
|
434
|
+
function handleKeyDown(e) {
|
|
435
|
+
if (e.key === "Escape" && value !== "") {
|
|
436
|
+
onChange("");
|
|
437
|
+
e.preventDefault();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return /* @__PURE__ */ jsxs13("div", { className: ["es-search", className].filter(Boolean).join(" "), children: [
|
|
441
|
+
/* @__PURE__ */ jsx22("label", { htmlFor: inputId, className: "es-search-label", children: "Search" }),
|
|
442
|
+
/* @__PURE__ */ jsx22(
|
|
443
|
+
"input",
|
|
444
|
+
{
|
|
445
|
+
ref,
|
|
446
|
+
id: inputId,
|
|
447
|
+
type: "search",
|
|
448
|
+
value,
|
|
449
|
+
onChange: handleChange,
|
|
450
|
+
onKeyDown: handleKeyDown,
|
|
451
|
+
placeholder,
|
|
452
|
+
autoComplete: "off",
|
|
453
|
+
spellCheck: false,
|
|
454
|
+
"aria-keyshortcuts": "/"
|
|
455
|
+
}
|
|
456
|
+
),
|
|
457
|
+
showCounts ? /* @__PURE__ */ jsx22("span", { className: "es-search-counts", "aria-live": "polite", children: value ? `${matchedCount} of ${totalCount}` : `${totalCount} total` }) : null
|
|
458
|
+
] });
|
|
459
|
+
}
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
// src/interactive/ReportFailureBanner.tsx
|
|
463
|
+
import { useCallback } from "react";
|
|
464
|
+
import { jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
465
|
+
function ReportFailureBanner({ failures }) {
|
|
466
|
+
const first = failures[0];
|
|
467
|
+
const jumpToFirst = useCallback(() => {
|
|
468
|
+
if (!first) return;
|
|
469
|
+
if (typeof window === "undefined") return;
|
|
470
|
+
const el = document.getElementById(first.scenarioId);
|
|
471
|
+
el?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
472
|
+
if (typeof history !== "undefined") {
|
|
473
|
+
history.replaceState(null, "", `#${first.scenarioId}`);
|
|
474
|
+
}
|
|
475
|
+
}, [first]);
|
|
476
|
+
if (failures.length === 0) return null;
|
|
477
|
+
return /* @__PURE__ */ jsxs14(
|
|
478
|
+
"aside",
|
|
479
|
+
{
|
|
480
|
+
className: "es-failure-banner",
|
|
481
|
+
role: "status",
|
|
482
|
+
"aria-live": "polite",
|
|
483
|
+
"aria-label": "Failure summary",
|
|
484
|
+
children: [
|
|
485
|
+
/* @__PURE__ */ jsxs14("span", { className: "es-failure-banner-text", children: [
|
|
486
|
+
/* @__PURE__ */ jsx23("strong", { children: failures.length }),
|
|
487
|
+
" ",
|
|
488
|
+
"failure",
|
|
489
|
+
failures.length === 1 ? "" : "s"
|
|
490
|
+
] }),
|
|
491
|
+
/* @__PURE__ */ jsx23(
|
|
492
|
+
"button",
|
|
493
|
+
{
|
|
494
|
+
type: "button",
|
|
495
|
+
className: "es-failure-banner-jump",
|
|
496
|
+
onClick: jumpToFirst,
|
|
497
|
+
"aria-label": "Jump to first failure",
|
|
498
|
+
children: "Jump to first \u2193"
|
|
499
|
+
}
|
|
500
|
+
)
|
|
501
|
+
]
|
|
502
|
+
}
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/interactive/ReportShortcutsHelp.tsx
|
|
507
|
+
import { useEffect, useRef } from "react";
|
|
508
|
+
import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
509
|
+
var SHORTCUTS = [
|
|
510
|
+
{ keys: "/", description: "Focus search" },
|
|
511
|
+
{ keys: "f", description: "Jump to next failure" },
|
|
512
|
+
{ keys: "Shift+F", description: "Jump to previous failure" },
|
|
513
|
+
{ keys: "Esc", description: "Clear search / close dialog" },
|
|
514
|
+
{ keys: "?", description: "Toggle this help" }
|
|
515
|
+
];
|
|
516
|
+
function ReportShortcutsHelp({ open, onClose }) {
|
|
517
|
+
const dialogRef = useRef(null);
|
|
518
|
+
useEffect(() => {
|
|
519
|
+
const dialog = dialogRef.current;
|
|
520
|
+
if (!dialog) return;
|
|
521
|
+
if (open && !dialog.open) dialog.showModal();
|
|
522
|
+
else if (!open && dialog.open) dialog.close();
|
|
523
|
+
}, [open]);
|
|
524
|
+
return /* @__PURE__ */ jsxs15(
|
|
525
|
+
"dialog",
|
|
526
|
+
{
|
|
527
|
+
ref: dialogRef,
|
|
528
|
+
className: "es-shortcuts-help",
|
|
529
|
+
"aria-label": "Keyboard shortcuts",
|
|
530
|
+
onClose,
|
|
531
|
+
children: [
|
|
532
|
+
/* @__PURE__ */ jsx24("h2", { children: "Keyboard shortcuts" }),
|
|
533
|
+
/* @__PURE__ */ jsx24("dl", { children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs15("div", { children: [
|
|
534
|
+
/* @__PURE__ */ jsx24("dt", { children: /* @__PURE__ */ jsx24("kbd", { children: s.keys }) }),
|
|
535
|
+
/* @__PURE__ */ jsx24("dd", { children: s.description })
|
|
536
|
+
] }, s.keys)) }),
|
|
537
|
+
/* @__PURE__ */ jsx24("form", { method: "dialog", children: /* @__PURE__ */ jsx24("button", { type: "submit", className: "es-shortcuts-close", children: "Close" }) })
|
|
538
|
+
]
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/interactive/use-keyboard-shortcuts.ts
|
|
544
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
545
|
+
function isEditableTarget(target) {
|
|
546
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
547
|
+
const tag = target.tagName;
|
|
548
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
549
|
+
if (target.isContentEditable) return true;
|
|
550
|
+
return false;
|
|
551
|
+
}
|
|
552
|
+
function useKeyboardShortcuts(handlers) {
|
|
553
|
+
const ref = useRef2(handlers);
|
|
554
|
+
ref.current = handlers;
|
|
555
|
+
useEffect2(() => {
|
|
556
|
+
function onKeyDown(e) {
|
|
557
|
+
const h = ref.current;
|
|
558
|
+
if (e.key === "Escape") {
|
|
559
|
+
h.onEscape?.();
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (isEditableTarget(e.target)) return;
|
|
563
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
564
|
+
switch (e.key) {
|
|
565
|
+
case "/":
|
|
566
|
+
h.onFocusSearch?.();
|
|
567
|
+
e.preventDefault();
|
|
568
|
+
return;
|
|
569
|
+
case "?":
|
|
570
|
+
h.onToggleHelp?.();
|
|
571
|
+
e.preventDefault();
|
|
572
|
+
return;
|
|
573
|
+
case "f":
|
|
574
|
+
h.onNextFailure?.();
|
|
575
|
+
e.preventDefault();
|
|
576
|
+
return;
|
|
577
|
+
case "F":
|
|
578
|
+
h.onPrevFailure?.();
|
|
579
|
+
e.preventDefault();
|
|
580
|
+
return;
|
|
581
|
+
default:
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
window.addEventListener("keydown", onKeyDown);
|
|
586
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
587
|
+
}, []);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// src/interactive/use-deep-link-scroll.ts
|
|
591
|
+
import { useEffect as useEffect3 } from "react";
|
|
592
|
+
function useDeepLinkScroll() {
|
|
593
|
+
useEffect3(() => {
|
|
594
|
+
function scrollToHash() {
|
|
595
|
+
const hash = window.location.hash.replace(/^#/, "");
|
|
596
|
+
if (!hash) return;
|
|
597
|
+
const el = document.getElementById(hash);
|
|
598
|
+
if (!el) return;
|
|
599
|
+
requestAnimationFrame(() => {
|
|
600
|
+
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
scrollToHash();
|
|
604
|
+
window.addEventListener("hashchange", scrollToHash);
|
|
605
|
+
return () => window.removeEventListener("hashchange", scrollToHash);
|
|
606
|
+
}, []);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// src/interactive/filter.ts
|
|
610
|
+
function normalizeQuery(query) {
|
|
611
|
+
return query.trim().toLowerCase();
|
|
612
|
+
}
|
|
613
|
+
function scenarioMatches(scenario, q) {
|
|
614
|
+
if (q === "") return true;
|
|
615
|
+
if (scenario.title.toLowerCase().includes(q)) return true;
|
|
616
|
+
for (const tag of scenario.tags) {
|
|
617
|
+
if (tag.toLowerCase().includes(q)) return true;
|
|
618
|
+
}
|
|
619
|
+
for (const step of scenario.steps) {
|
|
620
|
+
if (step.text.toLowerCase().includes(q)) return true;
|
|
621
|
+
}
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
function summarizeScenarios(scenarios) {
|
|
625
|
+
let total = 0, passed = 0, failed = 0, skipped = 0, pending = 0, durationMs = 0;
|
|
626
|
+
for (const s of scenarios) {
|
|
627
|
+
total += 1;
|
|
628
|
+
durationMs += s.durationMs;
|
|
629
|
+
if (s.status === "passed") passed += 1;
|
|
630
|
+
else if (s.status === "failed") failed += 1;
|
|
631
|
+
else if (s.status === "skipped") skipped += 1;
|
|
632
|
+
else pending += 1;
|
|
633
|
+
}
|
|
634
|
+
return { total, passed, failed, skipped, pending, durationMs };
|
|
635
|
+
}
|
|
636
|
+
function filterReport(report, query) {
|
|
637
|
+
const q = normalizeQuery(query);
|
|
638
|
+
if (q === "") return report;
|
|
639
|
+
const features = [];
|
|
640
|
+
let topTotal = 0, topPassed = 0, topFailed = 0, topSkipped = 0, topPending = 0, topDuration = 0;
|
|
641
|
+
for (const feature of report.features) {
|
|
642
|
+
const matched = feature.scenarios.filter((s) => scenarioMatches(s, q));
|
|
643
|
+
if (matched.length === 0) continue;
|
|
644
|
+
const summary = summarizeScenarios(matched);
|
|
645
|
+
features.push({ ...feature, summary, scenarios: matched });
|
|
646
|
+
topTotal += summary.total;
|
|
647
|
+
topPassed += summary.passed;
|
|
648
|
+
topFailed += summary.failed;
|
|
649
|
+
topSkipped += summary.skipped;
|
|
650
|
+
topPending += summary.pending;
|
|
651
|
+
topDuration += summary.durationMs;
|
|
652
|
+
}
|
|
653
|
+
return {
|
|
654
|
+
...report,
|
|
655
|
+
summary: {
|
|
656
|
+
total: topTotal,
|
|
657
|
+
passed: topPassed,
|
|
658
|
+
failed: topFailed,
|
|
659
|
+
skipped: topSkipped,
|
|
660
|
+
pending: topPending,
|
|
661
|
+
durationMs: topDuration
|
|
662
|
+
},
|
|
663
|
+
features
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function listFailures(report) {
|
|
667
|
+
const out = [];
|
|
668
|
+
for (const feature of report.features) {
|
|
669
|
+
for (const scenario of feature.scenarios) {
|
|
670
|
+
if (scenario.status === "failed") {
|
|
671
|
+
const ref = {
|
|
672
|
+
featureId: feature.id,
|
|
673
|
+
scenarioId: scenario.id,
|
|
674
|
+
scenarioTitle: scenario.title
|
|
675
|
+
};
|
|
676
|
+
if (scenario.errorMessage !== void 0) {
|
|
677
|
+
ref.errorMessage = scenario.errorMessage;
|
|
678
|
+
}
|
|
679
|
+
out.push(ref);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return out;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// src/interactive/ReportInteractive.tsx
|
|
687
|
+
import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
688
|
+
function isResult(value) {
|
|
689
|
+
return typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean";
|
|
690
|
+
}
|
|
691
|
+
function ReportInteractive(props) {
|
|
692
|
+
const { report, className, title, dataTheme } = props;
|
|
693
|
+
if (isResult(report)) {
|
|
694
|
+
if (!report.ok) {
|
|
695
|
+
return /* @__PURE__ */ jsx25(
|
|
696
|
+
"main",
|
|
697
|
+
{
|
|
698
|
+
className: ["es-report", className].filter(Boolean).join(" "),
|
|
699
|
+
"aria-label": title ?? "Test report",
|
|
700
|
+
"data-theme": dataTheme,
|
|
701
|
+
children: /* @__PURE__ */ jsx25(ReportSchemaError, { error: report.error })
|
|
702
|
+
}
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
return /* @__PURE__ */ jsx25(ReportInteractiveView, { ...props, report: report.data });
|
|
706
|
+
}
|
|
707
|
+
return /* @__PURE__ */ jsx25(ReportInteractiveView, { ...props, report });
|
|
708
|
+
}
|
|
709
|
+
function ReportInteractiveView({
|
|
710
|
+
report,
|
|
711
|
+
customRenderers,
|
|
712
|
+
renderers,
|
|
713
|
+
className,
|
|
714
|
+
title,
|
|
715
|
+
dataTheme
|
|
716
|
+
}) {
|
|
717
|
+
const [query, setQuery] = useState("");
|
|
718
|
+
const [helpOpen, setHelpOpen] = useState(false);
|
|
719
|
+
const searchRef = useRef3(null);
|
|
720
|
+
const failures = useMemo2(() => listFailures(report), [report]);
|
|
721
|
+
const filtered = useMemo2(() => filterReport(report, query), [report, query]);
|
|
722
|
+
const failureIndexRef = useRef3(0);
|
|
723
|
+
const focusSearch = useCallback2(() => {
|
|
724
|
+
searchRef.current?.focus();
|
|
725
|
+
}, []);
|
|
726
|
+
const scrollToScenario = useCallback2((scenarioId) => {
|
|
727
|
+
if (typeof document === "undefined") return;
|
|
728
|
+
const el = document.getElementById(scenarioId);
|
|
729
|
+
if (!el) return;
|
|
730
|
+
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
731
|
+
if (typeof history !== "undefined") {
|
|
732
|
+
history.replaceState(null, "", `#${scenarioId}`);
|
|
733
|
+
}
|
|
734
|
+
}, []);
|
|
735
|
+
const stepFailure = useCallback2(
|
|
736
|
+
(direction) => {
|
|
737
|
+
if (failures.length === 0) return;
|
|
738
|
+
failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
|
|
739
|
+
const target = failures[failureIndexRef.current];
|
|
740
|
+
if (target) scrollToScenario(target.scenarioId);
|
|
741
|
+
},
|
|
742
|
+
[failures, scrollToScenario]
|
|
743
|
+
);
|
|
744
|
+
const toggleHelp = useCallback2(() => {
|
|
745
|
+
setHelpOpen((v) => !v);
|
|
746
|
+
}, []);
|
|
747
|
+
const escape = useCallback2(() => {
|
|
748
|
+
if (helpOpen) setHelpOpen(false);
|
|
749
|
+
else if (query !== "") setQuery("");
|
|
750
|
+
}, [helpOpen, query]);
|
|
751
|
+
useKeyboardShortcuts({
|
|
752
|
+
onFocusSearch: focusSearch,
|
|
753
|
+
onNextFailure: () => stepFailure(1),
|
|
754
|
+
onPrevFailure: () => stepFailure(-1),
|
|
755
|
+
onToggleHelp: toggleHelp,
|
|
756
|
+
onEscape: escape
|
|
757
|
+
});
|
|
758
|
+
useDeepLinkScroll();
|
|
759
|
+
const hasContent = filtered.features.length > 0;
|
|
760
|
+
const totalScenarios = report.summary.total;
|
|
761
|
+
const matchedScenarios = filtered.summary.total;
|
|
762
|
+
return /* @__PURE__ */ jsx25(
|
|
763
|
+
ReportRoot,
|
|
764
|
+
{
|
|
765
|
+
report: filtered,
|
|
766
|
+
customRenderers,
|
|
767
|
+
renderers,
|
|
768
|
+
children: /* @__PURE__ */ jsxs16(
|
|
769
|
+
"main",
|
|
770
|
+
{
|
|
771
|
+
className: ["es-report", "es-report-interactive", className].filter(Boolean).join(" "),
|
|
772
|
+
"aria-label": title ?? "Test report",
|
|
773
|
+
"data-theme": dataTheme,
|
|
774
|
+
children: [
|
|
775
|
+
/* @__PURE__ */ jsxs16("header", { className: "es-report-header", children: [
|
|
776
|
+
/* @__PURE__ */ jsx25("h1", { children: title ?? "Story Report" }),
|
|
777
|
+
/* @__PURE__ */ jsx25(ReportSummary, {}),
|
|
778
|
+
/* @__PURE__ */ jsx25(
|
|
779
|
+
ReportSearch,
|
|
780
|
+
{
|
|
781
|
+
ref: searchRef,
|
|
782
|
+
value: query,
|
|
783
|
+
onChange: setQuery,
|
|
784
|
+
matchedCount: matchedScenarios,
|
|
785
|
+
totalCount: totalScenarios
|
|
786
|
+
}
|
|
787
|
+
)
|
|
788
|
+
] }),
|
|
789
|
+
/* @__PURE__ */ jsx25(ReportFailureBanner, { failures }),
|
|
790
|
+
hasContent ? /* @__PURE__ */ jsx25(ReportFeatureList, {}) : /* @__PURE__ */ jsx25(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
|
|
791
|
+
/* @__PURE__ */ jsx25(
|
|
792
|
+
"button",
|
|
793
|
+
{
|
|
794
|
+
type: "button",
|
|
795
|
+
className: "es-shortcuts-trigger",
|
|
796
|
+
"aria-label": "Keyboard shortcuts",
|
|
797
|
+
"aria-keyshortcuts": "Shift+?",
|
|
798
|
+
onClick: toggleHelp,
|
|
799
|
+
children: "?"
|
|
800
|
+
}
|
|
801
|
+
),
|
|
802
|
+
/* @__PURE__ */ jsx25(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) })
|
|
803
|
+
]
|
|
804
|
+
}
|
|
805
|
+
)
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
export {
|
|
810
|
+
ReportFailureBanner,
|
|
811
|
+
ReportInteractive,
|
|
812
|
+
ReportSearch,
|
|
813
|
+
ReportShortcutsHelp,
|
|
814
|
+
filterReport,
|
|
815
|
+
listFailures,
|
|
816
|
+
normalizeQuery,
|
|
817
|
+
useDeepLinkScroll,
|
|
818
|
+
useKeyboardShortcuts
|
|
819
|
+
};
|
|
820
|
+
//# sourceMappingURL=interactive.js.map
|