executable-stories-react 0.5.0 → 0.6.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.
@@ -241,7 +241,7 @@ function DocTable({ entry }) {
241
241
  entry.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
242
242
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
243
243
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
244
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
244
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { children: row.map((cell2, j) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell2 }, j)) }, i)) })
245
245
  ] }) })
246
246
  ] });
247
247
  }
@@ -894,20 +894,118 @@ function useCollapseState(computeDefault) {
894
894
 
895
895
  // src/interactive/scenario-actions.tsx
896
896
  var import_react7 = require("react");
897
+
898
+ // ../executable-stories-core/dist/utils/scenario-markdown.js
899
+ function cell(value) {
900
+ return value.replaceAll("|", "\\|").replaceAll("\n", " ");
901
+ }
902
+ function stringify(value) {
903
+ if (typeof value === "string") return value;
904
+ try {
905
+ return JSON.stringify(value, null, 2) ?? String(value);
906
+ } catch {
907
+ return String(value);
908
+ }
909
+ }
910
+ function docEntryToMarkdown(entry) {
911
+ const lines = [];
912
+ switch (entry.kind) {
913
+ case "note":
914
+ lines.push(entry.text);
915
+ break;
916
+ case "tag":
917
+ lines.push(entry.names.map((n) => `\`@${n}\``).join(" "));
918
+ break;
919
+ case "kv":
920
+ lines.push(`**${entry.label}:** ${stringify(entry.value)}`);
921
+ break;
922
+ case "code":
923
+ lines.push(`**${entry.label}**`, "", "```" + (entry.lang ?? ""), entry.content, "```");
924
+ break;
925
+ case "table": {
926
+ lines.push(`**${entry.label}**`, "");
927
+ lines.push(`| ${entry.columns.map(cell).join(" | ")} |`);
928
+ lines.push(`| ${entry.columns.map(() => "---").join(" | ")} |`);
929
+ for (const row of entry.rows) lines.push(`| ${row.map(cell).join(" | ")} |`);
930
+ break;
931
+ }
932
+ case "link":
933
+ lines.push(`[${entry.label}](${entry.url})`);
934
+ break;
935
+ case "section":
936
+ lines.push(`### ${entry.title}`, "", entry.markdown);
937
+ break;
938
+ case "mermaid":
939
+ if (entry.title) lines.push(`**${entry.title}**`, "");
940
+ lines.push("```mermaid", entry.code, "```");
941
+ break;
942
+ case "screenshot":
943
+ lines.push(`![${entry.alt ?? "screenshot"}](${entry.path})`);
944
+ break;
945
+ case "video":
946
+ lines.push(`Video: ${entry.path}${entry.caption ? ` \u2014 ${entry.caption}` : ""}`);
947
+ break;
948
+ case "html": {
949
+ const htmlTitle = entry.title ?? "Embedded HTML";
950
+ if (entry.content) {
951
+ lines.push(`**${htmlTitle}**`, "", "```html", entry.content, "```");
952
+ } else if (entry.url) {
953
+ lines.push(`[${htmlTitle}](${entry.url})`);
954
+ } else {
955
+ lines.push(`Embedded HTML: ${entry.path ?? "(inline)"}`);
956
+ }
957
+ break;
958
+ }
959
+ case "custom":
960
+ lines.push(`**${entry.type}**`, "", "```json", stringify(entry.data), "```");
961
+ break;
962
+ }
963
+ for (const child of entry.children ?? []) {
964
+ lines.push("", ...docEntryToMarkdown(child));
965
+ }
966
+ return lines;
967
+ }
968
+ function stepLines(step, ordinal, full) {
969
+ const status = step.status === "passed" ? "" : ` \u2014 **${step.status}**`;
970
+ const lines = [`${ordinal}. **${step.keyword}** ${step.text}${status}`];
971
+ if (!full) return lines;
972
+ if (step.errorMessage) {
973
+ lines.push(...step.errorMessage.split("\n").map((l) => ` > ${l}`));
974
+ }
975
+ for (const entry of step.docEntries) {
976
+ lines.push("", ...docEntryToMarkdown(entry).map((l) => l === "" ? l : ` ${l}`));
977
+ }
978
+ return lines;
979
+ }
980
+ function scenarioToMarkdown(scenario, options = {}) {
981
+ const { variant = "full", meta = [] } = options;
982
+ const full = variant === "full";
983
+ const lines = full ? [`# ${scenario.title}`, ""] : [`## ${scenario.title} _(${scenario.status})_`, ""];
984
+ if (full && meta.length > 0) lines.push(...meta);
985
+ if (scenario.steps.length > 0) {
986
+ if (full) lines.push("", "## Steps", "");
987
+ scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full)));
988
+ }
989
+ if (scenario.errorMessage) {
990
+ if (full) lines.push("", "## Failure", "");
991
+ else lines.push("");
992
+ lines.push("```", scenario.errorMessage.trim(), "```");
993
+ }
994
+ if (full && scenario.docEntries.length > 0) {
995
+ lines.push("", "## Docs", "");
996
+ for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry), "");
997
+ }
998
+ return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n").trimEnd() + (full ? "\n" : "");
999
+ }
1000
+
1001
+ // src/interactive/scenario-actions.tsx
897
1002
  var ScenarioActionsContext = (0, import_react7.createContext)(null);
898
1003
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
899
1004
  function useScenarioActions() {
900
1005
  return (0, import_react7.useContext)(ScenarioActionsContext);
901
1006
  }
902
- function scenarioToMarkdown(scenario) {
903
- const lines = [`## ${scenario.title} _(${scenario.status})_`, ""];
904
- for (const step of scenario.steps) {
905
- lines.push(`- **${step.keyword}** ${step.text}`);
906
- }
907
- if (scenario.errorMessage) {
908
- lines.push("", "```", scenario.errorMessage.trim(), "```");
909
- }
910
- return lines.join("\n");
1007
+ function scenarioToMarkdown2(scenario) {
1008
+ return scenarioToMarkdown(scenario, { variant: "compact" });
911
1009
  }
912
1010
  function scenarioToPrompt(scenario) {
913
1011
  const lines = [
@@ -2072,6 +2170,33 @@ function listFailures(report) {
2072
2170
  return out;
2073
2171
  }
2074
2172
 
2173
+ // src/interactive/triage-default.ts
2174
+ function needsAttention(status) {
2175
+ return status === "failed";
2176
+ }
2177
+ function defaultCollapsedIds(report) {
2178
+ const collapsed = /* @__PURE__ */ new Set();
2179
+ if (report.summary.failed === 0) return collapsed;
2180
+ for (const feature of report.features) {
2181
+ const featureHasFailure = feature.scenarios.some((s) => needsAttention(s.status));
2182
+ if (!featureHasFailure) collapsed.add(feature.id);
2183
+ for (const scenario of feature.scenarios) {
2184
+ if (!needsAttention(scenario.status)) collapsed.add(scenario.id);
2185
+ }
2186
+ }
2187
+ return collapsed;
2188
+ }
2189
+ function failuresFirst(report) {
2190
+ if (report.summary.failed === 0) return report;
2191
+ const withFailures = [];
2192
+ const rest = [];
2193
+ for (const feature of report.features) {
2194
+ (feature.scenarios.some((s) => needsAttention(s.status)) ? withFailures : rest).push(feature);
2195
+ }
2196
+ if (withFailures.length === 0) return report;
2197
+ return { ...report, features: [...withFailures, ...rest] };
2198
+ }
2199
+
2075
2200
  // src/interactive/ReportFilters.tsx
2076
2201
  var import_react14 = require("react");
2077
2202
  var import_jsx_runtime41 = require("react/jsx-runtime");
@@ -2362,7 +2487,9 @@ function ReportInteractiveView({
2362
2487
  const deferredQuery = (0, import_react18.useDeferredValue)(query);
2363
2488
  const failures = (0, import_react18.useMemo)(() => listFailures(report), [report]);
2364
2489
  const filtered = (0, import_react18.useMemo)(
2365
- () => filterReport(report, { query: deferredQuery, status: statusFilter, tags: activeTags }),
2490
+ // Features carrying failures float to the top so the first thing on screen
2491
+ // is the thing that needs attention; an all-green run keeps source order.
2492
+ () => failuresFirst(filterReport(report, { query: deferredQuery, status: statusFilter, tags: activeTags })),
2366
2493
  [report, deferredQuery, statusFilter, activeTags]
2367
2494
  );
2368
2495
  const isFiltering = query !== deferredQuery;
@@ -2400,7 +2527,7 @@ function ReportInteractiveView({
2400
2527
  const scenarioActions = (0, import_react18.useMemo)(
2401
2528
  () => ({
2402
2529
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
2403
- copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
2530
+ copyMarkdown: (s) => copy(scenarioToMarkdown2(s), "Markdown copied"),
2404
2531
  copyPrompt: (s) => copy(scenarioToPrompt(s), "Prompt copied")
2405
2532
  }),
2406
2533
  [copy]
@@ -2410,9 +2537,9 @@ function ReportInteractiveView({
2410
2537
  () => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
2411
2538
  [report]
2412
2539
  );
2413
- const collapse = useCollapseState();
2540
+ const collapse = useCollapseState((0, import_react18.useCallback)(() => defaultCollapsedIds(report), [report]));
2414
2541
  const collapseAll = (0, import_react18.useCallback)(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
2415
- const [expandedAll, setExpandedAll] = (0, import_react18.useState)(true);
2542
+ const [expandedAll, setExpandedAll] = (0, import_react18.useState)(() => report.summary.failed === 0);
2416
2543
  const setExpanded = (0, import_react18.useCallback)(
2417
2544
  (expanded) => {
2418
2545
  if (expanded) collapse.expandAll();