executable-stories-react 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -377,23 +377,11 @@ function useReport() {
377
377
  return ctx.report;
378
378
  }
379
379
 
380
- // ../executable-stories-core/dist/utils/duration.js
381
- function formatDuration(ms) {
382
- if (ms < 1e3) {
383
- return `${Math.round(ms)} ms`;
384
- }
385
- if (ms < 6e4) {
386
- return `${(ms / 1e3).toFixed(2)} s`;
387
- }
388
- const minutes = Math.floor(ms / 6e4);
389
- const seconds = Math.floor(ms % 6e4 / 1e3);
390
- if (minutes < 60) {
391
- return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
392
- }
393
- const hours = Math.floor(minutes / 60);
394
- const remainingMinutes = minutes % 60;
395
- return `${hours}h ${remainingMinutes}m`;
396
- }
380
+ // src/components/ReportScenario.tsx
381
+ var import_duration3 = require("executable-stories-core/utils/duration");
382
+
383
+ // src/components/ReportSteps.tsx
384
+ var import_duration = require("executable-stories-core/utils/duration");
397
385
 
398
386
  // src/components/doc/DocNote.tsx
399
387
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -485,28 +473,13 @@ function DocLink({ entry }) {
485
473
  // src/components/doc/DocSection.tsx
486
474
  var import_marked = require("marked");
487
475
 
488
- // ../executable-stories-core/dist/utils/url.js
489
- function safeUrl(value) {
490
- if (!value) return void 0;
491
- const trimmed = value.trim();
492
- const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
493
- if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
494
- return trimmed;
495
- }
496
- function safeImageUrl(value) {
497
- if (!value) return void 0;
498
- const trimmed = value.trim();
499
- if (/^data:image\//i.test(trimmed)) return trimmed;
500
- return safeUrl(trimmed);
501
- }
502
- function isLocalFsPath(value) {
503
- return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
504
- }
476
+ // src/lib/url.ts
477
+ var import_url = require("executable-stories-core/utils/url");
505
478
 
506
479
  // src/components/doc/DocSection.tsx
507
480
  var import_jsx_runtime9 = require("react/jsx-runtime");
508
481
  function neutralizeUrl(value) {
509
- return safeUrl(value) ?? "#";
482
+ return (0, import_url.safeUrl)(value) ?? "#";
510
483
  }
511
484
  function safeMarkdownHtml(markdown) {
512
485
  const raw = import_marked.marked.parse(markdown, { async: false });
@@ -557,7 +530,7 @@ function DocMermaid({ entry }) {
557
530
  // src/components/doc/DocScreenshot.tsx
558
531
  var import_jsx_runtime11 = require("react/jsx-runtime");
559
532
  function DocScreenshot({ entry }) {
560
- const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
533
+ const src = (0, import_url.isLocalFsPath)(entry.path) ? void 0 : (0, import_url.safeImageUrl)(entry.path);
561
534
  if (!src) {
562
535
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
563
536
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
@@ -582,15 +555,15 @@ function DocScreenshot({ entry }) {
582
555
  // src/components/doc/DocVideo.tsx
583
556
  var import_jsx_runtime12 = require("react/jsx-runtime");
584
557
  function DocVideo({ entry }) {
585
- if (isLocalFsPath(entry.path)) {
558
+ if ((0, import_url.isLocalFsPath)(entry.path)) {
586
559
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
587
560
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
588
561
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
589
562
  entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
590
563
  ] });
591
564
  }
592
- const src = safeUrl(entry.path);
593
- const poster = safeUrl(entry.poster);
565
+ const src = (0, import_url.safeUrl)(entry.path);
566
+ const poster = (0, import_url.safeUrl)(entry.poster);
594
567
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
595
568
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
596
569
  "video",
@@ -611,7 +584,7 @@ var import_jsx_runtime13 = require("react/jsx-runtime");
611
584
  function DocHtml({ entry }) {
612
585
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
613
586
  const title = entry.title ?? "Embedded HTML";
614
- const safeHref = safeUrl(entry.url ?? entry.path);
587
+ const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
615
588
  const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
616
589
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
617
590
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
@@ -778,7 +751,7 @@ function ReportStepItem({ step }) {
778
751
  }
779
752
  ),
780
753
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
781
- step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
754
+ step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration.formatDuration)(step.durationMs) }) : null
782
755
  ] }),
783
756
  step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
784
757
  "pre",
@@ -794,125 +767,10 @@ function ReportStepItem({ step }) {
794
767
  );
795
768
  }
796
769
 
797
- // ../executable-stories-core/dist/state-diff.js
798
- var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
799
- function deepEqual(a, b) {
800
- if (Object.is(a, b)) return true;
801
- if (Array.isArray(a) && Array.isArray(b)) {
802
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
803
- }
804
- if (isPlainObject(a) && isPlainObject(b)) {
805
- const ak = Object.keys(a);
806
- const bk = Object.keys(b);
807
- return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
808
- }
809
- return false;
810
- }
811
- var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
812
- function walk(path, before, after, out) {
813
- if (Object.is(before, after)) return;
814
- if (Array.isArray(before) && Array.isArray(after)) {
815
- const shared = Math.min(before.length, after.length);
816
- for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
817
- for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
818
- for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
819
- return;
820
- }
821
- if (isPlainObject(before) && isPlainObject(after)) {
822
- for (const key of Object.keys(after)) {
823
- const p = joinKey(path, key);
824
- if (key in before) walk(p, before[key], after[key], out);
825
- else out.push({ path: p, kind: "added", after: after[key] });
826
- }
827
- for (const key of Object.keys(before)) {
828
- if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
829
- }
830
- return;
831
- }
832
- if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
833
- }
834
- function diffStateValues(before, after) {
835
- const out = [];
836
- walk("", before, after, out);
837
- return out;
838
- }
839
- function formatStateValue(value, maxLen = 60) {
840
- const s = value === void 0 ? "undefined" : JSON.stringify(value);
841
- return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
842
- }
843
- function summarizeStateChanges(changes) {
844
- return changes.map((c) => {
845
- const at = c.path === "" ? "(root)" : c.path;
846
- switch (c.kind) {
847
- case "added":
848
- return `+ ${at}: ${formatStateValue(c.after)}`;
849
- case "removed":
850
- return `\u2212 ${at}`;
851
- case "changed":
852
- return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
853
- }
854
- });
855
- }
856
-
857
- // ../executable-stories-core/dist/storyboard.js
858
- function findScreenshot(entries) {
859
- for (const entry of entries) {
860
- if (entry.kind === "screenshot") return entry;
861
- if (entry.children) {
862
- const nested = findScreenshot(entry.children);
863
- if (nested) return nested;
864
- }
865
- }
866
- return void 0;
867
- }
868
- function findStates(entries, out = []) {
869
- for (const entry of entries) {
870
- if (entry.kind === "state") out.push(entry);
871
- if (entry.children) findStates(entry.children, out);
872
- }
873
- return out;
874
- }
875
- var laneOf = (label) => label ?? "";
876
- function extractStoryboardFrames(scenario) {
877
- const frames = [];
878
- const laneOrder = [];
879
- const previous = /* @__PURE__ */ new Map();
880
- for (const step of scenario.steps) {
881
- const shot = findScreenshot(step.docEntries);
882
- const stateDocs = findStates(step.docEntries);
883
- if (!shot && stateDocs.length === 0) continue;
884
- const byLane = /* @__PURE__ */ new Map();
885
- for (const doc of stateDocs) {
886
- const lane = laneOf(doc.label);
887
- if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
888
- byLane.set(lane, doc);
889
- }
890
- const states = [];
891
- for (const lane of laneOrder) {
892
- const doc = byLane.get(lane);
893
- if (!doc) continue;
894
- const card = {
895
- ...doc.label !== void 0 && { label: doc.label },
896
- value: doc.value
897
- };
898
- if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
899
- previous.set(lane, doc.value);
900
- states.push(card);
901
- }
902
- frames.push({
903
- stepId: step.id,
904
- stepIndex: step.index,
905
- keyword: step.keyword,
906
- text: step.text,
907
- status: step.status,
908
- ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
909
- states
910
- });
911
- }
912
- return frames;
913
- }
914
-
915
770
  // src/components/ReportStoryboard.tsx
771
+ var import_storyboard = require("executable-stories-core/storyboard");
772
+ var import_state_diff = require("executable-stories-core/state-diff");
773
+ var import_url6 = require("executable-stories-core/utils/url");
916
774
  var import_jsx_runtime20 = require("react/jsx-runtime");
917
775
  var FRAME_GLYPH = {
918
776
  passed: "\u2713",
@@ -927,7 +785,7 @@ var FRAME_GLYPH_COLOR = {
927
785
  pending: "text-pend"
928
786
  };
929
787
  function ReportStoryboard({ scenario }) {
930
- const frames = extractStoryboardFrames(scenario);
788
+ const frames = (0, import_storyboard.extractStoryboardFrames)(scenario);
931
789
  if (frames.length < 2) return null;
932
790
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
933
791
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
@@ -961,7 +819,7 @@ function StoryboardFrameItem({ frame }) {
961
819
  }
962
820
  function FrameScreenshot({ frame, label }) {
963
821
  const path = frame.screenshot?.path ?? "";
964
- const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
822
+ const src = (0, import_url6.isLocalFsPath)(path) ? void 0 : (0, import_url6.safeImageUrl)(path);
965
823
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
966
824
  "a",
967
825
  {
@@ -1007,7 +865,7 @@ function StateCardView({ card }) {
1007
865
  "rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
1008
866
  CHANGE_TONE[change.kind]
1009
867
  ),
1010
- children: summarizeStateChanges([change])[0]
868
+ children: (0, import_state_diff.summarizeStateChanges)([change])[0]
1011
869
  },
1012
870
  i
1013
871
  )) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
@@ -1067,6 +925,9 @@ function ReportAttachments({ attachments }) {
1067
925
  );
1068
926
  }
1069
927
 
928
+ // src/components/ReportTrace.tsx
929
+ var import_duration2 = require("executable-stories-core/utils/duration");
930
+
1070
931
  // src/lib/trace.ts
1071
932
  var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
1072
933
  var safeStatus = (s) => VALID.has(s) ? s : "unset";
@@ -1119,15 +980,15 @@ function toTraceRows(spans) {
1119
980
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
1120
981
  const rows = [];
1121
982
  const seen = /* @__PURE__ */ new Set();
1122
- const walk2 = (node, depth) => {
983
+ const walk = (node, depth) => {
1123
984
  if (seen.has(node.span.spanId)) return;
1124
985
  seen.add(node.span.spanId);
1125
986
  rows.push({ ...node.span, depth });
1126
- for (const child of node.children) walk2(child, depth + 1);
987
+ for (const child of node.children) walk(child, depth + 1);
1127
988
  };
1128
- for (const root of roots) walk2(root, 0);
989
+ for (const root of roots) walk(root, 0);
1129
990
  for (const node of byId.values()) {
1130
- if (!seen.has(node.span.spanId)) walk2(node, 0);
991
+ if (!seen.has(node.span.spanId)) walk(node, 0);
1131
992
  }
1132
993
  return rows;
1133
994
  }
@@ -1161,7 +1022,7 @@ function ReportTrace({ spans }) {
1161
1022
  " span",
1162
1023
  rows.length === 1 ? "" : "s",
1163
1024
  ", ",
1164
- formatDuration(totalMs),
1025
+ (0, import_duration2.formatDuration)(totalMs),
1165
1026
  ")"
1166
1027
  ] }),
1167
1028
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
@@ -1184,7 +1045,7 @@ function ReportTrace({ spans }) {
1184
1045
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
1185
1046
  }
1186
1047
  ) }),
1187
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
1048
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: (0, import_duration2.formatDuration)(row.durationMs) })
1188
1049
  ] }, `${row.spanId}-${i}`);
1189
1050
  }) })
1190
1051
  ] });
@@ -1295,6 +1156,7 @@ function useCollapse() {
1295
1156
 
1296
1157
  // src/interactive/scenario-actions.tsx
1297
1158
  var import_react7 = require("react");
1159
+ var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
1298
1160
  var ScenarioActionsContext = (0, import_react7.createContext)(null);
1299
1161
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1300
1162
  function useScenarioActions() {
@@ -1440,7 +1302,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1440
1302
  ] }),
1441
1303
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1442
1304
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1443
- scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1305
+ scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration3.formatDuration)(scenario.durationMs) }) : null,
1444
1306
  meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1445
1307
  actions ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenu, { children: [
1446
1308
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
@@ -1700,6 +1562,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1700
1562
  );
1701
1563
  }
1702
1564
 
1565
+ // src/components/ReportMeta.tsx
1566
+ var import_duration4 = require("executable-stories-core/utils/duration");
1567
+
1703
1568
  // src/lib/provenance.ts
1704
1569
  function reportLastRunMs(report) {
1705
1570
  if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
@@ -1759,7 +1624,7 @@ function ReportMeta() {
1759
1624
  items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
1760
1625
  }
1761
1626
  if (report.durationMs) {
1762
- items.push({ label: "Duration", value: formatDuration(report.durationMs) });
1627
+ items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
1763
1628
  }
1764
1629
  if (report.packageVersion) {
1765
1630
  items.push({ label: "Version", value: report.packageVersion });