executable-stories-react 0.6.0 → 0.8.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 +472 -194
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +468 -191
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +544 -286
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +10 -1
- package/dist/interactive.d.ts +10 -1
- package/dist/interactive.js +541 -283
- 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 +31 -31
- package/dist/ssr.cjs +408 -162
- 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 +405 -159
- package/dist/ssr.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -59,6 +59,7 @@ __export(src_exports, {
|
|
|
59
59
|
ReportSchemaError: () => ReportSchemaError,
|
|
60
60
|
ReportStepItem: () => ReportStepItem,
|
|
61
61
|
ReportSteps: () => ReportSteps,
|
|
62
|
+
ReportStoryboard: () => ReportStoryboard,
|
|
62
63
|
ReportSummary: () => ReportSummary,
|
|
63
64
|
ReportSummaryView: () => ReportSummaryView,
|
|
64
65
|
ReportTrace: () => ReportTrace,
|
|
@@ -299,7 +300,7 @@ function DocLink({ entry }) {
|
|
|
299
300
|
// src/components/doc/DocSection.tsx
|
|
300
301
|
var import_marked = require("marked");
|
|
301
302
|
|
|
302
|
-
//
|
|
303
|
+
// ../executable-stories-core/dist/utils/url.js
|
|
303
304
|
function safeUrl(value) {
|
|
304
305
|
if (!value) return void 0;
|
|
305
306
|
const trimmed = value.trim();
|
|
@@ -457,17 +458,29 @@ function DocHtml({ entry }) {
|
|
|
457
458
|
] });
|
|
458
459
|
}
|
|
459
460
|
|
|
460
|
-
// src/components/doc/
|
|
461
|
+
// src/components/doc/DocState.tsx
|
|
461
462
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
463
|
+
function DocState({ entry }) {
|
|
464
|
+
let json;
|
|
465
|
+
try {
|
|
466
|
+
json = JSON.stringify(entry.value, null, 2) ?? String(entry.value);
|
|
467
|
+
} catch {
|
|
468
|
+
json = String(entry.value);
|
|
469
|
+
}
|
|
470
|
+
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 }) });
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/components/doc/DocCustom.tsx
|
|
474
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
462
475
|
function DocCustom({ entry }) {
|
|
463
476
|
const renderers = useCustomRenderers();
|
|
464
477
|
const renderer = renderers[entry.type];
|
|
465
478
|
if (renderer) {
|
|
466
|
-
return /* @__PURE__ */ (0,
|
|
479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: renderer(entry) });
|
|
467
480
|
}
|
|
468
|
-
return /* @__PURE__ */ (0,
|
|
469
|
-
/* @__PURE__ */ (0,
|
|
470
|
-
/* @__PURE__ */ (0,
|
|
481
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
|
|
482
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
|
|
483
|
+
/* @__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) })
|
|
471
484
|
] });
|
|
472
485
|
}
|
|
473
486
|
function safeStringify(value) {
|
|
@@ -479,45 +492,47 @@ function safeStringify(value) {
|
|
|
479
492
|
}
|
|
480
493
|
|
|
481
494
|
// src/components/doc/DocEntry.tsx
|
|
482
|
-
var
|
|
495
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
483
496
|
function DocEntry({ entry }) {
|
|
484
497
|
switch (entry.kind) {
|
|
485
498
|
case "note":
|
|
486
|
-
return /* @__PURE__ */ (0,
|
|
499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocNote, { entry });
|
|
487
500
|
case "tag":
|
|
488
|
-
return /* @__PURE__ */ (0,
|
|
501
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTag, { entry });
|
|
489
502
|
case "kv":
|
|
490
|
-
return /* @__PURE__ */ (0,
|
|
503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocKv, { entry });
|
|
491
504
|
case "code":
|
|
492
|
-
return /* @__PURE__ */ (0,
|
|
505
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCode, { entry });
|
|
493
506
|
case "table":
|
|
494
|
-
return /* @__PURE__ */ (0,
|
|
507
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTable, { entry });
|
|
495
508
|
case "link":
|
|
496
|
-
return /* @__PURE__ */ (0,
|
|
509
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocLink, { entry });
|
|
497
510
|
case "section":
|
|
498
|
-
return /* @__PURE__ */ (0,
|
|
511
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocSection, { entry });
|
|
499
512
|
case "mermaid":
|
|
500
|
-
return /* @__PURE__ */ (0,
|
|
513
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocMermaid, { entry });
|
|
501
514
|
case "screenshot":
|
|
502
|
-
return /* @__PURE__ */ (0,
|
|
515
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocScreenshot, { entry });
|
|
503
516
|
case "video":
|
|
504
|
-
return /* @__PURE__ */ (0,
|
|
517
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocVideo, { entry });
|
|
505
518
|
case "html":
|
|
506
|
-
return /* @__PURE__ */ (0,
|
|
519
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocHtml, { entry });
|
|
520
|
+
case "state":
|
|
521
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocState, { entry });
|
|
507
522
|
case "custom":
|
|
508
|
-
return /* @__PURE__ */ (0,
|
|
523
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCustom, { entry });
|
|
509
524
|
}
|
|
510
525
|
}
|
|
511
526
|
|
|
512
527
|
// src/components/ReportDocEntries.tsx
|
|
513
|
-
var
|
|
528
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
514
529
|
function ReportDocEntries({ entries }) {
|
|
515
530
|
if (entries.length === 0) return null;
|
|
516
|
-
return /* @__PURE__ */ (0,
|
|
531
|
+
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)) });
|
|
517
532
|
}
|
|
518
533
|
|
|
519
534
|
// src/lib/step-params.tsx
|
|
520
|
-
var
|
|
535
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
521
536
|
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
|
|
522
537
|
function highlightStepParams(text) {
|
|
523
538
|
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
@@ -529,7 +544,7 @@ function highlightStepParams(text) {
|
|
|
529
544
|
const end = start + match[0].length;
|
|
530
545
|
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
531
546
|
nodes.push(
|
|
532
|
-
/* @__PURE__ */ (0,
|
|
547
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
|
|
533
548
|
);
|
|
534
549
|
lastIndex = end;
|
|
535
550
|
});
|
|
@@ -538,7 +553,7 @@ function highlightStepParams(text) {
|
|
|
538
553
|
}
|
|
539
554
|
|
|
540
555
|
// src/components/ReportSteps.tsx
|
|
541
|
-
var
|
|
556
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
542
557
|
var STEP_GLYPH = {
|
|
543
558
|
passed: "\u2713",
|
|
544
559
|
failed: "\u2717",
|
|
@@ -553,21 +568,21 @@ var STEP_GLYPH_COLOR = {
|
|
|
553
568
|
};
|
|
554
569
|
function ReportSteps({ scenario }) {
|
|
555
570
|
if (scenario.steps.length === 0) return null;
|
|
556
|
-
return /* @__PURE__ */ (0,
|
|
571
|
+
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)) });
|
|
557
572
|
}
|
|
558
573
|
function ReportStepItem({ step }) {
|
|
559
574
|
const kw = step.keyword.toLowerCase();
|
|
560
575
|
const continuation = kw === "and" || kw === "but";
|
|
561
|
-
return /* @__PURE__ */ (0,
|
|
576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
562
577
|
"li",
|
|
563
578
|
{
|
|
564
579
|
id: step.id,
|
|
565
580
|
"data-status": step.status,
|
|
566
581
|
className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
|
|
567
582
|
children: [
|
|
568
|
-
/* @__PURE__ */ (0,
|
|
569
|
-
/* @__PURE__ */ (0,
|
|
570
|
-
/* @__PURE__ */ (0,
|
|
583
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
584
|
+
/* @__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" }),
|
|
585
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
571
586
|
"span",
|
|
572
587
|
{
|
|
573
588
|
className: cn(
|
|
@@ -577,10 +592,10 @@ function ReportStepItem({ step }) {
|
|
|
577
592
|
children: step.keyword
|
|
578
593
|
}
|
|
579
594
|
),
|
|
580
|
-
/* @__PURE__ */ (0,
|
|
581
|
-
step.durationMs > 0 ? /* @__PURE__ */ (0,
|
|
595
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
596
|
+
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
|
|
582
597
|
] }),
|
|
583
|
-
step.errorMessage ? /* @__PURE__ */ (0,
|
|
598
|
+
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
584
599
|
"pre",
|
|
585
600
|
{
|
|
586
601
|
role: "alert",
|
|
@@ -588,24 +603,255 @@ function ReportStepItem({ step }) {
|
|
|
588
603
|
children: step.errorMessage
|
|
589
604
|
}
|
|
590
605
|
) : null,
|
|
591
|
-
step.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
606
|
+
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
|
|
592
607
|
]
|
|
593
608
|
}
|
|
594
609
|
);
|
|
595
610
|
}
|
|
596
611
|
|
|
612
|
+
// ../executable-stories-core/dist/state-diff.js
|
|
613
|
+
var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
614
|
+
function deepEqual(a, b) {
|
|
615
|
+
if (Object.is(a, b)) return true;
|
|
616
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
617
|
+
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
|
|
618
|
+
}
|
|
619
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
620
|
+
const ak = Object.keys(a);
|
|
621
|
+
const bk = Object.keys(b);
|
|
622
|
+
return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
|
|
623
|
+
}
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
|
|
627
|
+
function walk(path, before, after, out) {
|
|
628
|
+
if (Object.is(before, after)) return;
|
|
629
|
+
if (Array.isArray(before) && Array.isArray(after)) {
|
|
630
|
+
const shared = Math.min(before.length, after.length);
|
|
631
|
+
for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
|
|
632
|
+
for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
|
|
633
|
+
for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (isPlainObject(before) && isPlainObject(after)) {
|
|
637
|
+
for (const key of Object.keys(after)) {
|
|
638
|
+
const p = joinKey(path, key);
|
|
639
|
+
if (key in before) walk(p, before[key], after[key], out);
|
|
640
|
+
else out.push({ path: p, kind: "added", after: after[key] });
|
|
641
|
+
}
|
|
642
|
+
for (const key of Object.keys(before)) {
|
|
643
|
+
if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
|
|
644
|
+
}
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
|
|
648
|
+
}
|
|
649
|
+
function diffStateValues(before, after) {
|
|
650
|
+
const out = [];
|
|
651
|
+
walk("", before, after, out);
|
|
652
|
+
return out;
|
|
653
|
+
}
|
|
654
|
+
function formatStateValue(value, maxLen = 60) {
|
|
655
|
+
const s = value === void 0 ? "undefined" : JSON.stringify(value);
|
|
656
|
+
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
657
|
+
}
|
|
658
|
+
function summarizeStateChanges(changes) {
|
|
659
|
+
return changes.map((c) => {
|
|
660
|
+
const at = c.path === "" ? "(root)" : c.path;
|
|
661
|
+
switch (c.kind) {
|
|
662
|
+
case "added":
|
|
663
|
+
return `+ ${at}: ${formatStateValue(c.after)}`;
|
|
664
|
+
case "removed":
|
|
665
|
+
return `\u2212 ${at}`;
|
|
666
|
+
case "changed":
|
|
667
|
+
return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// ../executable-stories-core/dist/storyboard.js
|
|
673
|
+
function findScreenshot(entries) {
|
|
674
|
+
for (const entry of entries) {
|
|
675
|
+
if (entry.kind === "screenshot") return entry;
|
|
676
|
+
if (entry.children) {
|
|
677
|
+
const nested = findScreenshot(entry.children);
|
|
678
|
+
if (nested) return nested;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return void 0;
|
|
682
|
+
}
|
|
683
|
+
function findStates(entries, out = []) {
|
|
684
|
+
for (const entry of entries) {
|
|
685
|
+
if (entry.kind === "state") out.push(entry);
|
|
686
|
+
if (entry.children) findStates(entry.children, out);
|
|
687
|
+
}
|
|
688
|
+
return out;
|
|
689
|
+
}
|
|
690
|
+
var laneOf = (label) => label ?? "";
|
|
691
|
+
function extractStoryboardFrames(scenario) {
|
|
692
|
+
const frames = [];
|
|
693
|
+
const laneOrder = [];
|
|
694
|
+
const previous = /* @__PURE__ */ new Map();
|
|
695
|
+
for (const step of scenario.steps) {
|
|
696
|
+
const shot = findScreenshot(step.docEntries);
|
|
697
|
+
const stateDocs = findStates(step.docEntries);
|
|
698
|
+
if (!shot && stateDocs.length === 0) continue;
|
|
699
|
+
const byLane = /* @__PURE__ */ new Map();
|
|
700
|
+
for (const doc of stateDocs) {
|
|
701
|
+
const lane = laneOf(doc.label);
|
|
702
|
+
if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
|
|
703
|
+
byLane.set(lane, doc);
|
|
704
|
+
}
|
|
705
|
+
const states = [];
|
|
706
|
+
for (const lane of laneOrder) {
|
|
707
|
+
const doc = byLane.get(lane);
|
|
708
|
+
if (!doc) continue;
|
|
709
|
+
const card = {
|
|
710
|
+
...doc.label !== void 0 && { label: doc.label },
|
|
711
|
+
value: doc.value
|
|
712
|
+
};
|
|
713
|
+
if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
|
|
714
|
+
previous.set(lane, doc.value);
|
|
715
|
+
states.push(card);
|
|
716
|
+
}
|
|
717
|
+
frames.push({
|
|
718
|
+
stepId: step.id,
|
|
719
|
+
stepIndex: step.index,
|
|
720
|
+
keyword: step.keyword,
|
|
721
|
+
text: step.text,
|
|
722
|
+
status: step.status,
|
|
723
|
+
...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
|
|
724
|
+
states
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
return frames;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/components/ReportStoryboard.tsx
|
|
731
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
732
|
+
var FRAME_GLYPH = {
|
|
733
|
+
passed: "\u2713",
|
|
734
|
+
failed: "\u2717",
|
|
735
|
+
skipped: "\u25CB",
|
|
736
|
+
pending: "\u25CB"
|
|
737
|
+
};
|
|
738
|
+
var FRAME_GLYPH_COLOR = {
|
|
739
|
+
passed: "text-pass",
|
|
740
|
+
failed: "text-fail",
|
|
741
|
+
skipped: "text-skip",
|
|
742
|
+
pending: "text-pend"
|
|
743
|
+
};
|
|
744
|
+
function ReportStoryboard({ scenario }) {
|
|
745
|
+
const frames = extractStoryboardFrames(scenario);
|
|
746
|
+
if (frames.length < 2) return null;
|
|
747
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
|
|
748
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
|
|
749
|
+
/* @__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)) })
|
|
750
|
+
] });
|
|
751
|
+
}
|
|
752
|
+
function StoryboardFrameItem({ frame }) {
|
|
753
|
+
const label = `${frame.keyword} ${frame.text}`;
|
|
754
|
+
const hasStates = frame.states.length > 0;
|
|
755
|
+
const caption = /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
|
|
756
|
+
/* @__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" }),
|
|
757
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "min-w-0", children: [
|
|
758
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
|
|
759
|
+
" ",
|
|
760
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
|
|
761
|
+
] })
|
|
762
|
+
] });
|
|
763
|
+
return (
|
|
764
|
+
// State cards need room for diff rows; screenshot-only frames keep the
|
|
765
|
+
// original narrow film-cell width (and DOM) unchanged.
|
|
766
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
|
|
767
|
+
frame.screenshot ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(FrameScreenshot, { frame, label }) : null,
|
|
768
|
+
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,
|
|
769
|
+
frame.screenshot ? caption : (
|
|
770
|
+
// No screenshot means no image anchor, so the caption carries the
|
|
771
|
+
// jump-to-step link instead.
|
|
772
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
|
|
773
|
+
)
|
|
774
|
+
] })
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
function FrameScreenshot({ frame, label }) {
|
|
778
|
+
const path = frame.screenshot?.path ?? "";
|
|
779
|
+
const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
|
|
780
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
781
|
+
"a",
|
|
782
|
+
{
|
|
783
|
+
href: `#${frame.stepId}`,
|
|
784
|
+
"aria-label": `Jump to step: ${label}`,
|
|
785
|
+
className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
|
|
786
|
+
children: src ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
787
|
+
"img",
|
|
788
|
+
{
|
|
789
|
+
src,
|
|
790
|
+
alt: frame.screenshot?.alt ?? label,
|
|
791
|
+
loading: "lazy",
|
|
792
|
+
className: "h-24 w-full bg-muted object-cover object-top"
|
|
793
|
+
}
|
|
794
|
+
) : /* @__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" })
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
var CHANGE_TONE = {
|
|
799
|
+
added: "bg-pass-bg text-pass",
|
|
800
|
+
removed: "bg-fail-bg text-fail",
|
|
801
|
+
changed: "bg-skip-bg text-skip"
|
|
802
|
+
};
|
|
803
|
+
function prettyJson(value) {
|
|
804
|
+
try {
|
|
805
|
+
return JSON.stringify(value, null, 2) ?? String(value);
|
|
806
|
+
} catch {
|
|
807
|
+
return String(value);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function StateCardView({ card }) {
|
|
811
|
+
const json = prettyJson(card.value);
|
|
812
|
+
const firstCapture = card.changes === void 0;
|
|
813
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
|
|
814
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
|
|
815
|
+
/* @__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" }),
|
|
816
|
+
firstCapture ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
|
|
817
|
+
] }),
|
|
818
|
+
!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)(
|
|
819
|
+
"li",
|
|
820
|
+
{
|
|
821
|
+
className: cn(
|
|
822
|
+
"rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
|
|
823
|
+
CHANGE_TONE[change.kind]
|
|
824
|
+
),
|
|
825
|
+
children: summarizeStateChanges([change])[0]
|
|
826
|
+
},
|
|
827
|
+
i
|
|
828
|
+
)) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
830
|
+
"details",
|
|
831
|
+
{
|
|
832
|
+
open: firstCapture && json.length <= 240,
|
|
833
|
+
className: cn(firstCapture ? "mt-1.5" : "mt-1"),
|
|
834
|
+
children: [
|
|
835
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
|
|
836
|
+
/* @__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 })
|
|
837
|
+
]
|
|
838
|
+
}
|
|
839
|
+
)
|
|
840
|
+
] });
|
|
841
|
+
}
|
|
842
|
+
|
|
597
843
|
// src/components/ReportAttachments.tsx
|
|
598
|
-
var
|
|
844
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
599
845
|
function ReportAttachments({ attachments }) {
|
|
600
846
|
if (attachments.length === 0) return null;
|
|
601
847
|
return (
|
|
602
848
|
// role="group" (not a labelled <section>) so multiple scenarios' attachment
|
|
603
849
|
// blocks don't create duplicate "Attachments" region landmarks.
|
|
604
|
-
/* @__PURE__ */ (0,
|
|
850
|
+
/* @__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) => {
|
|
605
851
|
const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
|
|
606
852
|
const isImage = att.mediaType.startsWith("image/");
|
|
607
|
-
return /* @__PURE__ */ (0,
|
|
608
|
-
/* @__PURE__ */ (0,
|
|
853
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
854
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
609
855
|
"img",
|
|
610
856
|
{
|
|
611
857
|
src: dataUri,
|
|
@@ -614,8 +860,8 @@ function ReportAttachments({ attachments }) {
|
|
|
614
860
|
className: "max-w-full rounded-md border border-border"
|
|
615
861
|
}
|
|
616
862
|
),
|
|
617
|
-
/* @__PURE__ */ (0,
|
|
618
|
-
] }) : /* @__PURE__ */ (0,
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
|
|
864
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
619
865
|
"a",
|
|
620
866
|
{
|
|
621
867
|
href: dataUri,
|
|
@@ -624,7 +870,7 @@ function ReportAttachments({ attachments }) {
|
|
|
624
870
|
children: [
|
|
625
871
|
att.name,
|
|
626
872
|
" ",
|
|
627
|
-
/* @__PURE__ */ (0,
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-muted-foreground", children: [
|
|
628
874
|
"(",
|
|
629
875
|
att.mediaType,
|
|
630
876
|
")"
|
|
@@ -688,15 +934,15 @@ function toTraceRows(spans) {
|
|
|
688
934
|
roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
689
935
|
const rows = [];
|
|
690
936
|
const seen = /* @__PURE__ */ new Set();
|
|
691
|
-
const
|
|
937
|
+
const walk2 = (node, depth) => {
|
|
692
938
|
if (seen.has(node.span.spanId)) return;
|
|
693
939
|
seen.add(node.span.spanId);
|
|
694
940
|
rows.push({ ...node.span, depth });
|
|
695
|
-
for (const child of node.children)
|
|
941
|
+
for (const child of node.children) walk2(child, depth + 1);
|
|
696
942
|
};
|
|
697
|
-
for (const root of roots)
|
|
943
|
+
for (const root of roots) walk2(root, 0);
|
|
698
944
|
for (const node of byId.values()) {
|
|
699
|
-
if (!seen.has(node.span.spanId))
|
|
945
|
+
if (!seen.has(node.span.spanId)) walk2(node, 0);
|
|
700
946
|
}
|
|
701
947
|
return rows;
|
|
702
948
|
}
|
|
@@ -712,7 +958,7 @@ function traceWindow(rows) {
|
|
|
712
958
|
}
|
|
713
959
|
|
|
714
960
|
// src/components/ReportTrace.tsx
|
|
715
|
-
var
|
|
961
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
716
962
|
var BAR_COLOR = {
|
|
717
963
|
ok: "bg-pass",
|
|
718
964
|
error: "bg-fail",
|
|
@@ -723,8 +969,8 @@ function ReportTrace({ spans }) {
|
|
|
723
969
|
const rows = toTraceRows(spans);
|
|
724
970
|
if (rows.length === 0) return null;
|
|
725
971
|
const { minStartMs, totalMs } = traceWindow(rows);
|
|
726
|
-
return /* @__PURE__ */ (0,
|
|
727
|
-
/* @__PURE__ */ (0,
|
|
972
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
|
|
973
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
|
|
728
974
|
"Trace (",
|
|
729
975
|
rows.length,
|
|
730
976
|
" span",
|
|
@@ -733,11 +979,11 @@ function ReportTrace({ spans }) {
|
|
|
733
979
|
formatDuration(totalMs),
|
|
734
980
|
")"
|
|
735
981
|
] }),
|
|
736
|
-
/* @__PURE__ */ (0,
|
|
982
|
+
/* @__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) => {
|
|
737
983
|
const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
|
|
738
984
|
const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
|
|
739
|
-
return /* @__PURE__ */ (0,
|
|
740
|
-
/* @__PURE__ */ (0,
|
|
985
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-2", children: [
|
|
986
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
741
987
|
"span",
|
|
742
988
|
{
|
|
743
989
|
className: "min-w-0 flex-1 truncate text-foreground",
|
|
@@ -746,14 +992,14 @@ function ReportTrace({ spans }) {
|
|
|
746
992
|
children: row.name
|
|
747
993
|
}
|
|
748
994
|
),
|
|
749
|
-
/* @__PURE__ */ (0,
|
|
995
|
+
/* @__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)(
|
|
750
996
|
"span",
|
|
751
997
|
{
|
|
752
998
|
className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
|
|
753
999
|
style: { left: `${leftPct}%`, width: `${widthPct}%` }
|
|
754
1000
|
}
|
|
755
1001
|
) }),
|
|
756
|
-
/* @__PURE__ */ (0,
|
|
1002
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
|
|
757
1003
|
] }, `${row.spanId}-${i}`);
|
|
758
1004
|
}) })
|
|
759
1005
|
] });
|
|
@@ -804,7 +1050,7 @@ function flakinessOf(entries) {
|
|
|
804
1050
|
}
|
|
805
1051
|
|
|
806
1052
|
// src/components/ScenarioRunHistory.tsx
|
|
807
|
-
var
|
|
1053
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
808
1054
|
var MAX_DOTS = 10;
|
|
809
1055
|
var DOT_COLOR = {
|
|
810
1056
|
passed: "bg-pass",
|
|
@@ -824,16 +1070,16 @@ function ScenarioRunHistory({ scenarioId }) {
|
|
|
824
1070
|
const recent = entries.slice(-MAX_DOTS);
|
|
825
1071
|
const flaky = flakinessOf(recent) === "flaky";
|
|
826
1072
|
const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
|
|
827
|
-
return /* @__PURE__ */ (0,
|
|
828
|
-
flaky ? /* @__PURE__ */ (0,
|
|
829
|
-
/* @__PURE__ */ (0,
|
|
1073
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
1074
|
+
flaky ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
|
|
1075
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
830
1076
|
"span",
|
|
831
1077
|
{
|
|
832
1078
|
role: "img",
|
|
833
1079
|
"aria-label": `Run history: ${summary}`,
|
|
834
1080
|
title: summary,
|
|
835
1081
|
className: "flex shrink-0 items-center gap-[3px]",
|
|
836
|
-
children: recent.map((e, i) => /* @__PURE__ */ (0,
|
|
1082
|
+
children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
837
1083
|
"span",
|
|
838
1084
|
{
|
|
839
1085
|
"aria-hidden": true,
|
|
@@ -872,9 +1118,9 @@ function useScenarioActions() {
|
|
|
872
1118
|
|
|
873
1119
|
// src/components/ui/card.tsx
|
|
874
1120
|
var React2 = require("react");
|
|
875
|
-
var
|
|
1121
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
876
1122
|
function Card({ className, ...props }) {
|
|
877
|
-
return /* @__PURE__ */ (0,
|
|
1123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
878
1124
|
"div",
|
|
879
1125
|
{
|
|
880
1126
|
"data-slot": "card",
|
|
@@ -887,7 +1133,7 @@ function Card({ className, ...props }) {
|
|
|
887
1133
|
);
|
|
888
1134
|
}
|
|
889
1135
|
function CardHeader({ className, ...props }) {
|
|
890
|
-
return /* @__PURE__ */ (0,
|
|
1136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
891
1137
|
"div",
|
|
892
1138
|
{
|
|
893
1139
|
"data-slot": "card-header",
|
|
@@ -900,7 +1146,7 @@ function CardHeader({ className, ...props }) {
|
|
|
900
1146
|
);
|
|
901
1147
|
}
|
|
902
1148
|
function CardContent({ className, ...props }) {
|
|
903
|
-
return /* @__PURE__ */ (0,
|
|
1149
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
904
1150
|
"div",
|
|
905
1151
|
{
|
|
906
1152
|
"data-slot": "card-content",
|
|
@@ -914,13 +1160,13 @@ function CardContent({ className, ...props }) {
|
|
|
914
1160
|
var React3 = require("react");
|
|
915
1161
|
var import_menu = require("@base-ui/react/menu");
|
|
916
1162
|
var import_lucide_react = require("lucide-react");
|
|
917
|
-
var
|
|
1163
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
918
1164
|
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";
|
|
919
1165
|
function DropdownMenu(props) {
|
|
920
|
-
return /* @__PURE__ */ (0,
|
|
1166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
|
|
921
1167
|
}
|
|
922
1168
|
function DropdownMenuTrigger(props) {
|
|
923
|
-
return /* @__PURE__ */ (0,
|
|
1169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
924
1170
|
}
|
|
925
1171
|
function DropdownMenuContent({
|
|
926
1172
|
className,
|
|
@@ -928,14 +1174,14 @@ function DropdownMenuContent({
|
|
|
928
1174
|
sideOffset = 4,
|
|
929
1175
|
...props
|
|
930
1176
|
}) {
|
|
931
|
-
return /* @__PURE__ */ (0,
|
|
1177
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
932
1178
|
import_menu.Menu.Positioner,
|
|
933
1179
|
{
|
|
934
1180
|
"data-slot": "dropdown-menu-positioner",
|
|
935
1181
|
className: "z-50",
|
|
936
1182
|
align,
|
|
937
1183
|
sideOffset,
|
|
938
|
-
children: /* @__PURE__ */ (0,
|
|
1184
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
939
1185
|
import_menu.Menu.Popup,
|
|
940
1186
|
{
|
|
941
1187
|
"data-slot": "dropdown-menu-content",
|
|
@@ -953,7 +1199,7 @@ function DropdownMenuItem({
|
|
|
953
1199
|
className,
|
|
954
1200
|
...props
|
|
955
1201
|
}) {
|
|
956
|
-
return /* @__PURE__ */ (0,
|
|
1202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
957
1203
|
import_menu.Menu.Item,
|
|
958
1204
|
{
|
|
959
1205
|
"data-slot": "dropdown-menu-item",
|
|
@@ -965,7 +1211,7 @@ function DropdownMenuItem({
|
|
|
965
1211
|
|
|
966
1212
|
// src/components/ReportScenario.tsx
|
|
967
1213
|
var import_lucide_react2 = require("lucide-react");
|
|
968
|
-
var
|
|
1214
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
969
1215
|
var STATUS_META = {
|
|
970
1216
|
passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
|
|
971
1217
|
failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
|
|
@@ -981,7 +1227,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
981
1227
|
const collapsible = collapse !== null && !hideTitle;
|
|
982
1228
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
983
1229
|
const actions = useScenarioActions();
|
|
984
|
-
return /* @__PURE__ */ (0,
|
|
1230
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
985
1231
|
Card,
|
|
986
1232
|
{
|
|
987
1233
|
id: scenario.id,
|
|
@@ -989,10 +1235,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
989
1235
|
...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
|
|
990
1236
|
className: "gap-0 py-0",
|
|
991
1237
|
children: [
|
|
992
|
-
/* @__PURE__ */ (0,
|
|
993
|
-
/* @__PURE__ */ (0,
|
|
994
|
-
hideTitle ? null : /* @__PURE__ */ (0,
|
|
995
|
-
collapsible ? /* @__PURE__ */ (0,
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardHeader, { className: "px-4 py-3", children: [
|
|
1239
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
1240
|
+
hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1241
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
996
1242
|
"button",
|
|
997
1243
|
{
|
|
998
1244
|
type: "button",
|
|
@@ -1001,49 +1247,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1001
1247
|
"aria-controls": bodyId,
|
|
1002
1248
|
"aria-label": `Toggle ${scenario.title}`,
|
|
1003
1249
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
1004
|
-
children: /* @__PURE__ */ (0,
|
|
1250
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
1005
1251
|
}
|
|
1006
1252
|
) : null,
|
|
1007
|
-
/* @__PURE__ */ (0,
|
|
1008
|
-
/* @__PURE__ */ (0,
|
|
1253
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
|
|
1254
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: scenario.title })
|
|
1009
1255
|
] }),
|
|
1010
|
-
/* @__PURE__ */ (0,
|
|
1011
|
-
/* @__PURE__ */ (0,
|
|
1012
|
-
scenario.durationMs > 0 ? /* @__PURE__ */ (0,
|
|
1013
|
-
meta.showPill ? /* @__PURE__ */ (0,
|
|
1014
|
-
actions ? /* @__PURE__ */ (0,
|
|
1015
|
-
/* @__PURE__ */ (0,
|
|
1256
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1258
|
+
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,
|
|
1259
|
+
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}` }),
|
|
1260
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenu, { children: [
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1016
1262
|
DropdownMenuTrigger,
|
|
1017
1263
|
{
|
|
1018
1264
|
"aria-label": `Actions for ${scenario.title}`,
|
|
1019
1265
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
1020
|
-
children: /* @__PURE__ */ (0,
|
|
1266
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
|
|
1021
1267
|
}
|
|
1022
1268
|
),
|
|
1023
|
-
/* @__PURE__ */ (0,
|
|
1024
|
-
/* @__PURE__ */ (0,
|
|
1025
|
-
/* @__PURE__ */ (0,
|
|
1269
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuContent, { align: "end", children: [
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.LinkIcon, {}),
|
|
1026
1272
|
" Copy link"
|
|
1027
1273
|
] }),
|
|
1028
|
-
/* @__PURE__ */ (0,
|
|
1029
|
-
/* @__PURE__ */ (0,
|
|
1274
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.FileTextIcon, {}),
|
|
1030
1276
|
" Copy as Markdown"
|
|
1031
1277
|
] }),
|
|
1032
|
-
/* @__PURE__ */ (0,
|
|
1033
|
-
/* @__PURE__ */ (0,
|
|
1278
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
|
|
1279
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.SparklesIcon, {}),
|
|
1034
1280
|
" Explain with AI"
|
|
1035
1281
|
] })
|
|
1036
1282
|
] })
|
|
1037
1283
|
] }) : null
|
|
1038
1284
|
] })
|
|
1039
1285
|
] }),
|
|
1040
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0,
|
|
1041
|
-
scenario.tags.map((t) => /* @__PURE__ */ (0,
|
|
1042
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0,
|
|
1286
|
+
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: [
|
|
1287
|
+
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}`)),
|
|
1288
|
+
(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}`))
|
|
1043
1289
|
] }) : null
|
|
1044
1290
|
] }),
|
|
1045
|
-
/* @__PURE__ */ (0,
|
|
1046
|
-
scenario.errorMessage ? /* @__PURE__ */ (0,
|
|
1291
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1292
|
+
scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1047
1293
|
"pre",
|
|
1048
1294
|
{
|
|
1049
1295
|
role: "alert",
|
|
@@ -1051,10 +1297,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1051
1297
|
children: scenario.errorMessage
|
|
1052
1298
|
}
|
|
1053
1299
|
) : null,
|
|
1054
|
-
/* @__PURE__ */ (0,
|
|
1055
|
-
|
|
1056
|
-
/* @__PURE__ */ (0,
|
|
1057
|
-
/* @__PURE__ */ (0,
|
|
1300
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportStoryboard, { scenario }),
|
|
1301
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportSteps, { scenario }),
|
|
1302
|
+
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,
|
|
1303
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportAttachments, { attachments: scenario.attachments }),
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportTrace, { spans: scenario.otelSpans })
|
|
1058
1305
|
] })
|
|
1059
1306
|
]
|
|
1060
1307
|
}
|
|
@@ -1062,13 +1309,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1062
1309
|
}
|
|
1063
1310
|
|
|
1064
1311
|
// src/components/ReportScenarioList.tsx
|
|
1065
|
-
var
|
|
1312
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1066
1313
|
function ReportScenarioList({ feature }) {
|
|
1067
|
-
return /* @__PURE__ */ (0,
|
|
1314
|
+
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)) });
|
|
1068
1315
|
}
|
|
1069
1316
|
|
|
1070
1317
|
// src/components/ReportFeature.tsx
|
|
1071
|
-
var
|
|
1318
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1072
1319
|
function ReportFeature({ feature }) {
|
|
1073
1320
|
const titleId = `${feature.id}-title`;
|
|
1074
1321
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1079,7 +1326,7 @@ function ReportFeature({ feature }) {
|
|
|
1079
1326
|
return (
|
|
1080
1327
|
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1081
1328
|
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1082
|
-
/* @__PURE__ */ (0,
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1083
1330
|
"section",
|
|
1084
1331
|
{
|
|
1085
1332
|
id: feature.id,
|
|
@@ -1087,9 +1334,9 @@ function ReportFeature({ feature }) {
|
|
|
1087
1334
|
"aria-labelledby": titleId,
|
|
1088
1335
|
className: "flex flex-col gap-3",
|
|
1089
1336
|
children: [
|
|
1090
|
-
/* @__PURE__ */ (0,
|
|
1091
|
-
/* @__PURE__ */ (0,
|
|
1092
|
-
/* @__PURE__ */ (0,
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
|
|
1338
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "min-w-0", children: [
|
|
1339
|
+
/* @__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)(
|
|
1093
1340
|
"button",
|
|
1094
1341
|
{
|
|
1095
1342
|
type: "button",
|
|
@@ -1098,31 +1345,31 @@ function ReportFeature({ feature }) {
|
|
|
1098
1345
|
"aria-controls": bodyId,
|
|
1099
1346
|
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1100
1347
|
children: [
|
|
1101
|
-
/* @__PURE__ */ (0,
|
|
1102
|
-
/* @__PURE__ */ (0,
|
|
1348
|
+
/* @__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" }),
|
|
1349
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate", children: feature.title })
|
|
1103
1350
|
]
|
|
1104
1351
|
}
|
|
1105
1352
|
) : feature.title }),
|
|
1106
|
-
/* @__PURE__ */ (0,
|
|
1353
|
+
/* @__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 })
|
|
1107
1354
|
] }),
|
|
1108
|
-
/* @__PURE__ */ (0,
|
|
1355
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1109
1356
|
"p",
|
|
1110
1357
|
{
|
|
1111
1358
|
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1112
1359
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1113
1360
|
children: [
|
|
1114
|
-
/* @__PURE__ */ (0,
|
|
1361
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
|
|
1115
1362
|
s.passed,
|
|
1116
1363
|
" passed"
|
|
1117
1364
|
] }),
|
|
1118
|
-
/* @__PURE__ */ (0,
|
|
1119
|
-
/* @__PURE__ */ (0,
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
|
|
1120
1367
|
s.failed,
|
|
1121
1368
|
" failed"
|
|
1122
1369
|
] }),
|
|
1123
|
-
skipped > 0 ? /* @__PURE__ */ (0,
|
|
1124
|
-
/* @__PURE__ */ (0,
|
|
1125
|
-
/* @__PURE__ */ (0,
|
|
1370
|
+
skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
1371
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1372
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
|
|
1126
1373
|
skipped,
|
|
1127
1374
|
" skipped"
|
|
1128
1375
|
] })
|
|
@@ -1131,7 +1378,7 @@ function ReportFeature({ feature }) {
|
|
|
1131
1378
|
}
|
|
1132
1379
|
)
|
|
1133
1380
|
] }),
|
|
1134
|
-
/* @__PURE__ */ (0,
|
|
1381
|
+
/* @__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 }) })
|
|
1135
1382
|
]
|
|
1136
1383
|
}
|
|
1137
1384
|
)
|
|
@@ -1139,17 +1386,17 @@ function ReportFeature({ feature }) {
|
|
|
1139
1386
|
}
|
|
1140
1387
|
|
|
1141
1388
|
// src/components/ReportFeatureList.tsx
|
|
1142
|
-
var
|
|
1389
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1143
1390
|
function ReportFeatureList() {
|
|
1144
1391
|
const report = useReport();
|
|
1145
|
-
return /* @__PURE__ */ (0,
|
|
1392
|
+
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)) });
|
|
1146
1393
|
}
|
|
1147
1394
|
|
|
1148
1395
|
// src/components/ui/empty.tsx
|
|
1149
1396
|
var import_class_variance_authority2 = require("class-variance-authority");
|
|
1150
|
-
var
|
|
1397
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1151
1398
|
function Empty({ className, ...props }) {
|
|
1152
|
-
return /* @__PURE__ */ (0,
|
|
1399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1153
1400
|
"div",
|
|
1154
1401
|
{
|
|
1155
1402
|
"data-slot": "empty",
|
|
@@ -1162,7 +1409,7 @@ function Empty({ className, ...props }) {
|
|
|
1162
1409
|
);
|
|
1163
1410
|
}
|
|
1164
1411
|
function EmptyHeader({ className, ...props }) {
|
|
1165
|
-
return /* @__PURE__ */ (0,
|
|
1412
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1166
1413
|
"div",
|
|
1167
1414
|
{
|
|
1168
1415
|
"data-slot": "empty-header",
|
|
@@ -1189,7 +1436,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
|
1189
1436
|
}
|
|
1190
1437
|
);
|
|
1191
1438
|
function EmptyTitle({ className, ...props }) {
|
|
1192
|
-
return /* @__PURE__ */ (0,
|
|
1439
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1193
1440
|
"div",
|
|
1194
1441
|
{
|
|
1195
1442
|
"data-slot": "empty-title",
|
|
@@ -1200,16 +1447,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1200
1447
|
}
|
|
1201
1448
|
|
|
1202
1449
|
// src/components/ReportEmpty.tsx
|
|
1203
|
-
var
|
|
1450
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1204
1451
|
function ReportEmpty({ message }) {
|
|
1205
|
-
return /* @__PURE__ */ (0,
|
|
1452
|
+
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." }) }) });
|
|
1206
1453
|
}
|
|
1207
1454
|
|
|
1208
1455
|
// src/components/ReportSummary.tsx
|
|
1209
|
-
var
|
|
1456
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1210
1457
|
function ReportSummary({ className }) {
|
|
1211
1458
|
const report = useReport();
|
|
1212
|
-
return /* @__PURE__ */ (0,
|
|
1459
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1213
1460
|
ReportSummaryView,
|
|
1214
1461
|
{
|
|
1215
1462
|
summary: report.summary,
|
|
@@ -1235,7 +1482,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1235
1482
|
if (summary.pending > 0) {
|
|
1236
1483
|
cards.push({ status: "pending", label: "Pending", value: summary.pending });
|
|
1237
1484
|
}
|
|
1238
|
-
return /* @__PURE__ */ (0,
|
|
1485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1239
1486
|
"dl",
|
|
1240
1487
|
{
|
|
1241
1488
|
className: cn(
|
|
@@ -1248,17 +1495,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1248
1495
|
"aria-label": ariaLabel,
|
|
1249
1496
|
children: cards.map((c) => {
|
|
1250
1497
|
const tone = TONE[c.status];
|
|
1251
|
-
return /* @__PURE__ */ (0,
|
|
1498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1252
1499
|
"div",
|
|
1253
1500
|
{
|
|
1254
1501
|
"data-status": c.status,
|
|
1255
1502
|
className: "rounded-lg border bg-card px-4 py-3",
|
|
1256
1503
|
children: [
|
|
1257
|
-
/* @__PURE__ */ (0,
|
|
1258
|
-
/* @__PURE__ */ (0,
|
|
1504
|
+
/* @__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: [
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
|
|
1259
1506
|
c.label
|
|
1260
1507
|
] }),
|
|
1261
|
-
/* @__PURE__ */ (0,
|
|
1508
|
+
/* @__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 })
|
|
1262
1509
|
]
|
|
1263
1510
|
},
|
|
1264
1511
|
c.status
|
|
@@ -1318,7 +1565,7 @@ function prUrl(ci) {
|
|
|
1318
1565
|
}
|
|
1319
1566
|
|
|
1320
1567
|
// src/components/ReportMeta.tsx
|
|
1321
|
-
var
|
|
1568
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1322
1569
|
function ReportMeta() {
|
|
1323
1570
|
const report = useReport();
|
|
1324
1571
|
const items = [];
|
|
@@ -1341,13 +1588,13 @@ function ReportMeta() {
|
|
|
1341
1588
|
const short = sha.slice(0, 8);
|
|
1342
1589
|
items.push({
|
|
1343
1590
|
label: "Commit",
|
|
1344
|
-
value: url ? /* @__PURE__ */ (0,
|
|
1591
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1345
1592
|
});
|
|
1346
1593
|
}
|
|
1347
1594
|
if (report.ci) {
|
|
1348
1595
|
items.push({
|
|
1349
1596
|
label: "CI",
|
|
1350
|
-
value: report.ci.url ? /* @__PURE__ */ (0,
|
|
1597
|
+
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1351
1598
|
ciDisplayName(report.ci.name),
|
|
1352
1599
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1353
1600
|
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
@@ -1358,7 +1605,7 @@ function ReportMeta() {
|
|
|
1358
1605
|
const label = `#${report.ci.prNumber}`;
|
|
1359
1606
|
items.push({
|
|
1360
1607
|
label: "PR",
|
|
1361
|
-
value: url ? /* @__PURE__ */ (0,
|
|
1608
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1362
1609
|
});
|
|
1363
1610
|
}
|
|
1364
1611
|
if (items.length === 0) return null;
|
|
@@ -1366,48 +1613,48 @@ function ReportMeta() {
|
|
|
1366
1613
|
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1367
1614
|
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1368
1615
|
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1369
|
-
/* @__PURE__ */ (0,
|
|
1370
|
-
/* @__PURE__ */ (0,
|
|
1371
|
-
/* @__PURE__ */ (0,
|
|
1616
|
+
/* @__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: [
|
|
1617
|
+
/* @__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: [
|
|
1618
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1372
1619
|
"Run details"
|
|
1373
1620
|
] }),
|
|
1374
|
-
/* @__PURE__ */ (0,
|
|
1375
|
-
/* @__PURE__ */ (0,
|
|
1376
|
-
/* @__PURE__ */ (0,
|
|
1621
|
+
/* @__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: [
|
|
1622
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1623
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1377
1624
|
] }, item.label)) })
|
|
1378
1625
|
] })
|
|
1379
1626
|
);
|
|
1380
1627
|
}
|
|
1381
1628
|
|
|
1382
1629
|
// src/components/ReportSchemaError.tsx
|
|
1383
|
-
var
|
|
1630
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1384
1631
|
function ReportSchemaError({ error }) {
|
|
1385
|
-
return /* @__PURE__ */ (0,
|
|
1386
|
-
/* @__PURE__ */ (0,
|
|
1387
|
-
/* @__PURE__ */ (0,
|
|
1388
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0,
|
|
1632
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1633
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1634
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: error.message }),
|
|
1635
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { children: [
|
|
1389
1636
|
"The report bundle is newer than this version of ",
|
|
1390
|
-
/* @__PURE__ */ (0,
|
|
1637
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: "executable-stories-react" }),
|
|
1391
1638
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1392
1639
|
] }) : null,
|
|
1393
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0,
|
|
1394
|
-
/* @__PURE__ */ (0,
|
|
1640
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("details", { children: [
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("summary", { children: [
|
|
1395
1642
|
error.issues.length,
|
|
1396
1643
|
" validation issue",
|
|
1397
1644
|
error.issues.length === 1 ? "" : "s"
|
|
1398
1645
|
] }),
|
|
1399
|
-
/* @__PURE__ */ (0,
|
|
1646
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1400
1647
|
] }) : null
|
|
1401
1648
|
] });
|
|
1402
1649
|
}
|
|
1403
1650
|
|
|
1404
1651
|
// src/components/ReportShell.tsx
|
|
1405
|
-
var
|
|
1652
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1406
1653
|
function ReportTitleBlock({ title }) {
|
|
1407
|
-
return /* @__PURE__ */ (0,
|
|
1408
|
-
/* @__PURE__ */ (0,
|
|
1409
|
-
/* @__PURE__ */ (0,
|
|
1410
|
-
/* @__PURE__ */ (0,
|
|
1654
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h1", { children: title ?? "Story Report" }),
|
|
1656
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportSummary, {}),
|
|
1657
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportMeta, {})
|
|
1411
1658
|
] });
|
|
1412
1659
|
}
|
|
1413
1660
|
function ReportErrorShell({
|
|
@@ -1416,28 +1663,28 @@ function ReportErrorShell({
|
|
|
1416
1663
|
title,
|
|
1417
1664
|
dataTheme
|
|
1418
1665
|
}) {
|
|
1419
|
-
return /* @__PURE__ */ (0,
|
|
1666
|
+
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 }) });
|
|
1420
1667
|
}
|
|
1421
1668
|
|
|
1422
1669
|
// src/components/Report.tsx
|
|
1423
|
-
var
|
|
1670
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1424
1671
|
function Report(props) {
|
|
1425
1672
|
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1426
1673
|
const result = unwrapReport(report);
|
|
1427
1674
|
if (!result.ok) {
|
|
1428
|
-
return /* @__PURE__ */ (0,
|
|
1675
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1429
1676
|
}
|
|
1430
1677
|
const data = result.data;
|
|
1431
1678
|
const hasContent = data.features.length > 0;
|
|
1432
|
-
return /* @__PURE__ */ (0,
|
|
1433
|
-
/* @__PURE__ */ (0,
|
|
1434
|
-
hasContent ? /* @__PURE__ */ (0,
|
|
1679
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
|
|
1680
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("header", { className: "es-report-header", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportTitleBlock, { title }) }),
|
|
1681
|
+
hasContent ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportFeatureList, {}) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportEmpty, {})
|
|
1435
1682
|
] }) });
|
|
1436
1683
|
}
|
|
1437
1684
|
|
|
1438
1685
|
// src/components/doc/MermaidView.tsx
|
|
1439
1686
|
var import_react8 = require("react");
|
|
1440
|
-
var
|
|
1687
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1441
1688
|
function prefersDark(el) {
|
|
1442
1689
|
const themed = el?.closest("[data-theme]");
|
|
1443
1690
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -1474,9 +1721,9 @@ function MermaidView({ entry, load }) {
|
|
|
1474
1721
|
};
|
|
1475
1722
|
}, [entry.code, load]);
|
|
1476
1723
|
if (svg) {
|
|
1477
|
-
return /* @__PURE__ */ (0,
|
|
1478
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
1479
|
-
/* @__PURE__ */ (0,
|
|
1724
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("figure", { ref, className: "my-3", children: [
|
|
1725
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1480
1727
|
"div",
|
|
1481
1728
|
{
|
|
1482
1729
|
role: "img",
|
|
@@ -1487,18 +1734,18 @@ function MermaidView({ entry, load }) {
|
|
|
1487
1734
|
)
|
|
1488
1735
|
] });
|
|
1489
1736
|
}
|
|
1490
|
-
return /* @__PURE__ */ (0,
|
|
1737
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(MermaidSource, { entry, ref });
|
|
1491
1738
|
}
|
|
1492
1739
|
|
|
1493
1740
|
// src/components/doc/MermaidDiagram.tsx
|
|
1494
|
-
var
|
|
1741
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1495
1742
|
var mermaidLoader;
|
|
1496
1743
|
function loadMermaid() {
|
|
1497
1744
|
mermaidLoader ??= import("mermaid").then((m) => m.default);
|
|
1498
1745
|
return mermaidLoader;
|
|
1499
1746
|
}
|
|
1500
1747
|
function MermaidDiagram({ entry, load = loadMermaid }) {
|
|
1501
|
-
return /* @__PURE__ */ (0,
|
|
1748
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MermaidView, { entry, load });
|
|
1502
1749
|
}
|
|
1503
1750
|
|
|
1504
1751
|
// src/components/ScenarioExplorer.tsx
|
|
@@ -1508,12 +1755,12 @@ var import_react9 = require("react");
|
|
|
1508
1755
|
var React4 = require("react");
|
|
1509
1756
|
var import_cmdk = require("cmdk");
|
|
1510
1757
|
var import_lucide_react3 = require("lucide-react");
|
|
1511
|
-
var
|
|
1758
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1512
1759
|
function Command({
|
|
1513
1760
|
className,
|
|
1514
1761
|
...props
|
|
1515
1762
|
}) {
|
|
1516
|
-
return /* @__PURE__ */ (0,
|
|
1763
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1517
1764
|
import_cmdk.Command,
|
|
1518
1765
|
{
|
|
1519
1766
|
"data-slot": "command",
|
|
@@ -1529,14 +1776,14 @@ function CommandInput({
|
|
|
1529
1776
|
className,
|
|
1530
1777
|
...props
|
|
1531
1778
|
}) {
|
|
1532
|
-
return /* @__PURE__ */ (0,
|
|
1779
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
1533
1780
|
"div",
|
|
1534
1781
|
{
|
|
1535
1782
|
"data-slot": "command-input-wrapper",
|
|
1536
1783
|
className: "flex h-9 items-center gap-2 border-b px-3",
|
|
1537
1784
|
children: [
|
|
1538
|
-
/* @__PURE__ */ (0,
|
|
1539
|
-
/* @__PURE__ */ (0,
|
|
1785
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react3.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
|
|
1786
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1540
1787
|
import_cmdk.Command.Input,
|
|
1541
1788
|
{
|
|
1542
1789
|
"data-slot": "command-input",
|
|
@@ -1555,7 +1802,7 @@ function CommandList({
|
|
|
1555
1802
|
className,
|
|
1556
1803
|
...props
|
|
1557
1804
|
}) {
|
|
1558
|
-
return /* @__PURE__ */ (0,
|
|
1805
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1559
1806
|
import_cmdk.Command.List,
|
|
1560
1807
|
{
|
|
1561
1808
|
"data-slot": "command-list",
|
|
@@ -1570,7 +1817,7 @@ function CommandList({
|
|
|
1570
1817
|
function CommandEmpty({
|
|
1571
1818
|
...props
|
|
1572
1819
|
}) {
|
|
1573
|
-
return /* @__PURE__ */ (0,
|
|
1820
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1574
1821
|
import_cmdk.Command.Empty,
|
|
1575
1822
|
{
|
|
1576
1823
|
"data-slot": "command-empty",
|
|
@@ -1583,7 +1830,7 @@ function CommandGroup({
|
|
|
1583
1830
|
className,
|
|
1584
1831
|
...props
|
|
1585
1832
|
}) {
|
|
1586
|
-
return /* @__PURE__ */ (0,
|
|
1833
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1587
1834
|
import_cmdk.Command.Group,
|
|
1588
1835
|
{
|
|
1589
1836
|
"data-slot": "command-group",
|
|
@@ -1599,7 +1846,7 @@ function CommandItem({
|
|
|
1599
1846
|
className,
|
|
1600
1847
|
...props
|
|
1601
1848
|
}) {
|
|
1602
|
-
return /* @__PURE__ */ (0,
|
|
1849
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1603
1850
|
import_cmdk.Command.Item,
|
|
1604
1851
|
{
|
|
1605
1852
|
"data-slot": "command-item",
|
|
@@ -1613,7 +1860,7 @@ function CommandItem({
|
|
|
1613
1860
|
}
|
|
1614
1861
|
|
|
1615
1862
|
// src/components/ScenarioExplorer.tsx
|
|
1616
|
-
var
|
|
1863
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
1617
1864
|
var STATUS_FILTERS = [
|
|
1618
1865
|
{ value: "all", label: "All" },
|
|
1619
1866
|
{ value: "passed", label: "Passed" },
|
|
@@ -1624,22 +1871,22 @@ var STATUS_FILTERS = [
|
|
|
1624
1871
|
function ScenarioExplorer({ scenarios, summary }) {
|
|
1625
1872
|
const [status, setStatus] = (0, import_react9.useState)("all");
|
|
1626
1873
|
const visible = status === "all" ? scenarios : scenarios.filter((s) => s.status === status);
|
|
1627
|
-
return /* @__PURE__ */ (0,
|
|
1628
|
-
summary ? /* @__PURE__ */ (0,
|
|
1874
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "font-sans text-foreground", children: [
|
|
1875
|
+
summary ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mb-3 text-sm text-muted-foreground", children: [
|
|
1629
1876
|
summary.total,
|
|
1630
1877
|
" scenarios \xB7 ",
|
|
1631
|
-
/* @__PURE__ */ (0,
|
|
1878
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "text-pass", children: [
|
|
1632
1879
|
summary.passed,
|
|
1633
1880
|
" passed"
|
|
1634
1881
|
] }),
|
|
1635
1882
|
" \xB7",
|
|
1636
1883
|
" ",
|
|
1637
|
-
/* @__PURE__ */ (0,
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "text-fail", children: [
|
|
1638
1885
|
summary.failed,
|
|
1639
1886
|
" failed"
|
|
1640
1887
|
] })
|
|
1641
1888
|
] }) : null,
|
|
1642
|
-
/* @__PURE__ */ (0,
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "mb-2 flex flex-wrap gap-1.5", role: "group", "aria-label": "Filter by status", children: STATUS_FILTERS.map((f) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1643
1890
|
"button",
|
|
1644
1891
|
{
|
|
1645
1892
|
type: "button",
|
|
@@ -1654,26 +1901,26 @@ function ScenarioExplorer({ scenarios, summary }) {
|
|
|
1654
1901
|
},
|
|
1655
1902
|
f.value
|
|
1656
1903
|
)) }),
|
|
1657
|
-
/* @__PURE__ */ (0,
|
|
1904
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
1658
1905
|
Command,
|
|
1659
1906
|
{
|
|
1660
1907
|
className: "rounded-lg border border-border bg-card",
|
|
1661
1908
|
filter: (value, search) => value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0,
|
|
1662
1909
|
children: [
|
|
1663
|
-
/* @__PURE__ */ (0,
|
|
1664
|
-
/* @__PURE__ */ (0,
|
|
1665
|
-
/* @__PURE__ */ (0,
|
|
1666
|
-
/* @__PURE__ */ (0,
|
|
1910
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CommandInput, { placeholder: "Search scenarios\u2026" }),
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(CommandList, { className: "max-h-none", children: [
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CommandEmpty, { children: "No scenarios match your filters." }),
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CommandGroup, { children: visible.map((s) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
1667
1914
|
CommandItem,
|
|
1668
1915
|
{
|
|
1669
1916
|
value: `${s.title} ${s.feature} ${s.tags.join(" ")}`,
|
|
1670
1917
|
asChild: true,
|
|
1671
1918
|
className: "cursor-pointer",
|
|
1672
|
-
children: /* @__PURE__ */ (0,
|
|
1673
|
-
/* @__PURE__ */ (0,
|
|
1674
|
-
/* @__PURE__ */ (0,
|
|
1675
|
-
s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0,
|
|
1676
|
-
/* @__PURE__ */ (0,
|
|
1919
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("a", { href: s.href, className: "flex items-center gap-3", children: [
|
|
1920
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Badge, { variant: s.status, children: s.status }),
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "min-w-0 flex-1 truncate font-medium", children: s.title }),
|
|
1922
|
+
s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Badge, { variant: "tag", className: "hidden sm:inline-flex", children: t }, t)),
|
|
1923
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("code", { className: "hidden font-mono text-xs text-muted-foreground md:inline", children: s.feature })
|
|
1677
1924
|
] })
|
|
1678
1925
|
},
|
|
1679
1926
|
s.id
|
|
@@ -2169,6 +2416,9 @@ var story_report_v1_default = {
|
|
|
2169
2416
|
{
|
|
2170
2417
|
$ref: "#/$defs/DocHtml"
|
|
2171
2418
|
},
|
|
2419
|
+
{
|
|
2420
|
+
$ref: "#/$defs/DocState"
|
|
2421
|
+
},
|
|
2172
2422
|
{
|
|
2173
2423
|
$ref: "#/$defs/DocCustom"
|
|
2174
2424
|
}
|
|
@@ -2544,6 +2794,33 @@ var story_report_v1_default = {
|
|
|
2544
2794
|
}
|
|
2545
2795
|
]
|
|
2546
2796
|
},
|
|
2797
|
+
DocState: {
|
|
2798
|
+
type: "object",
|
|
2799
|
+
properties: {
|
|
2800
|
+
kind: {
|
|
2801
|
+
const: "state"
|
|
2802
|
+
},
|
|
2803
|
+
label: {
|
|
2804
|
+
type: "string"
|
|
2805
|
+
},
|
|
2806
|
+
value: {},
|
|
2807
|
+
phase: {
|
|
2808
|
+
$ref: "#/$defs/DocPhase"
|
|
2809
|
+
},
|
|
2810
|
+
children: {
|
|
2811
|
+
type: "array",
|
|
2812
|
+
items: {
|
|
2813
|
+
$ref: "#/$defs/DocEntry"
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
},
|
|
2817
|
+
required: [
|
|
2818
|
+
"kind",
|
|
2819
|
+
"value",
|
|
2820
|
+
"phase"
|
|
2821
|
+
],
|
|
2822
|
+
additionalProperties: false
|
|
2823
|
+
},
|
|
2547
2824
|
DocCustom: {
|
|
2548
2825
|
type: "object",
|
|
2549
2826
|
properties: {
|
|
@@ -2694,6 +2971,7 @@ function parseStoryReport(input) {
|
|
|
2694
2971
|
ReportSchemaError,
|
|
2695
2972
|
ReportStepItem,
|
|
2696
2973
|
ReportSteps,
|
|
2974
|
+
ReportStoryboard,
|
|
2697
2975
|
ReportSummary,
|
|
2698
2976
|
ReportSummaryView,
|
|
2699
2977
|
ReportTrace,
|