executable-stories-react 0.7.0 → 0.9.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 +453 -230
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +404 -181
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +669 -377
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +35 -4
- package/dist/interactive.d.ts +35 -4
- package/dist/interactive.js +583 -291
- package/dist/interactive.js.map +1 -1
- package/dist/parse.cjs +30 -0
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +10 -1
- package/dist/parse.d.ts +10 -1
- package/dist/parse.js +30 -0
- package/dist/parse.js.map +1 -1
- package/dist/report-island.global.js +26 -26
- package/dist/ssr.cjs +356 -194
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.d.cts +10 -1
- package/dist/ssr.d.ts +10 -1
- package/dist/ssr.js +316 -154
- package/dist/ssr.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +1 -1
package/dist/interactive.cjs
CHANGED
|
@@ -47,7 +47,7 @@ __export(interactive_exports, {
|
|
|
47
47
|
module.exports = __toCommonJS(interactive_exports);
|
|
48
48
|
|
|
49
49
|
// src/interactive/ReportInteractive.tsx
|
|
50
|
-
var
|
|
50
|
+
var import_react19 = require("react");
|
|
51
51
|
|
|
52
52
|
// src/result.ts
|
|
53
53
|
var ok = (data) => ({ ok: true, data });
|
|
@@ -422,17 +422,29 @@ function DocHtml({ entry }) {
|
|
|
422
422
|
] });
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
-
// src/components/doc/
|
|
425
|
+
// src/components/doc/DocState.tsx
|
|
426
426
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
427
|
+
function DocState({ entry }) {
|
|
428
|
+
let json;
|
|
429
|
+
try {
|
|
430
|
+
json = JSON.stringify(entry.value, null, 2) ?? String(entry.value);
|
|
431
|
+
} catch {
|
|
432
|
+
json = String(entry.value);
|
|
433
|
+
}
|
|
434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("code", { className: "font-mono text-xs language-json", children: json }) });
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/components/doc/DocCustom.tsx
|
|
438
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
427
439
|
function DocCustom({ entry }) {
|
|
428
440
|
const renderers = useCustomRenderers();
|
|
429
441
|
const renderer = renderers[entry.type];
|
|
430
442
|
if (renderer) {
|
|
431
|
-
return /* @__PURE__ */ (0,
|
|
443
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: renderer(entry) });
|
|
432
444
|
}
|
|
433
|
-
return /* @__PURE__ */ (0,
|
|
434
|
-
/* @__PURE__ */ (0,
|
|
435
|
-
/* @__PURE__ */ (0,
|
|
445
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
|
|
446
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
|
|
447
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
|
|
436
448
|
] });
|
|
437
449
|
}
|
|
438
450
|
function safeStringify(value) {
|
|
@@ -444,45 +456,47 @@ function safeStringify(value) {
|
|
|
444
456
|
}
|
|
445
457
|
|
|
446
458
|
// src/components/doc/DocEntry.tsx
|
|
447
|
-
var
|
|
459
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
448
460
|
function DocEntry({ entry }) {
|
|
449
461
|
switch (entry.kind) {
|
|
450
462
|
case "note":
|
|
451
|
-
return /* @__PURE__ */ (0,
|
|
463
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocNote, { entry });
|
|
452
464
|
case "tag":
|
|
453
|
-
return /* @__PURE__ */ (0,
|
|
465
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTag, { entry });
|
|
454
466
|
case "kv":
|
|
455
|
-
return /* @__PURE__ */ (0,
|
|
467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocKv, { entry });
|
|
456
468
|
case "code":
|
|
457
|
-
return /* @__PURE__ */ (0,
|
|
469
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCode, { entry });
|
|
458
470
|
case "table":
|
|
459
|
-
return /* @__PURE__ */ (0,
|
|
471
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTable, { entry });
|
|
460
472
|
case "link":
|
|
461
|
-
return /* @__PURE__ */ (0,
|
|
473
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocLink, { entry });
|
|
462
474
|
case "section":
|
|
463
|
-
return /* @__PURE__ */ (0,
|
|
475
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocSection, { entry });
|
|
464
476
|
case "mermaid":
|
|
465
|
-
return /* @__PURE__ */ (0,
|
|
477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocMermaid, { entry });
|
|
466
478
|
case "screenshot":
|
|
467
|
-
return /* @__PURE__ */ (0,
|
|
479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocScreenshot, { entry });
|
|
468
480
|
case "video":
|
|
469
|
-
return /* @__PURE__ */ (0,
|
|
481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocVideo, { entry });
|
|
470
482
|
case "html":
|
|
471
|
-
return /* @__PURE__ */ (0,
|
|
483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocHtml, { entry });
|
|
484
|
+
case "state":
|
|
485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocState, { entry });
|
|
472
486
|
case "custom":
|
|
473
|
-
return /* @__PURE__ */ (0,
|
|
487
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCustom, { entry });
|
|
474
488
|
}
|
|
475
489
|
}
|
|
476
490
|
|
|
477
491
|
// src/components/ReportDocEntries.tsx
|
|
478
|
-
var
|
|
492
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
479
493
|
function ReportDocEntries({ entries }) {
|
|
480
494
|
if (entries.length === 0) return null;
|
|
481
|
-
return /* @__PURE__ */ (0,
|
|
495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocEntry, { entry }, i)) });
|
|
482
496
|
}
|
|
483
497
|
|
|
484
498
|
// src/lib/step-params.tsx
|
|
485
|
-
var
|
|
499
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
486
500
|
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
|
|
487
501
|
function highlightStepParams(text) {
|
|
488
502
|
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
@@ -494,7 +508,7 @@ function highlightStepParams(text) {
|
|
|
494
508
|
const end = start + match[0].length;
|
|
495
509
|
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
496
510
|
nodes.push(
|
|
497
|
-
/* @__PURE__ */ (0,
|
|
511
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
|
|
498
512
|
);
|
|
499
513
|
lastIndex = end;
|
|
500
514
|
});
|
|
@@ -503,7 +517,7 @@ function highlightStepParams(text) {
|
|
|
503
517
|
}
|
|
504
518
|
|
|
505
519
|
// src/components/ReportSteps.tsx
|
|
506
|
-
var
|
|
520
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
507
521
|
var STEP_GLYPH = {
|
|
508
522
|
passed: "\u2713",
|
|
509
523
|
failed: "\u2717",
|
|
@@ -518,21 +532,21 @@ var STEP_GLYPH_COLOR = {
|
|
|
518
532
|
};
|
|
519
533
|
function ReportSteps({ scenario }) {
|
|
520
534
|
if (scenario.steps.length === 0) return null;
|
|
521
|
-
return /* @__PURE__ */ (0,
|
|
535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportStepItem, { step }, step.id)) });
|
|
522
536
|
}
|
|
523
537
|
function ReportStepItem({ step }) {
|
|
524
538
|
const kw = step.keyword.toLowerCase();
|
|
525
539
|
const continuation = kw === "and" || kw === "but";
|
|
526
|
-
return /* @__PURE__ */ (0,
|
|
540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
527
541
|
"li",
|
|
528
542
|
{
|
|
529
543
|
id: step.id,
|
|
530
544
|
"data-status": step.status,
|
|
531
545
|
className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
|
|
532
546
|
children: [
|
|
533
|
-
/* @__PURE__ */ (0,
|
|
534
|
-
/* @__PURE__ */ (0,
|
|
535
|
-
/* @__PURE__ */ (0,
|
|
547
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.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" }),
|
|
549
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
536
550
|
"span",
|
|
537
551
|
{
|
|
538
552
|
className: cn(
|
|
@@ -542,10 +556,10 @@ function ReportStepItem({ step }) {
|
|
|
542
556
|
children: step.keyword
|
|
543
557
|
}
|
|
544
558
|
),
|
|
545
|
-
/* @__PURE__ */ (0,
|
|
546
|
-
step.durationMs > 0 ? /* @__PURE__ */ (0,
|
|
559
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
560
|
+
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
|
|
547
561
|
] }),
|
|
548
|
-
step.errorMessage ? /* @__PURE__ */ (0,
|
|
562
|
+
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
549
563
|
"pre",
|
|
550
564
|
{
|
|
551
565
|
role: "alert",
|
|
@@ -553,12 +567,72 @@ function ReportStepItem({ step }) {
|
|
|
553
567
|
children: step.errorMessage
|
|
554
568
|
}
|
|
555
569
|
) : null,
|
|
556
|
-
step.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
570
|
+
step.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportDocEntries, { entries: step.docEntries }) }) : null
|
|
557
571
|
]
|
|
558
572
|
}
|
|
559
573
|
);
|
|
560
574
|
}
|
|
561
575
|
|
|
576
|
+
// ../executable-stories-core/dist/state-diff.js
|
|
577
|
+
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
578
|
+
function deepEqual(a, b) {
|
|
579
|
+
if (Object.is(a, b)) return true;
|
|
580
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
581
|
+
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
582
|
+
}
|
|
583
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
584
|
+
const ak = Object.keys(a);
|
|
585
|
+
const bk = Object.keys(b);
|
|
586
|
+
return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
|
|
587
|
+
}
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
|
|
591
|
+
function walk(path, before, after, out) {
|
|
592
|
+
if (Object.is(before, after)) return;
|
|
593
|
+
if (Array.isArray(before) && Array.isArray(after)) {
|
|
594
|
+
const shared = Math.min(before.length, after.length);
|
|
595
|
+
for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
|
|
596
|
+
for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
|
|
597
|
+
for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (isPlainObject(before) && isPlainObject(after)) {
|
|
601
|
+
for (const key of Object.keys(after)) {
|
|
602
|
+
const p = joinKey(path, key);
|
|
603
|
+
if (key in before) walk(p, before[key], after[key], out);
|
|
604
|
+
else out.push({ path: p, kind: "added", after: after[key] });
|
|
605
|
+
}
|
|
606
|
+
for (const key of Object.keys(before)) {
|
|
607
|
+
if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
|
|
608
|
+
}
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
|
|
612
|
+
}
|
|
613
|
+
function diffStateValues(before, after) {
|
|
614
|
+
const out = [];
|
|
615
|
+
walk("", before, after, out);
|
|
616
|
+
return out;
|
|
617
|
+
}
|
|
618
|
+
function formatStateValue(value, maxLen = 60) {
|
|
619
|
+
const s = value === void 0 ? "undefined" : JSON.stringify(value);
|
|
620
|
+
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
621
|
+
}
|
|
622
|
+
function summarizeStateChanges(changes) {
|
|
623
|
+
return changes.map((c) => {
|
|
624
|
+
const at = c.path === "" ? "(root)" : c.path;
|
|
625
|
+
switch (c.kind) {
|
|
626
|
+
case "added":
|
|
627
|
+
return `+ ${at}: ${formatStateValue(c.after)}`;
|
|
628
|
+
case "removed":
|
|
629
|
+
return `\u2212 ${at}`;
|
|
630
|
+
case "changed":
|
|
631
|
+
return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
|
|
562
636
|
// ../executable-stories-core/dist/storyboard.js
|
|
563
637
|
function findScreenshot(entries) {
|
|
564
638
|
for (const entry of entries) {
|
|
@@ -570,26 +644,55 @@ function findScreenshot(entries) {
|
|
|
570
644
|
}
|
|
571
645
|
return void 0;
|
|
572
646
|
}
|
|
647
|
+
function findStates(entries, out = []) {
|
|
648
|
+
for (const entry of entries) {
|
|
649
|
+
if (entry.kind === "state") out.push(entry);
|
|
650
|
+
if (entry.children) findStates(entry.children, out);
|
|
651
|
+
}
|
|
652
|
+
return out;
|
|
653
|
+
}
|
|
654
|
+
var laneOf = (label) => label ?? "";
|
|
573
655
|
function extractStoryboardFrames(scenario) {
|
|
574
656
|
const frames = [];
|
|
657
|
+
const laneOrder = [];
|
|
658
|
+
const previous = /* @__PURE__ */ new Map();
|
|
575
659
|
for (const step of scenario.steps) {
|
|
576
660
|
const shot = findScreenshot(step.docEntries);
|
|
577
|
-
|
|
661
|
+
const stateDocs = findStates(step.docEntries);
|
|
662
|
+
if (!shot && stateDocs.length === 0) continue;
|
|
663
|
+
const byLane = /* @__PURE__ */ new Map();
|
|
664
|
+
for (const doc of stateDocs) {
|
|
665
|
+
const lane = laneOf(doc.label);
|
|
666
|
+
if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
|
|
667
|
+
byLane.set(lane, doc);
|
|
668
|
+
}
|
|
669
|
+
const states = [];
|
|
670
|
+
for (const lane of laneOrder) {
|
|
671
|
+
const doc = byLane.get(lane);
|
|
672
|
+
if (!doc) continue;
|
|
673
|
+
const card = {
|
|
674
|
+
...doc.label !== void 0 && { label: doc.label },
|
|
675
|
+
value: doc.value
|
|
676
|
+
};
|
|
677
|
+
if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
|
|
678
|
+
previous.set(lane, doc.value);
|
|
679
|
+
states.push(card);
|
|
680
|
+
}
|
|
578
681
|
frames.push({
|
|
579
682
|
stepId: step.id,
|
|
580
683
|
stepIndex: step.index,
|
|
581
684
|
keyword: step.keyword,
|
|
582
685
|
text: step.text,
|
|
583
686
|
status: step.status,
|
|
584
|
-
path: shot.path,
|
|
585
|
-
|
|
687
|
+
...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
|
|
688
|
+
states
|
|
586
689
|
});
|
|
587
690
|
}
|
|
588
691
|
return frames;
|
|
589
692
|
}
|
|
590
693
|
|
|
591
694
|
// src/components/ReportStoryboard.tsx
|
|
592
|
-
var
|
|
695
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
593
696
|
var FRAME_GLYPH = {
|
|
594
697
|
passed: "\u2713",
|
|
595
698
|
failed: "\u2717",
|
|
@@ -605,55 +708,114 @@ var FRAME_GLYPH_COLOR = {
|
|
|
605
708
|
function ReportStoryboard({ scenario }) {
|
|
606
709
|
const frames = extractStoryboardFrames(scenario);
|
|
607
710
|
if (frames.length < 2) return null;
|
|
608
|
-
return /* @__PURE__ */ (0,
|
|
609
|
-
/* @__PURE__ */ (0,
|
|
610
|
-
/* @__PURE__ */ (0,
|
|
711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
|
|
712
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
|
|
713
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StoryboardFrameItem, { frame }, frame.stepId)) })
|
|
611
714
|
] });
|
|
612
715
|
}
|
|
613
716
|
function StoryboardFrameItem({ frame }) {
|
|
614
|
-
const src = isLocalFsPath(frame.path) ? void 0 : safeImageUrl(frame.path);
|
|
615
717
|
const label = `${frame.keyword} ${frame.text}`;
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
718
|
+
const hasStates = frame.states.length > 0;
|
|
719
|
+
const caption = /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
|
|
720
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
|
|
721
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "min-w-0", children: [
|
|
722
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
|
|
723
|
+
" ",
|
|
724
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
|
|
725
|
+
] })
|
|
726
|
+
] });
|
|
727
|
+
return (
|
|
728
|
+
// State cards need room for diff rows; screenshot-only frames keep the
|
|
729
|
+
// original narrow film-cell width (and DOM) unchanged.
|
|
730
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
|
|
731
|
+
frame.screenshot ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(FrameScreenshot, { frame, label }) : null,
|
|
732
|
+
hasStates ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StateCardView, { card }, card.label ?? i)) }) : null,
|
|
733
|
+
frame.screenshot ? caption : (
|
|
734
|
+
// No screenshot means no image anchor, so the caption carries the
|
|
735
|
+
// jump-to-step link instead.
|
|
736
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
|
|
737
|
+
)
|
|
738
|
+
] })
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
function FrameScreenshot({ frame, label }) {
|
|
742
|
+
const path = frame.screenshot?.path ?? "";
|
|
743
|
+
const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
|
|
744
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
745
|
+
"a",
|
|
746
|
+
{
|
|
747
|
+
href: `#${frame.stepId}`,
|
|
748
|
+
"aria-label": `Jump to step: ${label}`,
|
|
749
|
+
className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
|
|
750
|
+
children: src ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
751
|
+
"img",
|
|
752
|
+
{
|
|
753
|
+
src,
|
|
754
|
+
alt: frame.screenshot?.alt ?? label,
|
|
755
|
+
loading: "lazy",
|
|
756
|
+
className: "h-24 w-full bg-muted object-cover object-top"
|
|
757
|
+
}
|
|
758
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
|
|
759
|
+
}
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
var CHANGE_TONE = {
|
|
763
|
+
added: "bg-pass-bg text-pass",
|
|
764
|
+
removed: "bg-fail-bg text-fail",
|
|
765
|
+
changed: "bg-skip-bg text-skip"
|
|
766
|
+
};
|
|
767
|
+
function prettyJson(value) {
|
|
768
|
+
try {
|
|
769
|
+
return JSON.stringify(value, null, 2) ?? String(value);
|
|
770
|
+
} catch {
|
|
771
|
+
return String(value);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function StateCardView({ card }) {
|
|
775
|
+
const json = prettyJson(card.value);
|
|
776
|
+
const firstCapture = card.changes === void 0;
|
|
777
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
|
|
778
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
|
|
779
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "inline-flex rounded-full border border-border px-1.5 font-mono text-[0.625rem] font-medium text-muted-foreground", children: card.label ?? "State" }),
|
|
780
|
+
firstCapture ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
|
|
781
|
+
] }),
|
|
782
|
+
!firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
783
|
+
"li",
|
|
619
784
|
{
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
785
|
+
className: cn(
|
|
786
|
+
"rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
|
|
787
|
+
CHANGE_TONE[change.kind]
|
|
788
|
+
),
|
|
789
|
+
children: summarizeStateChanges([change])[0]
|
|
790
|
+
},
|
|
791
|
+
i
|
|
792
|
+
)) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
|
|
793
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
794
|
+
"details",
|
|
795
|
+
{
|
|
796
|
+
open: firstCapture && json.length <= 240,
|
|
797
|
+
className: cn(firstCapture ? "mt-1.5" : "mt-1"),
|
|
798
|
+
children: [
|
|
799
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
|
|
800
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pre", { tabIndex: 0, className: "mt-1 max-h-48 overflow-auto rounded-sm bg-muted p-1.5 font-mono text-[0.625rem] leading-snug", children: json })
|
|
801
|
+
]
|
|
632
802
|
}
|
|
633
|
-
)
|
|
634
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
|
|
635
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
|
|
636
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "min-w-0", children: [
|
|
637
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
|
|
638
|
-
" ",
|
|
639
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-muted-foreground", children: frame.alt ?? frame.text })
|
|
640
|
-
] })
|
|
641
|
-
] })
|
|
803
|
+
)
|
|
642
804
|
] });
|
|
643
805
|
}
|
|
644
806
|
|
|
645
807
|
// src/components/ReportAttachments.tsx
|
|
646
|
-
var
|
|
808
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
647
809
|
function ReportAttachments({ attachments }) {
|
|
648
810
|
if (attachments.length === 0) return null;
|
|
649
811
|
return (
|
|
650
812
|
// role="group" (not a labelled <section>) so multiple scenarios' attachment
|
|
651
813
|
// blocks don't create duplicate "Attachments" region landmarks.
|
|
652
|
-
/* @__PURE__ */ (0,
|
|
814
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
|
|
653
815
|
const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
|
|
654
816
|
const isImage = att.mediaType.startsWith("image/");
|
|
655
|
-
return /* @__PURE__ */ (0,
|
|
656
|
-
/* @__PURE__ */ (0,
|
|
817
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
818
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
657
819
|
"img",
|
|
658
820
|
{
|
|
659
821
|
src: dataUri,
|
|
@@ -662,8 +824,8 @@ function ReportAttachments({ attachments }) {
|
|
|
662
824
|
className: "max-w-full rounded-md border border-border"
|
|
663
825
|
}
|
|
664
826
|
),
|
|
665
|
-
/* @__PURE__ */ (0,
|
|
666
|
-
] }) : /* @__PURE__ */ (0,
|
|
827
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
|
|
828
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
667
829
|
"a",
|
|
668
830
|
{
|
|
669
831
|
href: dataUri,
|
|
@@ -672,7 +834,7 @@ function ReportAttachments({ attachments }) {
|
|
|
672
834
|
children: [
|
|
673
835
|
att.name,
|
|
674
836
|
" ",
|
|
675
|
-
/* @__PURE__ */ (0,
|
|
837
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
676
838
|
"(",
|
|
677
839
|
att.mediaType,
|
|
678
840
|
")"
|
|
@@ -736,15 +898,15 @@ function toTraceRows(spans) {
|
|
|
736
898
|
roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
737
899
|
const rows = [];
|
|
738
900
|
const seen = /* @__PURE__ */ new Set();
|
|
739
|
-
const
|
|
901
|
+
const walk2 = (node, depth) => {
|
|
740
902
|
if (seen.has(node.span.spanId)) return;
|
|
741
903
|
seen.add(node.span.spanId);
|
|
742
904
|
rows.push({ ...node.span, depth });
|
|
743
|
-
for (const child of node.children)
|
|
905
|
+
for (const child of node.children) walk2(child, depth + 1);
|
|
744
906
|
};
|
|
745
|
-
for (const root of roots)
|
|
907
|
+
for (const root of roots) walk2(root, 0);
|
|
746
908
|
for (const node of byId.values()) {
|
|
747
|
-
if (!seen.has(node.span.spanId))
|
|
909
|
+
if (!seen.has(node.span.spanId)) walk2(node, 0);
|
|
748
910
|
}
|
|
749
911
|
return rows;
|
|
750
912
|
}
|
|
@@ -760,7 +922,7 @@ function traceWindow(rows) {
|
|
|
760
922
|
}
|
|
761
923
|
|
|
762
924
|
// src/components/ReportTrace.tsx
|
|
763
|
-
var
|
|
925
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
764
926
|
var BAR_COLOR = {
|
|
765
927
|
ok: "bg-pass",
|
|
766
928
|
error: "bg-fail",
|
|
@@ -771,8 +933,8 @@ function ReportTrace({ spans }) {
|
|
|
771
933
|
const rows = toTraceRows(spans);
|
|
772
934
|
if (rows.length === 0) return null;
|
|
773
935
|
const { minStartMs, totalMs } = traceWindow(rows);
|
|
774
|
-
return /* @__PURE__ */ (0,
|
|
775
|
-
/* @__PURE__ */ (0,
|
|
936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
|
|
937
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
|
|
776
938
|
"Trace (",
|
|
777
939
|
rows.length,
|
|
778
940
|
" span",
|
|
@@ -781,11 +943,11 @@ function ReportTrace({ spans }) {
|
|
|
781
943
|
formatDuration(totalMs),
|
|
782
944
|
")"
|
|
783
945
|
] }),
|
|
784
|
-
/* @__PURE__ */ (0,
|
|
946
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
|
|
785
947
|
const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
|
|
786
948
|
const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
|
|
787
|
-
return /* @__PURE__ */ (0,
|
|
788
|
-
/* @__PURE__ */ (0,
|
|
949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-2", children: [
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
789
951
|
"span",
|
|
790
952
|
{
|
|
791
953
|
className: "min-w-0 flex-1 truncate text-foreground",
|
|
@@ -794,14 +956,14 @@ function ReportTrace({ spans }) {
|
|
|
794
956
|
children: row.name
|
|
795
957
|
}
|
|
796
958
|
),
|
|
797
|
-
/* @__PURE__ */ (0,
|
|
959
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
798
960
|
"span",
|
|
799
961
|
{
|
|
800
962
|
className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
|
|
801
963
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
802
964
|
}
|
|
803
965
|
) }),
|
|
804
|
-
/* @__PURE__ */ (0,
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
|
|
805
967
|
] }, `${row.spanId}-${i}`);
|
|
806
968
|
}) })
|
|
807
969
|
] });
|
|
@@ -850,11 +1012,11 @@ function flakinessOf(entries) {
|
|
|
850
1012
|
if (transitionScore > 0.2 || failureRate > 0.3) return "unstable";
|
|
851
1013
|
return "stable";
|
|
852
1014
|
}
|
|
853
|
-
function diffSinceLastRun(
|
|
1015
|
+
function diffSinceLastRun(history, scenarios) {
|
|
854
1016
|
let hasPreviousRun = false;
|
|
855
1017
|
const delta = { newlyFailing: [], newlyPassing: [], added: [] };
|
|
856
1018
|
for (const scenario of scenarios) {
|
|
857
|
-
const entries =
|
|
1019
|
+
const entries = history[scenario.id];
|
|
858
1020
|
if (!entries || entries.length === 0) continue;
|
|
859
1021
|
const last = entries[entries.length - 1];
|
|
860
1022
|
const prev = entries[entries.length - 2];
|
|
@@ -870,7 +1032,7 @@ function diffSinceLastRun(history2, scenarios) {
|
|
|
870
1032
|
}
|
|
871
1033
|
|
|
872
1034
|
// src/components/ScenarioRunHistory.tsx
|
|
873
|
-
var
|
|
1035
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
874
1036
|
var MAX_DOTS = 10;
|
|
875
1037
|
var DOT_COLOR = {
|
|
876
1038
|
passed: "bg-pass",
|
|
@@ -884,22 +1046,22 @@ function dotTitle(e) {
|
|
|
884
1046
|
return parts.join(" \xB7 ");
|
|
885
1047
|
}
|
|
886
1048
|
function ScenarioRunHistory({ scenarioId }) {
|
|
887
|
-
const
|
|
888
|
-
const entries =
|
|
1049
|
+
const history = useScenarioHistory();
|
|
1050
|
+
const entries = history?.[scenarioId];
|
|
889
1051
|
if (!entries || entries.length < 2) return null;
|
|
890
1052
|
const recent = entries.slice(-MAX_DOTS);
|
|
891
1053
|
const flaky = flakinessOf(recent) === "flaky";
|
|
892
1054
|
const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
|
|
893
|
-
return /* @__PURE__ */ (0,
|
|
894
|
-
flaky ? /* @__PURE__ */ (0,
|
|
895
|
-
/* @__PURE__ */ (0,
|
|
1055
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
1056
|
+
flaky ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
|
|
1057
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
896
1058
|
"span",
|
|
897
1059
|
{
|
|
898
1060
|
role: "img",
|
|
899
1061
|
"aria-label": `Run history: ${summary}`,
|
|
900
1062
|
title: summary,
|
|
901
1063
|
className: "flex shrink-0 items-center gap-[3px]",
|
|
902
|
-
children: recent.map((e, i) => /* @__PURE__ */ (0,
|
|
1064
|
+
children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
903
1065
|
"span",
|
|
904
1066
|
{
|
|
905
1067
|
"aria-hidden": true,
|
|
@@ -990,7 +1152,7 @@ function stringify(value) {
|
|
|
990
1152
|
return String(value);
|
|
991
1153
|
}
|
|
992
1154
|
}
|
|
993
|
-
function docEntryToMarkdown(entry) {
|
|
1155
|
+
function docEntryToMarkdown(entry, stateLanes) {
|
|
994
1156
|
const lines = [];
|
|
995
1157
|
switch (entry.kind) {
|
|
996
1158
|
case "note":
|
|
@@ -1042,13 +1204,24 @@ function docEntryToMarkdown(entry) {
|
|
|
1042
1204
|
case "custom":
|
|
1043
1205
|
lines.push(`**${entry.type}**`, "", "```json", stringify(entry.data), "```");
|
|
1044
1206
|
break;
|
|
1207
|
+
case "state": {
|
|
1208
|
+
const lane = entry.label ?? "";
|
|
1209
|
+
lines.push(`**${entry.label ?? "State"}**`);
|
|
1210
|
+
if (stateLanes?.has(lane)) {
|
|
1211
|
+
const summary = summarizeStateChanges(diffStateValues(stateLanes.get(lane), entry.value));
|
|
1212
|
+
if (summary.length > 0) lines.push("", ...summary.map((l) => `- ${l}`));
|
|
1213
|
+
}
|
|
1214
|
+
stateLanes?.set(lane, entry.value);
|
|
1215
|
+
lines.push("", "<details>", "<summary>snapshot</summary>", "", "```json", stringify(entry.value ?? null), "```", "", "</details>");
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1045
1218
|
}
|
|
1046
1219
|
for (const child of entry.children ?? []) {
|
|
1047
|
-
lines.push("", ...docEntryToMarkdown(child));
|
|
1220
|
+
lines.push("", ...docEntryToMarkdown(child, stateLanes));
|
|
1048
1221
|
}
|
|
1049
1222
|
return lines;
|
|
1050
1223
|
}
|
|
1051
|
-
function stepLines(step, ordinal, full) {
|
|
1224
|
+
function stepLines(step, ordinal, full, stateLanes) {
|
|
1052
1225
|
const status = step.status === "passed" ? "" : ` \u2014 **${step.status}**`;
|
|
1053
1226
|
const lines = [`${ordinal}. **${step.keyword}** ${step.text}${status}`];
|
|
1054
1227
|
if (!full) return lines;
|
|
@@ -1056,7 +1229,7 @@ function stepLines(step, ordinal, full) {
|
|
|
1056
1229
|
lines.push(...step.errorMessage.split("\n").map((l) => ` > ${l}`));
|
|
1057
1230
|
}
|
|
1058
1231
|
for (const entry of step.docEntries) {
|
|
1059
|
-
lines.push("", ...docEntryToMarkdown(entry).map((l) => l === "" ? l : ` ${l}`));
|
|
1232
|
+
lines.push("", ...docEntryToMarkdown(entry, stateLanes).map((l) => l === "" ? l : ` ${l}`));
|
|
1060
1233
|
}
|
|
1061
1234
|
return lines;
|
|
1062
1235
|
}
|
|
@@ -1065,9 +1238,10 @@ function scenarioToMarkdown(scenario, options = {}) {
|
|
|
1065
1238
|
const full = variant === "full";
|
|
1066
1239
|
const lines = full ? [`# ${scenario.title}`, ""] : [`## ${scenario.title} _(${scenario.status})_`, ""];
|
|
1067
1240
|
if (full && meta.length > 0) lines.push(...meta);
|
|
1241
|
+
const stateLanes = /* @__PURE__ */ new Map();
|
|
1068
1242
|
if (scenario.steps.length > 0) {
|
|
1069
1243
|
if (full) lines.push("", "## Steps", "");
|
|
1070
|
-
scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full)));
|
|
1244
|
+
scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full, stateLanes)));
|
|
1071
1245
|
}
|
|
1072
1246
|
if (scenario.errorMessage) {
|
|
1073
1247
|
if (full) lines.push("", "## Failure", "");
|
|
@@ -1076,7 +1250,7 @@ function scenarioToMarkdown(scenario, options = {}) {
|
|
|
1076
1250
|
}
|
|
1077
1251
|
if (full && scenario.docEntries.length > 0) {
|
|
1078
1252
|
lines.push("", "## Docs", "");
|
|
1079
|
-
for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry), "");
|
|
1253
|
+
for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry, stateLanes), "");
|
|
1080
1254
|
}
|
|
1081
1255
|
return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n").trimEnd() + (full ? "\n" : "");
|
|
1082
1256
|
}
|
|
@@ -1116,9 +1290,9 @@ function scenarioPermalink(scenario) {
|
|
|
1116
1290
|
|
|
1117
1291
|
// src/components/ui/card.tsx
|
|
1118
1292
|
var React2 = require("react");
|
|
1119
|
-
var
|
|
1293
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1120
1294
|
function Card({ className, ...props }) {
|
|
1121
|
-
return /* @__PURE__ */ (0,
|
|
1295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1122
1296
|
"div",
|
|
1123
1297
|
{
|
|
1124
1298
|
"data-slot": "card",
|
|
@@ -1131,7 +1305,7 @@ function Card({ className, ...props }) {
|
|
|
1131
1305
|
);
|
|
1132
1306
|
}
|
|
1133
1307
|
function CardHeader({ className, ...props }) {
|
|
1134
|
-
return /* @__PURE__ */ (0,
|
|
1308
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1135
1309
|
"div",
|
|
1136
1310
|
{
|
|
1137
1311
|
"data-slot": "card-header",
|
|
@@ -1144,7 +1318,7 @@ function CardHeader({ className, ...props }) {
|
|
|
1144
1318
|
);
|
|
1145
1319
|
}
|
|
1146
1320
|
function CardContent({ className, ...props }) {
|
|
1147
|
-
return /* @__PURE__ */ (0,
|
|
1321
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1148
1322
|
"div",
|
|
1149
1323
|
{
|
|
1150
1324
|
"data-slot": "card-content",
|
|
@@ -1158,13 +1332,13 @@ function CardContent({ className, ...props }) {
|
|
|
1158
1332
|
var React3 = require("react");
|
|
1159
1333
|
var import_menu = require("@base-ui/react/menu");
|
|
1160
1334
|
var import_lucide_react = require("lucide-react");
|
|
1161
|
-
var
|
|
1335
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1162
1336
|
var itemClasses = "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground";
|
|
1163
1337
|
function DropdownMenu(props) {
|
|
1164
|
-
return /* @__PURE__ */ (0,
|
|
1338
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
|
|
1165
1339
|
}
|
|
1166
1340
|
function DropdownMenuTrigger(props) {
|
|
1167
|
-
return /* @__PURE__ */ (0,
|
|
1341
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
1168
1342
|
}
|
|
1169
1343
|
function DropdownMenuContent({
|
|
1170
1344
|
className,
|
|
@@ -1172,14 +1346,14 @@ function DropdownMenuContent({
|
|
|
1172
1346
|
sideOffset = 4,
|
|
1173
1347
|
...props
|
|
1174
1348
|
}) {
|
|
1175
|
-
return /* @__PURE__ */ (0,
|
|
1349
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1176
1350
|
import_menu.Menu.Positioner,
|
|
1177
1351
|
{
|
|
1178
1352
|
"data-slot": "dropdown-menu-positioner",
|
|
1179
1353
|
className: "z-50",
|
|
1180
1354
|
align,
|
|
1181
1355
|
sideOffset,
|
|
1182
|
-
children: /* @__PURE__ */ (0,
|
|
1356
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1183
1357
|
import_menu.Menu.Popup,
|
|
1184
1358
|
{
|
|
1185
1359
|
"data-slot": "dropdown-menu-content",
|
|
@@ -1197,7 +1371,7 @@ function DropdownMenuItem({
|
|
|
1197
1371
|
className,
|
|
1198
1372
|
...props
|
|
1199
1373
|
}) {
|
|
1200
|
-
return /* @__PURE__ */ (0,
|
|
1374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1201
1375
|
import_menu.Menu.Item,
|
|
1202
1376
|
{
|
|
1203
1377
|
"data-slot": "dropdown-menu-item",
|
|
@@ -1209,7 +1383,7 @@ function DropdownMenuItem({
|
|
|
1209
1383
|
|
|
1210
1384
|
// src/components/ReportScenario.tsx
|
|
1211
1385
|
var import_lucide_react2 = require("lucide-react");
|
|
1212
|
-
var
|
|
1386
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1213
1387
|
var STATUS_META = {
|
|
1214
1388
|
passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
|
|
1215
1389
|
failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
|
|
@@ -1225,7 +1399,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1225
1399
|
const collapsible = collapse !== null && !hideTitle;
|
|
1226
1400
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
1227
1401
|
const actions = useScenarioActions();
|
|
1228
|
-
return /* @__PURE__ */ (0,
|
|
1402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1229
1403
|
Card,
|
|
1230
1404
|
{
|
|
1231
1405
|
id: scenario.id,
|
|
@@ -1233,10 +1407,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1233
1407
|
...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
|
|
1234
1408
|
className: "gap-0 py-0",
|
|
1235
1409
|
children: [
|
|
1236
|
-
/* @__PURE__ */ (0,
|
|
1237
|
-
/* @__PURE__ */ (0,
|
|
1238
|
-
hideTitle ? null : /* @__PURE__ */ (0,
|
|
1239
|
-
collapsible ? /* @__PURE__ */ (0,
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardHeader, { className: "px-4 py-3", children: [
|
|
1411
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
1412
|
+
hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1413
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1240
1414
|
"button",
|
|
1241
1415
|
{
|
|
1242
1416
|
type: "button",
|
|
@@ -1245,49 +1419,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1245
1419
|
"aria-controls": bodyId,
|
|
1246
1420
|
"aria-label": `Toggle ${scenario.title}`,
|
|
1247
1421
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
1248
|
-
children: /* @__PURE__ */ (0,
|
|
1422
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
1249
1423
|
}
|
|
1250
1424
|
) : null,
|
|
1251
|
-
/* @__PURE__ */ (0,
|
|
1252
|
-
/* @__PURE__ */ (0,
|
|
1425
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
|
|
1426
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: scenario.title })
|
|
1253
1427
|
] }),
|
|
1254
|
-
/* @__PURE__ */ (0,
|
|
1255
|
-
/* @__PURE__ */ (0,
|
|
1256
|
-
scenario.durationMs > 0 ? /* @__PURE__ */ (0,
|
|
1257
|
-
meta.showPill ? /* @__PURE__ */ (0,
|
|
1258
|
-
actions ? /* @__PURE__ */ (0,
|
|
1259
|
-
/* @__PURE__ */ (0,
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1430
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
|
|
1431
|
+
meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
|
|
1432
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenu, { children: [
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1260
1434
|
DropdownMenuTrigger,
|
|
1261
1435
|
{
|
|
1262
1436
|
"aria-label": `Actions for ${scenario.title}`,
|
|
1263
1437
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
1264
|
-
children: /* @__PURE__ */ (0,
|
|
1438
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
|
|
1265
1439
|
}
|
|
1266
1440
|
),
|
|
1267
|
-
/* @__PURE__ */ (0,
|
|
1268
|
-
/* @__PURE__ */ (0,
|
|
1269
|
-
/* @__PURE__ */ (0,
|
|
1441
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuContent, { align: "end", children: [
|
|
1442
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
|
|
1443
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.LinkIcon, {}),
|
|
1270
1444
|
" Copy link"
|
|
1271
1445
|
] }),
|
|
1272
|
-
/* @__PURE__ */ (0,
|
|
1273
|
-
/* @__PURE__ */ (0,
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
|
|
1447
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.FileTextIcon, {}),
|
|
1274
1448
|
" Copy as Markdown"
|
|
1275
1449
|
] }),
|
|
1276
|
-
/* @__PURE__ */ (0,
|
|
1277
|
-
/* @__PURE__ */ (0,
|
|
1450
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
|
|
1451
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.SparklesIcon, {}),
|
|
1278
1452
|
" Explain with AI"
|
|
1279
1453
|
] })
|
|
1280
1454
|
] })
|
|
1281
1455
|
] }) : null
|
|
1282
1456
|
] })
|
|
1283
1457
|
] }),
|
|
1284
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0,
|
|
1285
|
-
scenario.tags.map((t) => /* @__PURE__ */ (0,
|
|
1286
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0,
|
|
1458
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
1459
|
+
scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
1460
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
1287
1461
|
] }) : null
|
|
1288
1462
|
] }),
|
|
1289
|
-
/* @__PURE__ */ (0,
|
|
1290
|
-
scenario.errorMessage ? /* @__PURE__ */ (0,
|
|
1463
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1464
|
+
scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1291
1465
|
"pre",
|
|
1292
1466
|
{
|
|
1293
1467
|
role: "alert",
|
|
@@ -1295,11 +1469,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1295
1469
|
children: scenario.errorMessage
|
|
1296
1470
|
}
|
|
1297
1471
|
) : null,
|
|
1298
|
-
/* @__PURE__ */ (0,
|
|
1299
|
-
/* @__PURE__ */ (0,
|
|
1300
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
1301
|
-
/* @__PURE__ */ (0,
|
|
1302
|
-
/* @__PURE__ */ (0,
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportStoryboard, { scenario }),
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportSteps, { scenario }),
|
|
1474
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
1475
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportAttachments, { attachments: scenario.attachments }),
|
|
1476
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportTrace, { spans: scenario.otelSpans })
|
|
1303
1477
|
] })
|
|
1304
1478
|
]
|
|
1305
1479
|
}
|
|
@@ -1307,13 +1481,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1307
1481
|
}
|
|
1308
1482
|
|
|
1309
1483
|
// src/components/ReportScenarioList.tsx
|
|
1310
|
-
var
|
|
1484
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1311
1485
|
function ReportScenarioList({ feature }) {
|
|
1312
|
-
return /* @__PURE__ */ (0,
|
|
1486
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportScenario, { scenario }, scenario.id)) });
|
|
1313
1487
|
}
|
|
1314
1488
|
|
|
1315
1489
|
// src/components/ReportFeature.tsx
|
|
1316
|
-
var
|
|
1490
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1317
1491
|
function ReportFeature({ feature }) {
|
|
1318
1492
|
const titleId = `${feature.id}-title`;
|
|
1319
1493
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1324,7 +1498,7 @@ function ReportFeature({ feature }) {
|
|
|
1324
1498
|
return (
|
|
1325
1499
|
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1326
1500
|
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1327
|
-
/* @__PURE__ */ (0,
|
|
1501
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1328
1502
|
"section",
|
|
1329
1503
|
{
|
|
1330
1504
|
id: feature.id,
|
|
@@ -1332,9 +1506,9 @@ function ReportFeature({ feature }) {
|
|
|
1332
1506
|
"aria-labelledby": titleId,
|
|
1333
1507
|
className: "flex flex-col gap-3",
|
|
1334
1508
|
children: [
|
|
1335
|
-
/* @__PURE__ */ (0,
|
|
1336
|
-
/* @__PURE__ */ (0,
|
|
1337
|
-
/* @__PURE__ */ (0,
|
|
1509
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
|
|
1510
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "min-w-0", children: [
|
|
1511
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1338
1512
|
"button",
|
|
1339
1513
|
{
|
|
1340
1514
|
type: "button",
|
|
@@ -1343,31 +1517,31 @@ function ReportFeature({ feature }) {
|
|
|
1343
1517
|
"aria-controls": bodyId,
|
|
1344
1518
|
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1345
1519
|
children: [
|
|
1346
|
-
/* @__PURE__ */ (0,
|
|
1347
|
-
/* @__PURE__ */ (0,
|
|
1520
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate", children: feature.title })
|
|
1348
1522
|
]
|
|
1349
1523
|
}
|
|
1350
1524
|
) : feature.title }),
|
|
1351
|
-
/* @__PURE__ */ (0,
|
|
1525
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
|
|
1352
1526
|
] }),
|
|
1353
|
-
/* @__PURE__ */ (0,
|
|
1527
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1354
1528
|
"p",
|
|
1355
1529
|
{
|
|
1356
1530
|
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1357
1531
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1358
1532
|
children: [
|
|
1359
|
-
/* @__PURE__ */ (0,
|
|
1533
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
|
|
1360
1534
|
s.passed,
|
|
1361
1535
|
" passed"
|
|
1362
1536
|
] }),
|
|
1363
|
-
/* @__PURE__ */ (0,
|
|
1364
|
-
/* @__PURE__ */ (0,
|
|
1537
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1538
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
|
|
1365
1539
|
s.failed,
|
|
1366
1540
|
" failed"
|
|
1367
1541
|
] }),
|
|
1368
|
-
skipped > 0 ? /* @__PURE__ */ (0,
|
|
1369
|
-
/* @__PURE__ */ (0,
|
|
1370
|
-
/* @__PURE__ */ (0,
|
|
1542
|
+
skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
1543
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1544
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
|
|
1371
1545
|
skipped,
|
|
1372
1546
|
" skipped"
|
|
1373
1547
|
] })
|
|
@@ -1376,7 +1550,7 @@ function ReportFeature({ feature }) {
|
|
|
1376
1550
|
}
|
|
1377
1551
|
)
|
|
1378
1552
|
] }),
|
|
1379
|
-
/* @__PURE__ */ (0,
|
|
1553
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportScenarioList, { feature }) })
|
|
1380
1554
|
]
|
|
1381
1555
|
}
|
|
1382
1556
|
)
|
|
@@ -1384,17 +1558,17 @@ function ReportFeature({ feature }) {
|
|
|
1384
1558
|
}
|
|
1385
1559
|
|
|
1386
1560
|
// src/components/ReportFeatureList.tsx
|
|
1387
|
-
var
|
|
1561
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1388
1562
|
function ReportFeatureList() {
|
|
1389
1563
|
const report = useReport();
|
|
1390
|
-
return /* @__PURE__ */ (0,
|
|
1564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ReportFeature, { feature }, feature.id)) });
|
|
1391
1565
|
}
|
|
1392
1566
|
|
|
1393
1567
|
// src/components/ui/empty.tsx
|
|
1394
1568
|
var import_class_variance_authority2 = require("class-variance-authority");
|
|
1395
|
-
var
|
|
1569
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1396
1570
|
function Empty({ className, ...props }) {
|
|
1397
|
-
return /* @__PURE__ */ (0,
|
|
1571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1398
1572
|
"div",
|
|
1399
1573
|
{
|
|
1400
1574
|
"data-slot": "empty",
|
|
@@ -1407,7 +1581,7 @@ function Empty({ className, ...props }) {
|
|
|
1407
1581
|
);
|
|
1408
1582
|
}
|
|
1409
1583
|
function EmptyHeader({ className, ...props }) {
|
|
1410
|
-
return /* @__PURE__ */ (0,
|
|
1584
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1411
1585
|
"div",
|
|
1412
1586
|
{
|
|
1413
1587
|
"data-slot": "empty-header",
|
|
@@ -1434,7 +1608,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
|
1434
1608
|
}
|
|
1435
1609
|
);
|
|
1436
1610
|
function EmptyTitle({ className, ...props }) {
|
|
1437
|
-
return /* @__PURE__ */ (0,
|
|
1611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1438
1612
|
"div",
|
|
1439
1613
|
{
|
|
1440
1614
|
"data-slot": "empty-title",
|
|
@@ -1445,16 +1619,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1445
1619
|
}
|
|
1446
1620
|
|
|
1447
1621
|
// src/components/ReportEmpty.tsx
|
|
1448
|
-
var
|
|
1622
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1449
1623
|
function ReportEmpty({ message }) {
|
|
1450
|
-
return /* @__PURE__ */ (0,
|
|
1624
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1451
1625
|
}
|
|
1452
1626
|
|
|
1453
1627
|
// src/components/ReportSummary.tsx
|
|
1454
|
-
var
|
|
1628
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1455
1629
|
function ReportSummary({ className }) {
|
|
1456
1630
|
const report = useReport();
|
|
1457
|
-
return /* @__PURE__ */ (0,
|
|
1631
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1458
1632
|
ReportSummaryView,
|
|
1459
1633
|
{
|
|
1460
1634
|
summary: report.summary,
|
|
@@ -1480,7 +1654,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1480
1654
|
if (summary.pending > 0) {
|
|
1481
1655
|
cards.push({ status: "pending", label: "Pending", value: summary.pending });
|
|
1482
1656
|
}
|
|
1483
|
-
return /* @__PURE__ */ (0,
|
|
1657
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1484
1658
|
"dl",
|
|
1485
1659
|
{
|
|
1486
1660
|
className: cn(
|
|
@@ -1493,17 +1667,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1493
1667
|
"aria-label": ariaLabel,
|
|
1494
1668
|
children: cards.map((c) => {
|
|
1495
1669
|
const tone = TONE[c.status];
|
|
1496
|
-
return /* @__PURE__ */ (0,
|
|
1670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1497
1671
|
"div",
|
|
1498
1672
|
{
|
|
1499
1673
|
"data-status": c.status,
|
|
1500
1674
|
className: "rounded-lg border bg-card px-4 py-3",
|
|
1501
1675
|
children: [
|
|
1502
|
-
/* @__PURE__ */ (0,
|
|
1503
|
-
/* @__PURE__ */ (0,
|
|
1676
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
1677
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
|
|
1504
1678
|
c.label
|
|
1505
1679
|
] }),
|
|
1506
|
-
/* @__PURE__ */ (0,
|
|
1680
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
|
|
1507
1681
|
]
|
|
1508
1682
|
},
|
|
1509
1683
|
c.status
|
|
@@ -1563,7 +1737,7 @@ function prUrl(ci) {
|
|
|
1563
1737
|
}
|
|
1564
1738
|
|
|
1565
1739
|
// src/components/ReportMeta.tsx
|
|
1566
|
-
var
|
|
1740
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1567
1741
|
function ReportMeta() {
|
|
1568
1742
|
const report = useReport();
|
|
1569
1743
|
const items = [];
|
|
@@ -1586,13 +1760,13 @@ function ReportMeta() {
|
|
|
1586
1760
|
const short = sha.slice(0, 8);
|
|
1587
1761
|
items.push({
|
|
1588
1762
|
label: "Commit",
|
|
1589
|
-
value: url ? /* @__PURE__ */ (0,
|
|
1763
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1590
1764
|
});
|
|
1591
1765
|
}
|
|
1592
1766
|
if (report.ci) {
|
|
1593
1767
|
items.push({
|
|
1594
1768
|
label: "CI",
|
|
1595
|
-
value: report.ci.url ? /* @__PURE__ */ (0,
|
|
1769
|
+
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1596
1770
|
ciDisplayName(report.ci.name),
|
|
1597
1771
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1598
1772
|
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
@@ -1603,7 +1777,7 @@ function ReportMeta() {
|
|
|
1603
1777
|
const label = `#${report.ci.prNumber}`;
|
|
1604
1778
|
items.push({
|
|
1605
1779
|
label: "PR",
|
|
1606
|
-
value: url ? /* @__PURE__ */ (0,
|
|
1780
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1607
1781
|
});
|
|
1608
1782
|
}
|
|
1609
1783
|
if (items.length === 0) return null;
|
|
@@ -1611,50 +1785,50 @@ function ReportMeta() {
|
|
|
1611
1785
|
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1612
1786
|
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1613
1787
|
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1614
|
-
/* @__PURE__ */ (0,
|
|
1615
|
-
/* @__PURE__ */ (0,
|
|
1616
|
-
/* @__PURE__ */ (0,
|
|
1788
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1789
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1790
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1617
1791
|
"Run details"
|
|
1618
1792
|
] }),
|
|
1619
|
-
/* @__PURE__ */ (0,
|
|
1620
|
-
/* @__PURE__ */ (0,
|
|
1621
|
-
/* @__PURE__ */ (0,
|
|
1793
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1794
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1795
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1622
1796
|
] }, item.label)) })
|
|
1623
1797
|
] })
|
|
1624
1798
|
);
|
|
1625
1799
|
}
|
|
1626
1800
|
|
|
1627
1801
|
// src/components/ReportSchemaError.tsx
|
|
1628
|
-
var
|
|
1802
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1629
1803
|
function ReportSchemaError({ error }) {
|
|
1630
|
-
return /* @__PURE__ */ (0,
|
|
1631
|
-
/* @__PURE__ */ (0,
|
|
1632
|
-
/* @__PURE__ */ (0,
|
|
1633
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0,
|
|
1804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1805
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1806
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: error.message }),
|
|
1807
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { children: [
|
|
1634
1808
|
"The report bundle is newer than this version of ",
|
|
1635
|
-
/* @__PURE__ */ (0,
|
|
1809
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: "executable-stories-react" }),
|
|
1636
1810
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1637
1811
|
] }) : null,
|
|
1638
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0,
|
|
1639
|
-
/* @__PURE__ */ (0,
|
|
1812
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("details", { children: [
|
|
1813
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("summary", { children: [
|
|
1640
1814
|
error.issues.length,
|
|
1641
1815
|
" validation issue",
|
|
1642
1816
|
error.issues.length === 1 ? "" : "s"
|
|
1643
1817
|
] }),
|
|
1644
|
-
/* @__PURE__ */ (0,
|
|
1818
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1645
1819
|
] }) : null
|
|
1646
1820
|
] });
|
|
1647
1821
|
}
|
|
1648
1822
|
|
|
1649
1823
|
// src/components/ReportShell.tsx
|
|
1650
|
-
var
|
|
1824
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1651
1825
|
function ReportErrorShell({
|
|
1652
1826
|
error,
|
|
1653
1827
|
className,
|
|
1654
1828
|
title,
|
|
1655
1829
|
dataTheme
|
|
1656
1830
|
}) {
|
|
1657
|
-
return /* @__PURE__ */ (0,
|
|
1831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportSchemaError, { error }) });
|
|
1658
1832
|
}
|
|
1659
1833
|
|
|
1660
1834
|
// src/interactive/ReportInteractive.tsx
|
|
@@ -1663,12 +1837,12 @@ var import_lucide_react5 = require("lucide-react");
|
|
|
1663
1837
|
// src/components/ui/switch.tsx
|
|
1664
1838
|
var React4 = require("react");
|
|
1665
1839
|
var import_switch = require("@base-ui/react/switch");
|
|
1666
|
-
var
|
|
1840
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1667
1841
|
function Switch({
|
|
1668
1842
|
className,
|
|
1669
1843
|
...props
|
|
1670
1844
|
}) {
|
|
1671
|
-
return /* @__PURE__ */ (0,
|
|
1845
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1672
1846
|
import_switch.Switch.Root,
|
|
1673
1847
|
{
|
|
1674
1848
|
"data-slot": "switch",
|
|
@@ -1677,7 +1851,7 @@ function Switch({
|
|
|
1677
1851
|
className
|
|
1678
1852
|
),
|
|
1679
1853
|
...props,
|
|
1680
|
-
children: /* @__PURE__ */ (0,
|
|
1854
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1681
1855
|
import_switch.Switch.Thumb,
|
|
1682
1856
|
{
|
|
1683
1857
|
"data-slot": "switch-thumb",
|
|
@@ -1693,7 +1867,7 @@ var React5 = __toESM(require("react"), 1);
|
|
|
1693
1867
|
var import_toggle_group = require("@base-ui/react/toggle-group");
|
|
1694
1868
|
var import_toggle = require("@base-ui/react/toggle");
|
|
1695
1869
|
var import_class_variance_authority3 = require("class-variance-authority");
|
|
1696
|
-
var
|
|
1870
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1697
1871
|
var toggleItemVariants = (0, import_class_variance_authority3.cva)(
|
|
1698
1872
|
"inline-flex items-center justify-center gap-1.5 rounded-md font-medium whitespace-nowrap text-muted-foreground transition-[color,background-color,box-shadow] outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-background data-[pressed]:text-foreground data-[pressed]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1699
1873
|
{
|
|
@@ -1715,13 +1889,13 @@ function ToggleGroup({
|
|
|
1715
1889
|
children,
|
|
1716
1890
|
...props
|
|
1717
1891
|
}) {
|
|
1718
|
-
return /* @__PURE__ */ (0,
|
|
1892
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1719
1893
|
import_toggle_group.ToggleGroup,
|
|
1720
1894
|
{
|
|
1721
1895
|
"data-slot": "toggle-group",
|
|
1722
1896
|
className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
|
|
1723
1897
|
...props,
|
|
1724
|
-
children: /* @__PURE__ */ (0,
|
|
1898
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
|
|
1725
1899
|
}
|
|
1726
1900
|
);
|
|
1727
1901
|
}
|
|
@@ -1732,7 +1906,7 @@ function ToggleGroupItem({
|
|
|
1732
1906
|
...props
|
|
1733
1907
|
}) {
|
|
1734
1908
|
const context = React5.useContext(ToggleGroupContext);
|
|
1735
|
-
return /* @__PURE__ */ (0,
|
|
1909
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1736
1910
|
import_toggle.Toggle,
|
|
1737
1911
|
{
|
|
1738
1912
|
"data-slot": "toggle-group-item",
|
|
@@ -1749,9 +1923,9 @@ var import_lucide_react3 = require("lucide-react");
|
|
|
1749
1923
|
|
|
1750
1924
|
// src/components/ui/input.tsx
|
|
1751
1925
|
var React6 = require("react");
|
|
1752
|
-
var
|
|
1926
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1753
1927
|
function Input({ className, type, ...props }) {
|
|
1754
|
-
return /* @__PURE__ */ (0,
|
|
1928
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1755
1929
|
"input",
|
|
1756
1930
|
{
|
|
1757
1931
|
type,
|
|
@@ -1768,7 +1942,7 @@ function Input({ className, type, ...props }) {
|
|
|
1768
1942
|
}
|
|
1769
1943
|
|
|
1770
1944
|
// src/interactive/ReportSearch.tsx
|
|
1771
|
-
var
|
|
1945
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1772
1946
|
var ReportSearch = (0, import_react8.forwardRef)(
|
|
1773
1947
|
function ReportSearch2(props, ref) {
|
|
1774
1948
|
const {
|
|
@@ -1790,17 +1964,17 @@ var ReportSearch = (0, import_react8.forwardRef)(
|
|
|
1790
1964
|
e.preventDefault();
|
|
1791
1965
|
}
|
|
1792
1966
|
}
|
|
1793
|
-
return /* @__PURE__ */ (0,
|
|
1794
|
-
/* @__PURE__ */ (0,
|
|
1795
|
-
/* @__PURE__ */ (0,
|
|
1796
|
-
/* @__PURE__ */ (0,
|
|
1967
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
|
|
1968
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "relative w-full sm:w-72", children: [
|
|
1970
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1797
1971
|
import_lucide_react3.Search,
|
|
1798
1972
|
{
|
|
1799
1973
|
"aria-hidden": true,
|
|
1800
1974
|
className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
|
|
1801
1975
|
}
|
|
1802
1976
|
),
|
|
1803
|
-
/* @__PURE__ */ (0,
|
|
1977
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1804
1978
|
Input,
|
|
1805
1979
|
{
|
|
1806
1980
|
ref,
|
|
@@ -1817,7 +1991,7 @@ var ReportSearch = (0, import_react8.forwardRef)(
|
|
|
1817
1991
|
}
|
|
1818
1992
|
)
|
|
1819
1993
|
] }),
|
|
1820
|
-
showCounts ? /* @__PURE__ */ (0,
|
|
1994
|
+
showCounts ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
1821
1995
|
"span",
|
|
1822
1996
|
{
|
|
1823
1997
|
className: "text-xs tabular-nums text-muted-foreground",
|
|
@@ -1837,6 +2011,54 @@ var ReportSearch = (0, import_react8.forwardRef)(
|
|
|
1837
2011
|
// src/interactive/ReportFailureBanner.tsx
|
|
1838
2012
|
var import_react9 = require("react");
|
|
1839
2013
|
|
|
2014
|
+
// src/lib/hash-state.ts
|
|
2015
|
+
var DEFAULT_URL_STATE = {
|
|
2016
|
+
query: "",
|
|
2017
|
+
status: "all",
|
|
2018
|
+
tags: [],
|
|
2019
|
+
detail: "full"
|
|
2020
|
+
};
|
|
2021
|
+
var STATUSES = ["all", "passed", "failed", "skipped", "pending"];
|
|
2022
|
+
function splitHash(hash) {
|
|
2023
|
+
const raw = hash.replace(/^#/, "");
|
|
2024
|
+
const split = raw.indexOf("?");
|
|
2025
|
+
if (split === -1) return { anchor: raw, params: new URLSearchParams() };
|
|
2026
|
+
return { anchor: raw.slice(0, split), params: new URLSearchParams(raw.slice(split + 1)) };
|
|
2027
|
+
}
|
|
2028
|
+
function decodeUrlState(params) {
|
|
2029
|
+
const status = params.get("status");
|
|
2030
|
+
const tags = params.get("tags");
|
|
2031
|
+
return {
|
|
2032
|
+
query: params.get("q") ?? DEFAULT_URL_STATE.query,
|
|
2033
|
+
status: STATUSES.includes(status) ? status : DEFAULT_URL_STATE.status,
|
|
2034
|
+
tags: tags ? tags.split(",").filter(Boolean) : [],
|
|
2035
|
+
detail: params.get("docs") === "0" ? "minimal" : "full"
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
function encodeUrlState(state) {
|
|
2039
|
+
const params = new URLSearchParams();
|
|
2040
|
+
if (state.query) params.set("q", state.query);
|
|
2041
|
+
if (state.status !== "all") params.set("status", state.status);
|
|
2042
|
+
if (state.tags.length > 0) params.set("tags", state.tags.join(","));
|
|
2043
|
+
if (state.detail === "minimal") params.set("docs", "0");
|
|
2044
|
+
return params.toString();
|
|
2045
|
+
}
|
|
2046
|
+
function formatHash(anchor, params) {
|
|
2047
|
+
if (params) return `#${anchor}?${params}`;
|
|
2048
|
+
return anchor ? `#${anchor}` : "";
|
|
2049
|
+
}
|
|
2050
|
+
function writeHash(anchor, params) {
|
|
2051
|
+
if (typeof window === "undefined") return;
|
|
2052
|
+
const next = formatHash(anchor, params);
|
|
2053
|
+
if (next === window.location.hash) return;
|
|
2054
|
+
const url = next || window.location.href.split("#")[0];
|
|
2055
|
+
try {
|
|
2056
|
+
window.history.replaceState(window.history.state, "", url);
|
|
2057
|
+
} catch {
|
|
2058
|
+
window.location.hash = next.slice(1);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
|
|
1840
2062
|
// src/lib/scroll.ts
|
|
1841
2063
|
function prefersReducedMotion() {
|
|
1842
2064
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
@@ -1852,13 +2074,13 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
1852
2074
|
behavior: prefersReducedMotion() ? "auto" : "smooth",
|
|
1853
2075
|
block: "start"
|
|
1854
2076
|
});
|
|
1855
|
-
if (options.updateHash !== false
|
|
1856
|
-
|
|
2077
|
+
if (options.updateHash !== false) {
|
|
2078
|
+
writeHash(scenarioId, splitHash(window.location.hash).params.toString());
|
|
1857
2079
|
}
|
|
1858
2080
|
}
|
|
1859
2081
|
|
|
1860
2082
|
// src/interactive/ReportFailureBanner.tsx
|
|
1861
|
-
var
|
|
2083
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
1862
2084
|
function ReportFailureBanner({ failures }) {
|
|
1863
2085
|
const first = failures[0];
|
|
1864
2086
|
const jumpToFirst = (0, import_react9.useCallback)(() => {
|
|
@@ -1866,7 +2088,7 @@ function ReportFailureBanner({ failures }) {
|
|
|
1866
2088
|
scrollToScenarioId(first.scenarioId);
|
|
1867
2089
|
}, [first]);
|
|
1868
2090
|
if (failures.length === 0) return null;
|
|
1869
|
-
return /* @__PURE__ */ (0,
|
|
2091
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
1870
2092
|
"div",
|
|
1871
2093
|
{
|
|
1872
2094
|
className: "es-failure-banner",
|
|
@@ -1874,13 +2096,13 @@ function ReportFailureBanner({ failures }) {
|
|
|
1874
2096
|
"aria-live": "polite",
|
|
1875
2097
|
"aria-label": "Failure summary",
|
|
1876
2098
|
children: [
|
|
1877
|
-
/* @__PURE__ */ (0,
|
|
1878
|
-
/* @__PURE__ */ (0,
|
|
2099
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "es-failure-banner-text", children: [
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("strong", { children: failures.length }),
|
|
1879
2101
|
" ",
|
|
1880
2102
|
"failure",
|
|
1881
2103
|
failures.length === 1 ? "" : "s"
|
|
1882
2104
|
] }),
|
|
1883
|
-
/* @__PURE__ */ (0,
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1884
2106
|
"button",
|
|
1885
2107
|
{
|
|
1886
2108
|
type: "button",
|
|
@@ -1897,7 +2119,7 @@ function ReportFailureBanner({ failures }) {
|
|
|
1897
2119
|
|
|
1898
2120
|
// src/interactive/ReportFreshness.tsx
|
|
1899
2121
|
var import_react10 = require("react");
|
|
1900
|
-
var
|
|
2122
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1901
2123
|
function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
1902
2124
|
const [now, setNow] = (0, import_react10.useState)(nowMs);
|
|
1903
2125
|
(0, import_react10.useEffect)(() => {
|
|
@@ -1905,7 +2127,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
1905
2127
|
}, [nowMs]);
|
|
1906
2128
|
if (lastRunMs === void 0 || now === void 0) return null;
|
|
1907
2129
|
const age = formatRelativeAge(lastRunMs, now);
|
|
1908
|
-
const runLink = ciUrl ? /* @__PURE__ */ (0,
|
|
2130
|
+
const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
1909
2131
|
"a",
|
|
1910
2132
|
{
|
|
1911
2133
|
href: ciUrl,
|
|
@@ -1916,9 +2138,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
1916
2138
|
}
|
|
1917
2139
|
) : null;
|
|
1918
2140
|
if (isReportStale(lastRunMs, staleAfterDays, now)) {
|
|
1919
|
-
return /* @__PURE__ */ (0,
|
|
1920
|
-
/* @__PURE__ */ (0,
|
|
1921
|
-
/* @__PURE__ */ (0,
|
|
2141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
|
|
2142
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
|
|
2143
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("strong", { children: [
|
|
1922
2144
|
"Last verified ",
|
|
1923
2145
|
age,
|
|
1924
2146
|
"."
|
|
@@ -1928,10 +2150,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
1928
2150
|
runLink
|
|
1929
2151
|
] });
|
|
1930
2152
|
}
|
|
1931
|
-
return /* @__PURE__ */ (0,
|
|
2153
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
|
|
1932
2154
|
"Verified ",
|
|
1933
2155
|
age,
|
|
1934
|
-
runLink ? /* @__PURE__ */ (0,
|
|
2156
|
+
runLink ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
1935
2157
|
" \xB7 ",
|
|
1936
2158
|
runLink
|
|
1937
2159
|
] }) : null
|
|
@@ -1939,15 +2161,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
1939
2161
|
}
|
|
1940
2162
|
|
|
1941
2163
|
// src/interactive/ReportLastRunDelta.tsx
|
|
1942
|
-
var
|
|
2164
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
1943
2165
|
var MAX_LINKS = 5;
|
|
1944
2166
|
function ScenarioLinks({ scenarios, tone }) {
|
|
1945
|
-
return /* @__PURE__ */ (0,
|
|
1946
|
-
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0,
|
|
2167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2168
|
+
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
1947
2169
|
i > 0 ? ", " : "",
|
|
1948
|
-
/* @__PURE__ */ (0,
|
|
2170
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
|
|
1949
2171
|
] }, s.id)),
|
|
1950
|
-
scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0,
|
|
2172
|
+
scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
1951
2173
|
" and ",
|
|
1952
2174
|
scenarios.length - MAX_LINKS,
|
|
1953
2175
|
" more"
|
|
@@ -1955,32 +2177,32 @@ function ScenarioLinks({ scenarios, tone }) {
|
|
|
1955
2177
|
] });
|
|
1956
2178
|
}
|
|
1957
2179
|
function ReportLastRunDelta({
|
|
1958
|
-
history
|
|
2180
|
+
history,
|
|
1959
2181
|
report
|
|
1960
2182
|
}) {
|
|
1961
|
-
if (!
|
|
2183
|
+
if (!history) return null;
|
|
1962
2184
|
const scenarios = report.features.flatMap((f) => f.scenarios.map((s) => ({ id: s.id, title: s.title })));
|
|
1963
|
-
const delta = diffSinceLastRun(
|
|
2185
|
+
const delta = diffSinceLastRun(history, scenarios);
|
|
1964
2186
|
if (!delta) return null;
|
|
1965
2187
|
const { newlyFailing, newlyPassing, added } = delta;
|
|
1966
2188
|
if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
|
|
1967
|
-
return /* @__PURE__ */ (0,
|
|
2189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
|
|
1968
2190
|
}
|
|
1969
|
-
return /* @__PURE__ */ (0,
|
|
1970
|
-
/* @__PURE__ */ (0,
|
|
1971
|
-
newlyFailing.length > 0 ? /* @__PURE__ */ (0,
|
|
2191
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
|
|
2192
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
|
|
2193
|
+
newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-fail", children: [
|
|
1972
2194
|
newlyFailing.length,
|
|
1973
2195
|
" newly failing (",
|
|
1974
|
-
/* @__PURE__ */ (0,
|
|
2196
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
|
|
1975
2197
|
")"
|
|
1976
2198
|
] }) : null,
|
|
1977
|
-
newlyPassing.length > 0 ? /* @__PURE__ */ (0,
|
|
2199
|
+
newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-pass", children: [
|
|
1978
2200
|
newlyPassing.length,
|
|
1979
2201
|
" fixed (",
|
|
1980
|
-
/* @__PURE__ */ (0,
|
|
2202
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
|
|
1981
2203
|
")"
|
|
1982
2204
|
] }) : null,
|
|
1983
|
-
added.length > 0 ? /* @__PURE__ */ (0,
|
|
2205
|
+
added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2", children: [
|
|
1984
2206
|
added.length,
|
|
1985
2207
|
" new"
|
|
1986
2208
|
] }) : null
|
|
@@ -2044,7 +2266,7 @@ function Button({
|
|
|
2044
2266
|
}
|
|
2045
2267
|
|
|
2046
2268
|
// src/interactive/ReportShortcutsHelp.tsx
|
|
2047
|
-
var
|
|
2269
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2048
2270
|
var SHORTCUTS = [
|
|
2049
2271
|
{ keys: "/", description: "Focus search" },
|
|
2050
2272
|
{ keys: "f", description: "Jump to next failure" },
|
|
@@ -2056,9 +2278,9 @@ var SHORTCUTS = [
|
|
|
2056
2278
|
];
|
|
2057
2279
|
function Keys({ keys }) {
|
|
2058
2280
|
const parts = keys.split(" ");
|
|
2059
|
-
return /* @__PURE__ */ (0,
|
|
2060
|
-
i > 0 ? /* @__PURE__ */ (0,
|
|
2061
|
-
/* @__PURE__ */ (0,
|
|
2281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
|
|
2282
|
+
i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
|
|
2283
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
|
|
2062
2284
|
] }, k)) });
|
|
2063
2285
|
}
|
|
2064
2286
|
function ReportShortcutsHelp({ open, onClose }) {
|
|
@@ -2069,7 +2291,7 @@ function ReportShortcutsHelp({ open, onClose }) {
|
|
|
2069
2291
|
if (open && !dialog.open) dialog.showModal();
|
|
2070
2292
|
else if (!open && dialog.open) dialog.close();
|
|
2071
2293
|
}, [open]);
|
|
2072
|
-
return /* @__PURE__ */ (0,
|
|
2294
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2073
2295
|
"dialog",
|
|
2074
2296
|
{
|
|
2075
2297
|
ref: dialogRef,
|
|
@@ -2077,12 +2299,12 @@ function ReportShortcutsHelp({ open, onClose }) {
|
|
|
2077
2299
|
"aria-label": "Keyboard shortcuts",
|
|
2078
2300
|
onClose,
|
|
2079
2301
|
children: [
|
|
2080
|
-
/* @__PURE__ */ (0,
|
|
2081
|
-
/* @__PURE__ */ (0,
|
|
2082
|
-
/* @__PURE__ */ (0,
|
|
2083
|
-
/* @__PURE__ */ (0,
|
|
2302
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
|
|
2303
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
|
|
2304
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Keys, { keys: s.keys }) }),
|
|
2305
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
|
|
2084
2306
|
] }, s.keys)) }),
|
|
2085
|
-
/* @__PURE__ */ (0,
|
|
2307
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
|
|
2086
2308
|
]
|
|
2087
2309
|
}
|
|
2088
2310
|
);
|
|
@@ -2146,12 +2368,14 @@ function useKeyboardShortcuts(handlers) {
|
|
|
2146
2368
|
// src/interactive/use-deep-link-scroll.ts
|
|
2147
2369
|
var import_react13 = require("react");
|
|
2148
2370
|
function useDeepLinkScroll() {
|
|
2371
|
+
const lastAnchor = (0, import_react13.useRef)(null);
|
|
2149
2372
|
(0, import_react13.useEffect)(() => {
|
|
2150
2373
|
function scrollToHash() {
|
|
2151
|
-
const
|
|
2152
|
-
if (!
|
|
2374
|
+
const { anchor } = splitHash(window.location.hash);
|
|
2375
|
+
if (!anchor || anchor === lastAnchor.current) return;
|
|
2376
|
+
lastAnchor.current = anchor;
|
|
2153
2377
|
requestAnimationFrame(() => {
|
|
2154
|
-
scrollToScenarioId(
|
|
2378
|
+
scrollToScenarioId(anchor, { updateHash: false });
|
|
2155
2379
|
});
|
|
2156
2380
|
}
|
|
2157
2381
|
scrollToHash();
|
|
@@ -2283,7 +2507,7 @@ function failuresFirst(report) {
|
|
|
2283
2507
|
|
|
2284
2508
|
// src/interactive/ReportFilters.tsx
|
|
2285
2509
|
var import_react14 = require("react");
|
|
2286
|
-
var
|
|
2510
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2287
2511
|
var TAG_LIMIT = 12;
|
|
2288
2512
|
function ReportFilters({
|
|
2289
2513
|
statuses,
|
|
@@ -2297,10 +2521,10 @@ function ReportFilters({
|
|
|
2297
2521
|
if (statuses.length === 0 && tags.length === 0) return null;
|
|
2298
2522
|
const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
|
|
2299
2523
|
const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
|
|
2300
|
-
return /* @__PURE__ */ (0,
|
|
2301
|
-
statuses.length > 0 ? /* @__PURE__ */ (0,
|
|
2524
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2525
|
+
statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
|
|
2302
2526
|
const active = status === opt.key;
|
|
2303
|
-
return /* @__PURE__ */ (0,
|
|
2527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
2304
2528
|
"button",
|
|
2305
2529
|
{
|
|
2306
2530
|
type: "button",
|
|
@@ -2312,23 +2536,23 @@ function ReportFilters({
|
|
|
2312
2536
|
),
|
|
2313
2537
|
children: [
|
|
2314
2538
|
opt.label,
|
|
2315
|
-
/* @__PURE__ */ (0,
|
|
2539
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
|
|
2316
2540
|
]
|
|
2317
2541
|
},
|
|
2318
2542
|
opt.key
|
|
2319
2543
|
);
|
|
2320
2544
|
}) }) : null,
|
|
2321
|
-
tags.length > 0 ? /* @__PURE__ */ (0,
|
|
2545
|
+
tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
|
|
2322
2546
|
visibleTags.map((tag) => {
|
|
2323
2547
|
const active = activeTags.includes(tag);
|
|
2324
|
-
return /* @__PURE__ */ (0,
|
|
2548
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2325
2549
|
"button",
|
|
2326
2550
|
{
|
|
2327
2551
|
type: "button",
|
|
2328
2552
|
"aria-pressed": active,
|
|
2329
2553
|
onClick: () => onToggleTag(tag),
|
|
2330
2554
|
className: "cursor-pointer",
|
|
2331
|
-
children: /* @__PURE__ */ (0,
|
|
2555
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2332
2556
|
Badge,
|
|
2333
2557
|
{
|
|
2334
2558
|
variant: "tag",
|
|
@@ -2341,7 +2565,7 @@ function ReportFilters({
|
|
|
2341
2565
|
}
|
|
2342
2566
|
) }, tag);
|
|
2343
2567
|
}),
|
|
2344
|
-
tags.length > TAG_LIMIT ? /* @__PURE__ */ (0,
|
|
2568
|
+
tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2345
2569
|
"button",
|
|
2346
2570
|
{
|
|
2347
2571
|
type: "button",
|
|
@@ -2357,7 +2581,7 @@ function ReportFilters({
|
|
|
2357
2581
|
|
|
2358
2582
|
// src/interactive/ReportToc.tsx
|
|
2359
2583
|
var import_react15 = require("react");
|
|
2360
|
-
var
|
|
2584
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2361
2585
|
var DOT = {
|
|
2362
2586
|
passed: "bg-pass",
|
|
2363
2587
|
failed: "bg-fail",
|
|
@@ -2386,11 +2610,11 @@ function TocContent({ onNavigate }) {
|
|
|
2386
2610
|
return () => observer.disconnect();
|
|
2387
2611
|
}, [scenarioIds]);
|
|
2388
2612
|
if (report.features.length === 0) return null;
|
|
2389
|
-
return /* @__PURE__ */ (0,
|
|
2390
|
-
/* @__PURE__ */ (0,
|
|
2391
|
-
/* @__PURE__ */ (0,
|
|
2613
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { children: [
|
|
2614
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
|
|
2615
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
|
|
2392
2616
|
const active = activeId === scenario.id;
|
|
2393
|
-
return /* @__PURE__ */ (0,
|
|
2617
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
2394
2618
|
"a",
|
|
2395
2619
|
{
|
|
2396
2620
|
href: `#${scenario.id}`,
|
|
@@ -2405,14 +2629,14 @@ function TocContent({ onNavigate }) {
|
|
|
2405
2629
|
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
2406
2630
|
),
|
|
2407
2631
|
children: [
|
|
2408
|
-
/* @__PURE__ */ (0,
|
|
2632
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2409
2633
|
"span",
|
|
2410
2634
|
{
|
|
2411
2635
|
"aria-hidden": true,
|
|
2412
2636
|
className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
|
|
2413
2637
|
}
|
|
2414
2638
|
),
|
|
2415
|
-
/* @__PURE__ */ (0,
|
|
2639
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "break-words", children: scenario.title })
|
|
2416
2640
|
]
|
|
2417
2641
|
}
|
|
2418
2642
|
) }, scenario.id);
|
|
@@ -2422,14 +2646,14 @@ function TocContent({ onNavigate }) {
|
|
|
2422
2646
|
function ReportToc() {
|
|
2423
2647
|
const report = useReport();
|
|
2424
2648
|
if (report.features.length === 0) return null;
|
|
2425
|
-
return /* @__PURE__ */ (0,
|
|
2649
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
2426
2650
|
"nav",
|
|
2427
2651
|
{
|
|
2428
2652
|
"aria-label": "Table of contents",
|
|
2429
2653
|
className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
|
|
2430
2654
|
children: [
|
|
2431
|
-
/* @__PURE__ */ (0,
|
|
2432
|
-
/* @__PURE__ */ (0,
|
|
2655
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
|
|
2656
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(TocContent, {})
|
|
2433
2657
|
]
|
|
2434
2658
|
}
|
|
2435
2659
|
);
|
|
@@ -2439,31 +2663,31 @@ function ReportToc() {
|
|
|
2439
2663
|
var import_react16 = require("react");
|
|
2440
2664
|
var import_dialog = require("@base-ui/react/dialog");
|
|
2441
2665
|
var import_lucide_react4 = require("lucide-react");
|
|
2442
|
-
var
|
|
2666
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2443
2667
|
function ReportTocDrawer() {
|
|
2444
2668
|
const report = useReport();
|
|
2445
2669
|
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
2446
2670
|
if (report.features.length === 0) return null;
|
|
2447
|
-
return /* @__PURE__ */ (0,
|
|
2448
|
-
/* @__PURE__ */ (0,
|
|
2449
|
-
/* @__PURE__ */ (0,
|
|
2671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
|
|
2672
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
|
|
2673
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
|
|
2450
2674
|
" Contents"
|
|
2451
2675
|
] }),
|
|
2452
|
-
/* @__PURE__ */ (0,
|
|
2453
|
-
/* @__PURE__ */ (0,
|
|
2454
|
-
/* @__PURE__ */ (0,
|
|
2455
|
-
/* @__PURE__ */ (0,
|
|
2456
|
-
/* @__PURE__ */ (0,
|
|
2457
|
-
/* @__PURE__ */ (0,
|
|
2676
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Portal, { children: [
|
|
2677
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
|
|
2678
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
|
|
2679
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2680
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
|
|
2681
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2458
2682
|
import_dialog.Dialog.Close,
|
|
2459
2683
|
{
|
|
2460
2684
|
"aria-label": "Close contents",
|
|
2461
2685
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
2462
|
-
children: /* @__PURE__ */ (0,
|
|
2686
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
|
|
2463
2687
|
}
|
|
2464
2688
|
)
|
|
2465
2689
|
] }),
|
|
2466
|
-
/* @__PURE__ */ (0,
|
|
2690
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TocContent, { onNavigate: () => setOpen(false) })
|
|
2467
2691
|
] })
|
|
2468
2692
|
] })
|
|
2469
2693
|
] });
|
|
@@ -2517,15 +2741,43 @@ function useTheme() {
|
|
|
2517
2741
|
return { pref, setPref };
|
|
2518
2742
|
}
|
|
2519
2743
|
|
|
2744
|
+
// src/interactive/use-url-state.ts
|
|
2745
|
+
var import_react18 = require("react");
|
|
2746
|
+
var WRITE_DELAY_MS = 250;
|
|
2747
|
+
function useUrlState() {
|
|
2748
|
+
const [state, setState] = (0, import_react18.useState)(DEFAULT_URL_STATE);
|
|
2749
|
+
const ready = (0, import_react18.useRef)(false);
|
|
2750
|
+
(0, import_react18.useEffect)(() => {
|
|
2751
|
+
const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
|
|
2752
|
+
read();
|
|
2753
|
+
ready.current = true;
|
|
2754
|
+
window.addEventListener("hashchange", read);
|
|
2755
|
+
return () => window.removeEventListener("hashchange", read);
|
|
2756
|
+
}, []);
|
|
2757
|
+
(0, import_react18.useEffect)(() => {
|
|
2758
|
+
if (!ready.current) return;
|
|
2759
|
+
const timer = setTimeout(
|
|
2760
|
+
() => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
|
|
2761
|
+
WRITE_DELAY_MS
|
|
2762
|
+
);
|
|
2763
|
+
return () => clearTimeout(timer);
|
|
2764
|
+
}, [state]);
|
|
2765
|
+
const update = (0, import_react18.useCallback)(
|
|
2766
|
+
(patch) => setState((prev) => ({ ...prev, ...patch })),
|
|
2767
|
+
[]
|
|
2768
|
+
);
|
|
2769
|
+
return [state, update];
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2520
2772
|
// src/interactive/ReportInteractive.tsx
|
|
2521
|
-
var
|
|
2773
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2522
2774
|
function ReportInteractive(props) {
|
|
2523
2775
|
const { report, className, title, dataTheme } = props;
|
|
2524
2776
|
const result = unwrapReport(report);
|
|
2525
2777
|
if (!result.ok) {
|
|
2526
|
-
return /* @__PURE__ */ (0,
|
|
2778
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
2527
2779
|
}
|
|
2528
|
-
return /* @__PURE__ */ (0,
|
|
2780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportInteractiveView, { ...props, report: result.data });
|
|
2529
2781
|
}
|
|
2530
2782
|
var THEME_OPTIONS = [
|
|
2531
2783
|
{ value: "light", label: "Light", Icon: import_lucide_react5.Sun },
|
|
@@ -2533,7 +2785,7 @@ var THEME_OPTIONS = [
|
|
|
2533
2785
|
{ value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
|
|
2534
2786
|
];
|
|
2535
2787
|
function ThemeSegment({ pref, onPref }) {
|
|
2536
|
-
return /* @__PURE__ */ (0,
|
|
2788
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2537
2789
|
ToggleGroup,
|
|
2538
2790
|
{
|
|
2539
2791
|
size: "sm",
|
|
@@ -2543,7 +2795,7 @@ function ThemeSegment({ pref, onPref }) {
|
|
|
2543
2795
|
if (next) onPref(next);
|
|
2544
2796
|
},
|
|
2545
2797
|
"aria-label": "Color theme",
|
|
2546
|
-
children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0,
|
|
2798
|
+
children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Icon, {}) }, value))
|
|
2547
2799
|
}
|
|
2548
2800
|
);
|
|
2549
2801
|
}
|
|
@@ -2559,25 +2811,32 @@ function ReportInteractiveView({
|
|
|
2559
2811
|
staleAfterDays = 7,
|
|
2560
2812
|
scenarioHistory
|
|
2561
2813
|
}) {
|
|
2562
|
-
const [
|
|
2563
|
-
const
|
|
2564
|
-
const
|
|
2565
|
-
const
|
|
2566
|
-
|
|
2814
|
+
const [urlState, setUrlState] = useUrlState();
|
|
2815
|
+
const { query, status: statusFilter, tags: activeTags, detail } = urlState;
|
|
2816
|
+
const setQuery = (0, import_react19.useCallback)((next) => setUrlState({ query: next }), [setUrlState]);
|
|
2817
|
+
const setStatusFilter = (0, import_react19.useCallback)(
|
|
2818
|
+
(next) => setUrlState({ status: next }),
|
|
2819
|
+
[setUrlState]
|
|
2820
|
+
);
|
|
2821
|
+
const setDetail = (0, import_react19.useCallback)(
|
|
2822
|
+
(next) => setUrlState({ detail: next }),
|
|
2823
|
+
[setUrlState]
|
|
2824
|
+
);
|
|
2825
|
+
const [helpOpen, setHelpOpen] = (0, import_react19.useState)(false);
|
|
2567
2826
|
const { pref: themePref, setPref: setThemePref } = useTheme();
|
|
2568
|
-
const searchRef = (0,
|
|
2569
|
-
const expandId = (0,
|
|
2570
|
-
const docsId = (0,
|
|
2571
|
-
const deferredQuery = (0,
|
|
2572
|
-
const failures = (0,
|
|
2573
|
-
const filtered = (0,
|
|
2827
|
+
const searchRef = (0, import_react19.useRef)(null);
|
|
2828
|
+
const expandId = (0, import_react19.useId)();
|
|
2829
|
+
const docsId = (0, import_react19.useId)();
|
|
2830
|
+
const deferredQuery = (0, import_react19.useDeferredValue)(query);
|
|
2831
|
+
const failures = (0, import_react19.useMemo)(() => listFailures(report), [report]);
|
|
2832
|
+
const filtered = (0, import_react19.useMemo)(
|
|
2574
2833
|
// Features carrying failures float to the top so the first thing on screen
|
|
2575
2834
|
// is the thing that needs attention; an all-green run keeps source order.
|
|
2576
2835
|
() => failuresFirst(filterReport(report, { query: deferredQuery, status: statusFilter, tags: activeTags })),
|
|
2577
2836
|
[report, deferredQuery, statusFilter, activeTags]
|
|
2578
2837
|
);
|
|
2579
2838
|
const isFiltering = query !== deferredQuery;
|
|
2580
|
-
const statusOptions = (0,
|
|
2839
|
+
const statusOptions = (0, import_react19.useMemo)(() => {
|
|
2581
2840
|
const s = report.summary;
|
|
2582
2841
|
const opts = [
|
|
2583
2842
|
{ key: "all", label: "All", count: s.total }
|
|
@@ -2588,19 +2847,21 @@ function ReportInteractiveView({
|
|
|
2588
2847
|
if (s.pending) opts.push({ key: "pending", label: "Pending", count: s.pending });
|
|
2589
2848
|
return opts;
|
|
2590
2849
|
}, [report]);
|
|
2591
|
-
const tagOptions = (0,
|
|
2592
|
-
const toggleTag = (0,
|
|
2593
|
-
(tag) =>
|
|
2594
|
-
|
|
2850
|
+
const tagOptions = (0, import_react19.useMemo)(() => allTags(report), [report]);
|
|
2851
|
+
const toggleTag = (0, import_react19.useCallback)(
|
|
2852
|
+
(tag) => setUrlState({
|
|
2853
|
+
tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
|
|
2854
|
+
}),
|
|
2855
|
+
[activeTags, setUrlState]
|
|
2595
2856
|
);
|
|
2596
|
-
const [toast, setToast] = (0,
|
|
2597
|
-
const toastTimer = (0,
|
|
2598
|
-
const showToast = (0,
|
|
2857
|
+
const [toast, setToast] = (0, import_react19.useState)(null);
|
|
2858
|
+
const toastTimer = (0, import_react19.useRef)(null);
|
|
2859
|
+
const showToast = (0, import_react19.useCallback)((msg) => {
|
|
2599
2860
|
setToast(msg);
|
|
2600
2861
|
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
2601
2862
|
toastTimer.current = setTimeout(() => setToast(null), 1800);
|
|
2602
2863
|
}, []);
|
|
2603
|
-
const copy = (0,
|
|
2864
|
+
const copy = (0, import_react19.useCallback)(
|
|
2604
2865
|
(text, msg) => {
|
|
2605
2866
|
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
2606
2867
|
navigator.clipboard.writeText(text).then(() => showToast(msg), () => showToast("Copy failed"));
|
|
@@ -2608,7 +2869,7 @@ function ReportInteractiveView({
|
|
|
2608
2869
|
},
|
|
2609
2870
|
[showToast]
|
|
2610
2871
|
);
|
|
2611
|
-
const scenarioActions = (0,
|
|
2872
|
+
const scenarioActions = (0, import_react19.useMemo)(
|
|
2612
2873
|
() => ({
|
|
2613
2874
|
copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
|
|
2614
2875
|
copyMarkdown: (s) => copy(scenarioToMarkdown2(s), "Markdown copied"),
|
|
@@ -2616,15 +2877,15 @@ function ReportInteractiveView({
|
|
|
2616
2877
|
}),
|
|
2617
2878
|
[copy]
|
|
2618
2879
|
);
|
|
2619
|
-
const failureIndexRef = (0,
|
|
2620
|
-
const allCollapsibleIds = (0,
|
|
2880
|
+
const failureIndexRef = (0, import_react19.useRef)(0);
|
|
2881
|
+
const allCollapsibleIds = (0, import_react19.useMemo)(
|
|
2621
2882
|
() => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
|
|
2622
2883
|
[report]
|
|
2623
2884
|
);
|
|
2624
|
-
const collapse = useCollapseState((0,
|
|
2625
|
-
const collapseAll = (0,
|
|
2626
|
-
const [expandedAll, setExpandedAll] = (0,
|
|
2627
|
-
const setExpanded = (0,
|
|
2885
|
+
const collapse = useCollapseState((0, import_react19.useCallback)(() => defaultCollapsedIds(report), [report]));
|
|
2886
|
+
const collapseAll = (0, import_react19.useCallback)(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
|
|
2887
|
+
const [expandedAll, setExpandedAll] = (0, import_react19.useState)(() => report.summary.failed === 0);
|
|
2888
|
+
const setExpanded = (0, import_react19.useCallback)(
|
|
2628
2889
|
(expanded) => {
|
|
2629
2890
|
if (expanded) collapse.expandAll();
|
|
2630
2891
|
else collapseAll();
|
|
@@ -2632,10 +2893,10 @@ function ReportInteractiveView({
|
|
|
2632
2893
|
},
|
|
2633
2894
|
[collapse, collapseAll]
|
|
2634
2895
|
);
|
|
2635
|
-
const focusSearch = (0,
|
|
2896
|
+
const focusSearch = (0, import_react19.useCallback)(() => {
|
|
2636
2897
|
searchRef.current?.focus();
|
|
2637
2898
|
}, []);
|
|
2638
|
-
const stepFailure = (0,
|
|
2899
|
+
const stepFailure = (0, import_react19.useCallback)(
|
|
2639
2900
|
(direction) => {
|
|
2640
2901
|
if (failures.length === 0) return;
|
|
2641
2902
|
failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
|
|
@@ -2644,10 +2905,10 @@ function ReportInteractiveView({
|
|
|
2644
2905
|
},
|
|
2645
2906
|
[failures]
|
|
2646
2907
|
);
|
|
2647
|
-
const toggleHelp = (0,
|
|
2908
|
+
const toggleHelp = (0, import_react19.useCallback)(() => {
|
|
2648
2909
|
setHelpOpen((v) => !v);
|
|
2649
2910
|
}, []);
|
|
2650
|
-
const escape = (0,
|
|
2911
|
+
const escape = (0, import_react19.useCallback)(() => {
|
|
2651
2912
|
if (helpOpen) setHelpOpen(false);
|
|
2652
2913
|
else if (query !== "") setQuery("");
|
|
2653
2914
|
}, [helpOpen, query]);
|
|
@@ -2665,13 +2926,13 @@ function ReportInteractiveView({
|
|
|
2665
2926
|
const totalScenarios = report.summary.total;
|
|
2666
2927
|
const matchedScenarios = filtered.summary.total;
|
|
2667
2928
|
const showToc = !hideToc && totalScenarios > 3;
|
|
2668
|
-
return /* @__PURE__ */ (0,
|
|
2929
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2669
2930
|
ReportRoot,
|
|
2670
2931
|
{
|
|
2671
2932
|
report: filtered,
|
|
2672
2933
|
customRenderers,
|
|
2673
2934
|
renderers,
|
|
2674
|
-
children: /* @__PURE__ */ (0,
|
|
2935
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
2675
2936
|
"main",
|
|
2676
2937
|
{
|
|
2677
2938
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -2680,8 +2941,8 @@ function ReportInteractiveView({
|
|
|
2680
2941
|
"data-theme": dataTheme,
|
|
2681
2942
|
"data-detail-level": detail,
|
|
2682
2943
|
children: [
|
|
2683
|
-
/* @__PURE__ */ (0,
|
|
2684
|
-
/* @__PURE__ */ (0,
|
|
2944
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("header", { className: "es-report-header", children: [
|
|
2945
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
2685
2946
|
"div",
|
|
2686
2947
|
{
|
|
2687
2948
|
"data-es-navbar": true,
|
|
@@ -2690,9 +2951,9 @@ function ReportInteractiveView({
|
|
|
2690
2951
|
hideHeader ? "justify-end" : "justify-between"
|
|
2691
2952
|
),
|
|
2692
2953
|
children: [
|
|
2693
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
2694
|
-
/* @__PURE__ */ (0,
|
|
2695
|
-
/* @__PURE__ */ (0,
|
|
2954
|
+
hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
|
|
2955
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2956
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2696
2957
|
ReportSearch,
|
|
2697
2958
|
{
|
|
2698
2959
|
ref: searchRef,
|
|
@@ -2702,16 +2963,16 @@ function ReportInteractiveView({
|
|
|
2702
2963
|
totalCount: totalScenarios
|
|
2703
2964
|
}
|
|
2704
2965
|
),
|
|
2705
|
-
/* @__PURE__ */ (0,
|
|
2966
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
|
|
2706
2967
|
] })
|
|
2707
2968
|
]
|
|
2708
2969
|
}
|
|
2709
2970
|
),
|
|
2710
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
2711
|
-
/* @__PURE__ */ (0,
|
|
2712
|
-
/* @__PURE__ */ (0,
|
|
2971
|
+
hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
2972
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportSummary, {}),
|
|
2973
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportMeta, {})
|
|
2713
2974
|
] }),
|
|
2714
|
-
/* @__PURE__ */ (0,
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2715
2976
|
ReportFreshness,
|
|
2716
2977
|
{
|
|
2717
2978
|
lastRunMs: reportLastRunMs(report),
|
|
@@ -2719,8 +2980,8 @@ function ReportInteractiveView({
|
|
|
2719
2980
|
staleAfterDays
|
|
2720
2981
|
}
|
|
2721
2982
|
),
|
|
2722
|
-
/* @__PURE__ */ (0,
|
|
2723
|
-
/* @__PURE__ */ (0,
|
|
2983
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
|
|
2984
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2724
2985
|
ReportFilters,
|
|
2725
2986
|
{
|
|
2726
2987
|
statuses: statusOptions,
|
|
@@ -2731,14 +2992,14 @@ function ReportInteractiveView({
|
|
|
2731
2992
|
onToggleTag: toggleTag
|
|
2732
2993
|
}
|
|
2733
2994
|
),
|
|
2734
|
-
/* @__PURE__ */ (0,
|
|
2735
|
-
showToc ? /* @__PURE__ */ (0,
|
|
2736
|
-
/* @__PURE__ */ (0,
|
|
2737
|
-
/* @__PURE__ */ (0,
|
|
2738
|
-
/* @__PURE__ */ (0,
|
|
2995
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
|
|
2996
|
+
showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportTocDrawer, {}) : null,
|
|
2997
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2998
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
|
|
2999
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
|
|
2739
3000
|
] }),
|
|
2740
|
-
/* @__PURE__ */ (0,
|
|
2741
|
-
/* @__PURE__ */ (0,
|
|
3001
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3002
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2742
3003
|
Switch,
|
|
2743
3004
|
{
|
|
2744
3005
|
id: docsId,
|
|
@@ -2746,16 +3007,16 @@ function ReportInteractiveView({
|
|
|
2746
3007
|
onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
|
|
2747
3008
|
}
|
|
2748
3009
|
),
|
|
2749
|
-
/* @__PURE__ */ (0,
|
|
3010
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
|
|
2750
3011
|
] })
|
|
2751
3012
|
] })
|
|
2752
3013
|
] }),
|
|
2753
|
-
/* @__PURE__ */ (0,
|
|
2754
|
-
hasContent ? showToc ? /* @__PURE__ */ (0,
|
|
2755
|
-
/* @__PURE__ */ (0,
|
|
2756
|
-
/* @__PURE__ */ (0,
|
|
2757
|
-
] }) : /* @__PURE__ */ (0,
|
|
2758
|
-
/* @__PURE__ */ (0,
|
|
3014
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFailureBanner, { failures }),
|
|
3015
|
+
hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex gap-6", children: [
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportToc, {}),
|
|
3017
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFeatureList, {}) })
|
|
3018
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
|
|
3019
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2759
3020
|
"button",
|
|
2760
3021
|
{
|
|
2761
3022
|
type: "button",
|
|
@@ -2766,8 +3027,8 @@ function ReportInteractiveView({
|
|
|
2766
3027
|
children: "?"
|
|
2767
3028
|
}
|
|
2768
3029
|
),
|
|
2769
|
-
/* @__PURE__ */ (0,
|
|
2770
|
-
toast ? /* @__PURE__ */ (0,
|
|
3030
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
|
|
3031
|
+
toast ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2771
3032
|
"div",
|
|
2772
3033
|
{
|
|
2773
3034
|
role: "status",
|
|
@@ -2784,14 +3045,14 @@ function ReportInteractiveView({
|
|
|
2784
3045
|
}
|
|
2785
3046
|
|
|
2786
3047
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
2787
|
-
var
|
|
3048
|
+
var import_react22 = require("react");
|
|
2788
3049
|
|
|
2789
3050
|
// src/interactive/island-renderers.tsx
|
|
2790
|
-
var
|
|
3051
|
+
var import_react21 = require("react");
|
|
2791
3052
|
|
|
2792
3053
|
// src/components/doc/MermaidView.tsx
|
|
2793
|
-
var
|
|
2794
|
-
var
|
|
3054
|
+
var import_react20 = require("react");
|
|
3055
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
2795
3056
|
function prefersDark(el) {
|
|
2796
3057
|
const themed = el?.closest("[data-theme]");
|
|
2797
3058
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -2799,23 +3060,40 @@ function prefersDark(el) {
|
|
|
2799
3060
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
2800
3061
|
}
|
|
2801
3062
|
function MermaidView({ entry, load }) {
|
|
2802
|
-
const ref = (0,
|
|
2803
|
-
const [svg, setSvg] = (0,
|
|
2804
|
-
(0,
|
|
3063
|
+
const ref = (0, import_react20.useRef)(null);
|
|
3064
|
+
const [svg, setSvg] = (0, import_react20.useState)(null);
|
|
3065
|
+
const [error, setError] = (0, import_react20.useState)(null);
|
|
3066
|
+
(0, import_react20.useEffect)(() => {
|
|
2805
3067
|
let cancelled = false;
|
|
2806
3068
|
const draw = async () => {
|
|
3069
|
+
let mermaid;
|
|
3070
|
+
try {
|
|
3071
|
+
mermaid = await load();
|
|
3072
|
+
} catch {
|
|
3073
|
+
if (!cancelled) {
|
|
3074
|
+
setSvg(null);
|
|
3075
|
+
setError(null);
|
|
3076
|
+
}
|
|
3077
|
+
return;
|
|
3078
|
+
}
|
|
2807
3079
|
try {
|
|
2808
|
-
const mermaid = await load();
|
|
2809
3080
|
mermaid.initialize({
|
|
2810
3081
|
startOnLoad: false,
|
|
2811
3082
|
securityLevel: "strict",
|
|
2812
3083
|
theme: prefersDark(ref.current) ? "dark" : "default"
|
|
2813
3084
|
});
|
|
3085
|
+
await mermaid.parse(entry.code);
|
|
2814
3086
|
const id = `es-mermaid-${Math.random().toString(36).slice(2)}`;
|
|
2815
3087
|
const { svg: rendered } = await mermaid.render(id, entry.code);
|
|
2816
|
-
if (!cancelled)
|
|
2817
|
-
|
|
2818
|
-
|
|
3088
|
+
if (!cancelled) {
|
|
3089
|
+
setSvg(rendered);
|
|
3090
|
+
setError(null);
|
|
3091
|
+
}
|
|
3092
|
+
} catch (err) {
|
|
3093
|
+
if (!cancelled) {
|
|
3094
|
+
setSvg(null);
|
|
3095
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
3096
|
+
}
|
|
2819
3097
|
}
|
|
2820
3098
|
};
|
|
2821
3099
|
void draw();
|
|
@@ -2828,9 +3106,9 @@ function MermaidView({ entry, load }) {
|
|
|
2828
3106
|
};
|
|
2829
3107
|
}, [entry.code, load]);
|
|
2830
3108
|
if (svg) {
|
|
2831
|
-
return /* @__PURE__ */ (0,
|
|
2832
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
2833
|
-
/* @__PURE__ */ (0,
|
|
3109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("figure", { ref, className: "my-3", children: [
|
|
3110
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
3111
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2834
3112
|
"div",
|
|
2835
3113
|
{
|
|
2836
3114
|
role: "img",
|
|
@@ -2841,11 +3119,25 @@ function MermaidView({ entry, load }) {
|
|
|
2841
3119
|
)
|
|
2842
3120
|
] });
|
|
2843
3121
|
}
|
|
2844
|
-
return /* @__PURE__ */ (0,
|
|
3122
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
|
|
3123
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3124
|
+
"p",
|
|
3125
|
+
{
|
|
3126
|
+
role: "alert",
|
|
3127
|
+
"data-es-mermaid-error": true,
|
|
3128
|
+
className: "mt-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
|
|
3129
|
+
children: [
|
|
3130
|
+
"Diagram failed to render: ",
|
|
3131
|
+
error
|
|
3132
|
+
]
|
|
3133
|
+
}
|
|
3134
|
+
) : null,
|
|
3135
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(MermaidSource, { entry, ref })
|
|
3136
|
+
] });
|
|
2845
3137
|
}
|
|
2846
3138
|
|
|
2847
3139
|
// src/interactive/island-renderers.tsx
|
|
2848
|
-
var
|
|
3140
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
2849
3141
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
2850
3142
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
2851
3143
|
function cachedCdnModule(url) {
|
|
@@ -2862,8 +3154,8 @@ function cachedCdnModule(url) {
|
|
|
2862
3154
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
2863
3155
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
2864
3156
|
function CdnHighlightedCode({ entry }) {
|
|
2865
|
-
const [html, setHtml] = (0,
|
|
2866
|
-
(0,
|
|
3157
|
+
const [html, setHtml] = (0, import_react21.useState)(null);
|
|
3158
|
+
(0, import_react21.useEffect)(() => {
|
|
2867
3159
|
let cancelled = false;
|
|
2868
3160
|
void (async () => {
|
|
2869
3161
|
try {
|
|
@@ -2879,27 +3171,27 @@ function CdnHighlightedCode({ entry }) {
|
|
|
2879
3171
|
cancelled = true;
|
|
2880
3172
|
};
|
|
2881
3173
|
}, [entry.content, entry.lang]);
|
|
2882
|
-
return /* @__PURE__ */ (0,
|
|
3174
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2883
3175
|
"code",
|
|
2884
3176
|
{
|
|
2885
3177
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
2886
3178
|
dangerouslySetInnerHTML: { __html: html }
|
|
2887
3179
|
}
|
|
2888
|
-
) : /* @__PURE__ */ (0,
|
|
3180
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
2889
3181
|
}
|
|
2890
3182
|
function buildIslandRenderers(opts) {
|
|
2891
3183
|
const renderers = {};
|
|
2892
3184
|
if (opts.mermaid) {
|
|
2893
|
-
renderers.mermaid = (entry) => (0,
|
|
3185
|
+
renderers.mermaid = (entry) => (0, import_react21.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
|
|
2894
3186
|
}
|
|
2895
3187
|
if (opts.syntaxHighlighting) {
|
|
2896
|
-
renderers.code = (entry) => (0,
|
|
3188
|
+
renderers.code = (entry) => (0, import_react21.createElement)(CdnHighlightedCode, { entry });
|
|
2897
3189
|
}
|
|
2898
3190
|
return renderers;
|
|
2899
3191
|
}
|
|
2900
3192
|
|
|
2901
3193
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
2902
|
-
var
|
|
3194
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
2903
3195
|
function ReportInteractiveIsland({
|
|
2904
3196
|
report,
|
|
2905
3197
|
title,
|
|
@@ -2909,11 +3201,11 @@ function ReportInteractiveIsland({
|
|
|
2909
3201
|
syntaxHighlighting = true,
|
|
2910
3202
|
mermaid = true
|
|
2911
3203
|
}) {
|
|
2912
|
-
const renderers = (0,
|
|
3204
|
+
const renderers = (0, import_react22.useMemo)(
|
|
2913
3205
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
2914
3206
|
[syntaxHighlighting, mermaid]
|
|
2915
3207
|
);
|
|
2916
|
-
return /* @__PURE__ */ (0,
|
|
3208
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2917
3209
|
ReportInteractive,
|
|
2918
3210
|
{
|
|
2919
3211
|
report,
|