@flanksource/clicky-ui 0.2.12 → 0.2.13

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.
Files changed (168) hide show
  1. package/dist/components/Combobox.cjs +47 -33
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts +12 -0
  4. package/dist/components/Combobox.d.ts.map +1 -1
  5. package/dist/components/Combobox.js +48 -34
  6. package/dist/components/Combobox.js.map +1 -1
  7. package/dist/components/IconButton.cjs +26 -0
  8. package/dist/components/IconButton.cjs.map +1 -0
  9. package/dist/components/IconButton.d.ts +25 -0
  10. package/dist/components/IconButton.d.ts.map +1 -0
  11. package/dist/components/IconButton.js +26 -0
  12. package/dist/components/IconButton.js.map +1 -0
  13. package/dist/components/SecretKeySelector.cjs +151 -0
  14. package/dist/components/SecretKeySelector.cjs.map +1 -0
  15. package/dist/components/SecretKeySelector.d.ts +47 -0
  16. package/dist/components/SecretKeySelector.d.ts.map +1 -0
  17. package/dist/components/SecretKeySelector.js +151 -0
  18. package/dist/components/SecretKeySelector.js.map +1 -0
  19. package/dist/components/WorkloadPicker.cjs +144 -0
  20. package/dist/components/WorkloadPicker.cjs.map +1 -0
  21. package/dist/components/WorkloadPicker.d.ts +52 -0
  22. package/dist/components/WorkloadPicker.d.ts.map +1 -0
  23. package/dist/components/WorkloadPicker.js +144 -0
  24. package/dist/components/WorkloadPicker.js.map +1 -0
  25. package/dist/components/icon-menu-picker.cjs +83 -112
  26. package/dist/components/icon-menu-picker.cjs.map +1 -1
  27. package/dist/components/icon-menu-picker.d.ts.map +1 -1
  28. package/dist/components/icon-menu-picker.js +85 -114
  29. package/dist/components/icon-menu-picker.js.map +1 -1
  30. package/dist/components.cjs +10 -0
  31. package/dist/components.cjs.map +1 -1
  32. package/dist/components.d.ts +3 -0
  33. package/dist/components.d.ts.map +1 -1
  34. package/dist/components.js +11 -1
  35. package/dist/components.js.map +1 -1
  36. package/dist/data/Clicky.cjs +1 -1
  37. package/dist/data/Clicky.cjs.map +1 -1
  38. package/dist/data/Clicky.js +1 -1
  39. package/dist/data/Clicky.js.map +1 -1
  40. package/dist/data/Version.cjs +42 -0
  41. package/dist/data/Version.cjs.map +1 -0
  42. package/dist/data/Version.d.ts +36 -0
  43. package/dist/data/Version.d.ts.map +1 -0
  44. package/dist/data/Version.js +42 -0
  45. package/dist/data/Version.js.map +1 -0
  46. package/dist/data/test-runner/TestDetailPanel.cjs +165 -0
  47. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -0
  48. package/dist/data/test-runner/TestDetailPanel.d.ts +7 -0
  49. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -0
  50. package/dist/data/test-runner/TestDetailPanel.js +165 -0
  51. package/dist/data/test-runner/TestDetailPanel.js.map +1 -0
  52. package/dist/data/test-runner/TestFailureDetail.cjs +78 -0
  53. package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -0
  54. package/dist/data/test-runner/TestFailureDetail.d.ts +14 -0
  55. package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -0
  56. package/dist/data/test-runner/TestFailureDetail.js +78 -0
  57. package/dist/data/test-runner/TestFailureDetail.js.map +1 -0
  58. package/dist/data/test-runner/TestFilterBar.cjs +108 -0
  59. package/dist/data/test-runner/TestFilterBar.cjs.map +1 -0
  60. package/dist/data/test-runner/TestFilterBar.d.ts +15 -0
  61. package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -0
  62. package/dist/data/test-runner/TestFilterBar.js +108 -0
  63. package/dist/data/test-runner/TestFilterBar.js.map +1 -0
  64. package/dist/data/test-runner/TestRunSummary.cjs +81 -0
  65. package/dist/data/test-runner/TestRunSummary.cjs.map +1 -0
  66. package/dist/data/test-runner/TestRunSummary.d.ts +19 -0
  67. package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -0
  68. package/dist/data/test-runner/TestRunSummary.js +81 -0
  69. package/dist/data/test-runner/TestRunSummary.js.map +1 -0
  70. package/dist/data/test-runner/TestRunner.cjs +143 -0
  71. package/dist/data/test-runner/TestRunner.cjs.map +1 -0
  72. package/dist/data/test-runner/TestRunner.d.ts +42 -0
  73. package/dist/data/test-runner/TestRunner.d.ts.map +1 -0
  74. package/dist/data/test-runner/TestRunner.fixtures.d.ts +26 -0
  75. package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -0
  76. package/dist/data/test-runner/TestRunner.js +143 -0
  77. package/dist/data/test-runner/TestRunner.js.map +1 -0
  78. package/dist/data/test-runner/TestTree.cjs +42 -0
  79. package/dist/data/test-runner/TestTree.cjs.map +1 -0
  80. package/dist/data/test-runner/TestTree.d.ts +9 -0
  81. package/dist/data/test-runner/TestTree.d.ts.map +1 -0
  82. package/dist/data/test-runner/TestTree.js +42 -0
  83. package/dist/data/test-runner/TestTree.js.map +1 -0
  84. package/dist/data/test-runner/TestTreeNode.cjs +99 -0
  85. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -0
  86. package/dist/data/test-runner/TestTreeNode.d.ts +12 -0
  87. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -0
  88. package/dist/data/test-runner/TestTreeNode.js +99 -0
  89. package/dist/data/test-runner/TestTreeNode.js.map +1 -0
  90. package/dist/data/test-runner/adapter.cjs +57 -0
  91. package/dist/data/test-runner/adapter.cjs.map +1 -0
  92. package/dist/data/test-runner/adapter.d.ts +60 -0
  93. package/dist/data/test-runner/adapter.d.ts.map +1 -0
  94. package/dist/data/test-runner/adapter.js +57 -0
  95. package/dist/data/test-runner/adapter.js.map +1 -0
  96. package/dist/data/test-runner/context.cjs +15 -0
  97. package/dist/data/test-runner/context.cjs.map +1 -0
  98. package/dist/data/test-runner/context.d.ts +40 -0
  99. package/dist/data/test-runner/context.d.ts.map +1 -0
  100. package/dist/data/test-runner/context.js +15 -0
  101. package/dist/data/test-runner/context.js.map +1 -0
  102. package/dist/data/test-runner/filterState.cjs +47 -0
  103. package/dist/data/test-runner/filterState.cjs.map +1 -0
  104. package/dist/data/test-runner/filterState.d.ts +14 -0
  105. package/dist/data/test-runner/filterState.d.ts.map +1 -0
  106. package/dist/data/test-runner/filterState.js +47 -0
  107. package/dist/data/test-runner/filterState.js.map +1 -0
  108. package/dist/data/test-runner/frameworkIcon.cjs +27 -0
  109. package/dist/data/test-runner/frameworkIcon.cjs.map +1 -0
  110. package/dist/data/test-runner/frameworkIcon.d.ts +3 -0
  111. package/dist/data/test-runner/frameworkIcon.d.ts.map +1 -0
  112. package/dist/data/test-runner/frameworkIcon.js +27 -0
  113. package/dist/data/test-runner/frameworkIcon.js.map +1 -0
  114. package/dist/data/test-runner/index.d.ts +14 -0
  115. package/dist/data/test-runner/index.d.ts.map +1 -0
  116. package/dist/data/test-runner/status.cjs +234 -0
  117. package/dist/data/test-runner/status.cjs.map +1 -0
  118. package/dist/data/test-runner/status.d.ts +52 -0
  119. package/dist/data/test-runner/status.d.ts.map +1 -0
  120. package/dist/data/test-runner/status.js +234 -0
  121. package/dist/data/test-runner/status.js.map +1 -0
  122. package/dist/data/test-runner/types.d.ts +148 -0
  123. package/dist/data/test-runner/types.d.ts.map +1 -0
  124. package/dist/data/version-info.cjs +26 -0
  125. package/dist/data/version-info.cjs.map +1 -0
  126. package/dist/data/version-info.d.ts +11 -0
  127. package/dist/data/version-info.d.ts.map +1 -0
  128. package/dist/data/version-info.js +26 -0
  129. package/dist/data/version-info.js.map +1 -0
  130. package/dist/data.cjs +49 -0
  131. package/dist/data.cjs.map +1 -1
  132. package/dist/data.d.ts +3 -0
  133. package/dist/data.d.ts.map +1 -1
  134. package/dist/data.js +50 -1
  135. package/dist/data.js.map +1 -1
  136. package/dist/index.cjs +59 -0
  137. package/dist/index.cjs.map +1 -1
  138. package/dist/index.js +60 -1
  139. package/dist/index.js.map +1 -1
  140. package/dist/overlay/DropdownMenu.cjs +57 -30
  141. package/dist/overlay/DropdownMenu.cjs.map +1 -1
  142. package/dist/overlay/DropdownMenu.d.ts.map +1 -1
  143. package/dist/overlay/DropdownMenu.js +57 -30
  144. package/dist/overlay/DropdownMenu.js.map +1 -1
  145. package/dist/overlay/Modal.cjs +62 -6
  146. package/dist/overlay/Modal.cjs.map +1 -1
  147. package/dist/overlay/Modal.d.ts +19 -1
  148. package/dist/overlay/Modal.d.ts.map +1 -1
  149. package/dist/overlay/Modal.js +62 -6
  150. package/dist/overlay/Modal.js.map +1 -1
  151. package/dist/rpc/CommandForm.cjs +2 -2
  152. package/dist/rpc/CommandForm.cjs.map +1 -1
  153. package/dist/rpc/CommandForm.d.ts.map +1 -1
  154. package/dist/rpc/CommandForm.js +3 -3
  155. package/dist/rpc/CommandForm.js.map +1 -1
  156. package/dist/rpc/formMetadata.cjs +5 -4
  157. package/dist/rpc/formMetadata.cjs.map +1 -1
  158. package/dist/rpc/formMetadata.d.ts.map +1 -1
  159. package/dist/rpc/formMetadata.js +6 -5
  160. package/dist/rpc/formMetadata.js.map +1 -1
  161. package/dist/rpc/types.cjs +4 -0
  162. package/dist/rpc/types.cjs.map +1 -1
  163. package/dist/rpc/types.d.ts +10 -0
  164. package/dist/rpc/types.d.ts.map +1 -1
  165. package/dist/rpc/types.js +5 -1
  166. package/dist/rpc/types.js.map +1 -1
  167. package/dist/styles.css +1 -1
  168. package/package.json +3 -1
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const Icon = require("../Icon.cjs");
6
+ const SplitPane = require("../../layout/SplitPane.cjs");
7
+ const utils = require("../../lib/utils.cjs");
8
+ const status = require("./status.cjs");
9
+ const adapter = require("./adapter.cjs");
10
+ const context = require("./context.cjs");
11
+ const TestTree = require("./TestTree.cjs");
12
+ const TestDetailPanel = require("./TestDetailPanel.cjs");
13
+ const TestRunSummary = require("./TestRunSummary.cjs");
14
+ const TestFilterBar = require("./TestFilterBar.cjs");
15
+ const UiBeaker = require("../../icons/components/UiBeaker.cjs");
16
+ function counts(tests) {
17
+ return tests.reduce(
18
+ (acc, t) => {
19
+ const s = status.sumNonTaskTests(t);
20
+ Object.keys(acc).forEach((k) => acc[k] += s[k]);
21
+ return acc;
22
+ },
23
+ { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
24
+ );
25
+ }
26
+ function toRegistry(adapters) {
27
+ if (adapters instanceof adapter.TestNodeAdapterRegistry) return adapters;
28
+ return adapter.createTestRunnerRegistry(adapters ?? []);
29
+ }
30
+ function TestRunner({
31
+ tests,
32
+ selected,
33
+ filters,
34
+ expandAll,
35
+ done,
36
+ runMeta,
37
+ status: status$1,
38
+ statusText,
39
+ now,
40
+ busy,
41
+ onSelect,
42
+ onFiltersChange,
43
+ onExpandAllChange,
44
+ onRerun,
45
+ onStop,
46
+ adapters,
47
+ headerSlot,
48
+ defaultSplit = 45,
49
+ startTime,
50
+ endTime,
51
+ className
52
+ }) {
53
+ const registry = react.useMemo(() => toRegistry(adapters), [adapters]);
54
+ const ctx = react.useMemo(
55
+ () => ({
56
+ tests,
57
+ selected,
58
+ runMeta,
59
+ status: status$1,
60
+ done,
61
+ now,
62
+ filters,
63
+ expandAll,
64
+ busy: busy ?? {},
65
+ onSelect,
66
+ onFiltersChange,
67
+ onExpandAllChange,
68
+ onRerun,
69
+ onStop,
70
+ adapters: registry
71
+ }),
72
+ [
73
+ tests,
74
+ selected,
75
+ runMeta,
76
+ status$1,
77
+ done,
78
+ now,
79
+ filters,
80
+ expandAll,
81
+ busy,
82
+ onSelect,
83
+ onFiltersChange,
84
+ onExpandAllChange,
85
+ onRerun,
86
+ onStop,
87
+ registry
88
+ ]
89
+ );
90
+ const frameworks = react.useMemo(() => status.collectFrameworks(tests), [tests]);
91
+ const filterCounts = react.useMemo(() => counts(tests), [tests]);
92
+ const hasContent = tests.length > 0;
93
+ return /* @__PURE__ */ jsxRuntime.jsx(context.TestRunnerProvider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex h-full min-h-0 flex-col bg-background", className), children: [
94
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
95
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
96
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
97
+ /* @__PURE__ */ jsxRuntime.jsxs("h1", { className: "inline-flex items-center gap-1.5 text-lg font-bold", children: [
98
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiBeaker.UiBeaker, className: "text-primary" }),
99
+ "Test Results"
100
+ ] }),
101
+ statusText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: statusText })
102
+ ] }),
103
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
104
+ headerSlot,
105
+ /* @__PURE__ */ jsxRuntime.jsx(
106
+ TestRunSummary.TestRunSummary,
107
+ {
108
+ tests,
109
+ startTime,
110
+ endTime,
111
+ done,
112
+ now,
113
+ runMeta
114
+ }
115
+ )
116
+ ] })
117
+ ] }),
118
+ hasContent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
119
+ TestFilterBar.TestFilterBar,
120
+ {
121
+ filters,
122
+ onChange: onFiltersChange,
123
+ counts: filterCounts,
124
+ frameworks
125
+ }
126
+ ) })
127
+ ] }),
128
+ /* @__PURE__ */ jsxRuntime.jsx(
129
+ SplitPane.SplitPane,
130
+ {
131
+ className: "min-h-0 flex-1",
132
+ defaultSplit,
133
+ minLeft: 25,
134
+ minRight: 30,
135
+ leftClass: "border-r-2 border-border bg-muted/30",
136
+ left: /* @__PURE__ */ jsxRuntime.jsx(TestTree.TestTree, {}),
137
+ right: /* @__PURE__ */ jsxRuntime.jsx(TestDetailPanel.TestDetailPanel, {})
138
+ }
139
+ )
140
+ ] }) });
141
+ }
142
+ exports.TestRunner = TestRunner;
143
+ //# sourceMappingURL=TestRunner.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestRunner.cjs","sources":["../../../src/data/test-runner/TestRunner.tsx"],"sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { SplitPane } from \"../../layout/SplitPane\";\nimport { cn } from \"../../lib/utils\";\nimport { UiBeaker } from \"../../icons\";\nimport type { RunMeta, SnapshotStatus, Test } from \"./types\";\nimport { collectFrameworks, sumNonTaskTests, type StatusCounts } from \"./status\";\nimport { type TestFilters } from \"./filterState\";\nimport {\n createTestRunnerRegistry,\n type TestNodeAdapter,\n TestNodeAdapterRegistry,\n} from \"./adapter\";\nimport { TestRunnerProvider, type TestRunnerContext } from \"./context\";\nimport { TestTree } from \"./TestTree\";\nimport { TestDetailPanel } from \"./TestDetailPanel\";\nimport { TestRunSummary } from \"./TestRunSummary\";\nimport { TestFilterBar } from \"./TestFilterBar\";\n\nexport type TestRunnerProps = {\n tests: Test[];\n selected: Test | null;\n filters: TestFilters;\n expandAll: boolean | null;\n done: boolean;\n runMeta?: RunMeta;\n status?: SnapshotStatus;\n /** Free-form status line shown next to the title (e.g. \"Running tests...\"). */\n statusText?: string;\n now?: number;\n busy?: { rerun?: boolean; stop?: boolean };\n\n onSelect: (node: Test | null) => void;\n onFiltersChange: (next: TestFilters) => void;\n onExpandAllChange: (next: boolean | null) => void;\n onRerun?: (node: Test) => void;\n onStop?: (node: Test) => void;\n\n /** Domain node adapters, or a pre-built registry. Defaults to default-only. */\n adapters?: TestNodeAdapter[] | TestNodeAdapterRegistry;\n /** Extra header content (e.g. export/download controls owned by the host). */\n headerSlot?: ReactNode;\n /** Initial left-pane width percentage. */\n defaultSplit?: number;\n startTime?: number | null;\n endTime?: number | null;\n className?: string;\n};\n\nfunction counts(tests: Test[]): StatusCounts {\n return tests.reduce(\n (acc, t) => {\n const s = sumNonTaskTests(t);\n (Object.keys(acc) as (keyof StatusCounts)[]).forEach((k) => (acc[k] += s[k]));\n return acc;\n },\n { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 },\n );\n}\n\nfunction toRegistry(\n adapters: TestNodeAdapter[] | TestNodeAdapterRegistry | undefined,\n): TestNodeAdapterRegistry {\n if (adapters instanceof TestNodeAdapterRegistry) return adapters;\n return createTestRunnerRegistry(adapters ?? []);\n}\n\n/**\n * Top-level, pure-presentational test runner: a summary/filter header over a\n * resizable tree + detail split. All data and handlers flow in via props and\n * are exposed to sub-components through a single `TestRunnerContext`. Domain\n * rendering is pluggable via node adapters; no fetch/SSE/routing lives here.\n */\nexport function TestRunner({\n tests,\n selected,\n filters,\n expandAll,\n done,\n runMeta,\n status,\n statusText,\n now,\n busy,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n adapters,\n headerSlot,\n defaultSplit = 45,\n startTime,\n endTime,\n className,\n}: TestRunnerProps) {\n const registry = useMemo(() => toRegistry(adapters), [adapters]);\n\n const ctx: TestRunnerContext = useMemo(\n () => ({\n tests,\n selected,\n runMeta,\n status,\n done,\n now,\n filters,\n expandAll,\n busy: busy ?? {},\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n adapters: registry,\n }),\n [\n tests,\n selected,\n runMeta,\n status,\n done,\n now,\n filters,\n expandAll,\n busy,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n registry,\n ],\n );\n\n const frameworks = useMemo(() => collectFrameworks(tests), [tests]);\n const filterCounts = useMemo(() => counts(tests), [tests]);\n const hasContent = tests.length > 0;\n\n return (\n <TestRunnerProvider value={ctx}>\n <div className={cn(\"flex h-full min-h-0 flex-col bg-background\", className)}>\n <div className=\"border-b border-border bg-card px-density-4 py-density-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <h1 className=\"inline-flex items-center gap-1.5 text-lg font-bold\">\n <Icon icon={UiBeaker} className=\"text-primary\" />\n Test Results\n </h1>\n {statusText && <span className=\"text-sm text-muted-foreground\">{statusText}</span>}\n </div>\n <div className=\"flex items-center gap-3\">\n {headerSlot}\n <TestRunSummary\n tests={tests}\n startTime={startTime}\n endTime={endTime}\n done={done}\n now={now}\n runMeta={runMeta}\n />\n </div>\n </div>\n {hasContent && (\n <div className=\"mt-2\">\n <TestFilterBar\n filters={filters}\n onChange={onFiltersChange}\n counts={filterCounts}\n frameworks={frameworks}\n />\n </div>\n )}\n </div>\n\n <SplitPane\n className=\"min-h-0 flex-1\"\n defaultSplit={defaultSplit}\n minLeft={25}\n minRight={30}\n leftClass=\"border-r-2 border-border bg-muted/30\"\n left={<TestTree />}\n right={<TestDetailPanel />}\n />\n </div>\n </TestRunnerProvider>\n );\n}\n"],"names":["sumNonTaskTests","TestNodeAdapterRegistry","createTestRunnerRegistry","status","useMemo","collectFrameworks","jsx","TestRunnerProvider","jsxs","cn","Icon","UiBeaker","TestRunSummary","TestFilterBar","SplitPane","TestTree","TestDetailPanel"],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { ReactNode } from 'react';
2
+ import { RunMeta, SnapshotStatus, Test } from './types';
3
+ import { TestFilters } from './filterState';
4
+ import { TestNodeAdapter, TestNodeAdapterRegistry } from './adapter';
5
+ export type TestRunnerProps = {
6
+ tests: Test[];
7
+ selected: Test | null;
8
+ filters: TestFilters;
9
+ expandAll: boolean | null;
10
+ done: boolean;
11
+ runMeta?: RunMeta;
12
+ status?: SnapshotStatus;
13
+ /** Free-form status line shown next to the title (e.g. "Running tests..."). */
14
+ statusText?: string;
15
+ now?: number;
16
+ busy?: {
17
+ rerun?: boolean;
18
+ stop?: boolean;
19
+ };
20
+ onSelect: (node: Test | null) => void;
21
+ onFiltersChange: (next: TestFilters) => void;
22
+ onExpandAllChange: (next: boolean | null) => void;
23
+ onRerun?: (node: Test) => void;
24
+ onStop?: (node: Test) => void;
25
+ /** Domain node adapters, or a pre-built registry. Defaults to default-only. */
26
+ adapters?: TestNodeAdapter[] | TestNodeAdapterRegistry;
27
+ /** Extra header content (e.g. export/download controls owned by the host). */
28
+ headerSlot?: ReactNode;
29
+ /** Initial left-pane width percentage. */
30
+ defaultSplit?: number;
31
+ startTime?: number | null;
32
+ endTime?: number | null;
33
+ className?: string;
34
+ };
35
+ /**
36
+ * Top-level, pure-presentational test runner: a summary/filter header over a
37
+ * resizable tree + detail split. All data and handlers flow in via props and
38
+ * are exposed to sub-components through a single `TestRunnerContext`. Domain
39
+ * rendering is pluggable via node adapters; no fetch/SSE/routing lives here.
40
+ */
41
+ export declare function TestRunner({ tests, selected, filters, expandAll, done, runMeta, status, statusText, now, busy, onSelect, onFiltersChange, onExpandAllChange, onRerun, onStop, adapters, headerSlot, defaultSplit, startTime, endTime, className, }: TestRunnerProps): import("react/jsx-runtime").JSX.Element;
42
+ //# sourceMappingURL=TestRunner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestRunner.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunner.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKhD,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE7D,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAEL,KAAK,eAAe,EACpB,uBAAuB,EACxB,MAAM,WAAW,CAAC;AAOnB,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAE3C,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IACtC,eAAe,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IAC7C,iBAAiB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC/B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAE9B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,eAAe,EAAE,GAAG,uBAAuB,CAAC;IACvD,8EAA8E;IAC9E,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAoBF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,OAAO,EACP,SAAS,EACT,IAAI,EACJ,OAAO,EACP,MAAM,EACN,UAAU,EACV,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,MAAM,EACN,QAAQ,EACR,UAAU,EACV,YAAiB,EACjB,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE,eAAe,2CA4FjB"}
@@ -0,0 +1,26 @@
1
+ import { TestNodeAdapter } from './adapter';
2
+ import { Test } from './types';
3
+ /** A completed, mixed-status run across go test, ginkgo and fixture frameworks. */
4
+ export declare const completedTests: Test[];
5
+ /** Same forest, but as an in-flight run: some running, some queued. */
6
+ export declare const runningTests: Test[];
7
+ /**
8
+ * A suite whose leaves carry very large payloads: a 6×4 deep object, a
9
+ * 500-row array, and an 800-line log — to stress JsonView, LogViewer, and the
10
+ * detail pane's independent scrolling.
11
+ */
12
+ export declare const largeDetailTests: Test[];
13
+ /**
14
+ * A very large forest (4 roots × depth 4 × breadth 4 ≈ several hundred nodes)
15
+ * whose every leaf carries the LargePayloads-sized detail (deep object +
16
+ * 500-row array + 800-line log). Used for the dialog stress test: a long
17
+ * scrolling tree on the left and very large detail payloads on the right.
18
+ */
19
+ export declare const largeTreeTests: Test[];
20
+ /**
21
+ * Demo adapter proving the registry extension point: nodes named "setup" get a
22
+ * custom detail body, an extra "Context" tab, and a node action — the seam that
23
+ * lets a downstream host drop its DetailPanel wrapper.
24
+ */
25
+ export declare const setupAdapter: TestNodeAdapter;
26
+ //# sourceMappingURL=TestRunner.fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestRunner.fixtures.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunner.fixtures.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAIpC,mFAAmF;AACnF,eAAO,MAAM,cAAc,EAAE,IAAI,EAmFhC,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,YAAY,EAAE,IAAI,EA+B9B,CAAC;AAsCF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,IAAI,EA0BlC,CAAC;AAwDF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,IAAI,EAEhC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,eA0C1B,CAAC"}
@@ -0,0 +1,143 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { Icon } from "../Icon.js";
4
+ import { SplitPane } from "../../layout/SplitPane.js";
5
+ import { cn } from "../../lib/utils.js";
6
+ import { collectFrameworks, sumNonTaskTests } from "./status.js";
7
+ import { TestNodeAdapterRegistry, createTestRunnerRegistry } from "./adapter.js";
8
+ import { TestRunnerProvider } from "./context.js";
9
+ import { TestTree } from "./TestTree.js";
10
+ import { TestDetailPanel } from "./TestDetailPanel.js";
11
+ import { TestRunSummary } from "./TestRunSummary.js";
12
+ import { TestFilterBar } from "./TestFilterBar.js";
13
+ import { UiBeaker } from "../../icons/components/UiBeaker.js";
14
+ function counts(tests) {
15
+ return tests.reduce(
16
+ (acc, t) => {
17
+ const s = sumNonTaskTests(t);
18
+ Object.keys(acc).forEach((k) => acc[k] += s[k]);
19
+ return acc;
20
+ },
21
+ { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
22
+ );
23
+ }
24
+ function toRegistry(adapters) {
25
+ if (adapters instanceof TestNodeAdapterRegistry) return adapters;
26
+ return createTestRunnerRegistry(adapters ?? []);
27
+ }
28
+ function TestRunner({
29
+ tests,
30
+ selected,
31
+ filters,
32
+ expandAll,
33
+ done,
34
+ runMeta,
35
+ status,
36
+ statusText,
37
+ now,
38
+ busy,
39
+ onSelect,
40
+ onFiltersChange,
41
+ onExpandAllChange,
42
+ onRerun,
43
+ onStop,
44
+ adapters,
45
+ headerSlot,
46
+ defaultSplit = 45,
47
+ startTime,
48
+ endTime,
49
+ className
50
+ }) {
51
+ const registry = useMemo(() => toRegistry(adapters), [adapters]);
52
+ const ctx = useMemo(
53
+ () => ({
54
+ tests,
55
+ selected,
56
+ runMeta,
57
+ status,
58
+ done,
59
+ now,
60
+ filters,
61
+ expandAll,
62
+ busy: busy ?? {},
63
+ onSelect,
64
+ onFiltersChange,
65
+ onExpandAllChange,
66
+ onRerun,
67
+ onStop,
68
+ adapters: registry
69
+ }),
70
+ [
71
+ tests,
72
+ selected,
73
+ runMeta,
74
+ status,
75
+ done,
76
+ now,
77
+ filters,
78
+ expandAll,
79
+ busy,
80
+ onSelect,
81
+ onFiltersChange,
82
+ onExpandAllChange,
83
+ onRerun,
84
+ onStop,
85
+ registry
86
+ ]
87
+ );
88
+ const frameworks = useMemo(() => collectFrameworks(tests), [tests]);
89
+ const filterCounts = useMemo(() => counts(tests), [tests]);
90
+ const hasContent = tests.length > 0;
91
+ return /* @__PURE__ */ jsx(TestRunnerProvider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col bg-background", className), children: [
92
+ /* @__PURE__ */ jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
93
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
94
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
95
+ /* @__PURE__ */ jsxs("h1", { className: "inline-flex items-center gap-1.5 text-lg font-bold", children: [
96
+ /* @__PURE__ */ jsx(Icon, { icon: UiBeaker, className: "text-primary" }),
97
+ "Test Results"
98
+ ] }),
99
+ statusText && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: statusText })
100
+ ] }),
101
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
102
+ headerSlot,
103
+ /* @__PURE__ */ jsx(
104
+ TestRunSummary,
105
+ {
106
+ tests,
107
+ startTime,
108
+ endTime,
109
+ done,
110
+ now,
111
+ runMeta
112
+ }
113
+ )
114
+ ] })
115
+ ] }),
116
+ hasContent && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
117
+ TestFilterBar,
118
+ {
119
+ filters,
120
+ onChange: onFiltersChange,
121
+ counts: filterCounts,
122
+ frameworks
123
+ }
124
+ ) })
125
+ ] }),
126
+ /* @__PURE__ */ jsx(
127
+ SplitPane,
128
+ {
129
+ className: "min-h-0 flex-1",
130
+ defaultSplit,
131
+ minLeft: 25,
132
+ minRight: 30,
133
+ leftClass: "border-r-2 border-border bg-muted/30",
134
+ left: /* @__PURE__ */ jsx(TestTree, {}),
135
+ right: /* @__PURE__ */ jsx(TestDetailPanel, {})
136
+ }
137
+ )
138
+ ] }) });
139
+ }
140
+ export {
141
+ TestRunner
142
+ };
143
+ //# sourceMappingURL=TestRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestRunner.js","sources":["../../../src/data/test-runner/TestRunner.tsx"],"sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { SplitPane } from \"../../layout/SplitPane\";\nimport { cn } from \"../../lib/utils\";\nimport { UiBeaker } from \"../../icons\";\nimport type { RunMeta, SnapshotStatus, Test } from \"./types\";\nimport { collectFrameworks, sumNonTaskTests, type StatusCounts } from \"./status\";\nimport { type TestFilters } from \"./filterState\";\nimport {\n createTestRunnerRegistry,\n type TestNodeAdapter,\n TestNodeAdapterRegistry,\n} from \"./adapter\";\nimport { TestRunnerProvider, type TestRunnerContext } from \"./context\";\nimport { TestTree } from \"./TestTree\";\nimport { TestDetailPanel } from \"./TestDetailPanel\";\nimport { TestRunSummary } from \"./TestRunSummary\";\nimport { TestFilterBar } from \"./TestFilterBar\";\n\nexport type TestRunnerProps = {\n tests: Test[];\n selected: Test | null;\n filters: TestFilters;\n expandAll: boolean | null;\n done: boolean;\n runMeta?: RunMeta;\n status?: SnapshotStatus;\n /** Free-form status line shown next to the title (e.g. \"Running tests...\"). */\n statusText?: string;\n now?: number;\n busy?: { rerun?: boolean; stop?: boolean };\n\n onSelect: (node: Test | null) => void;\n onFiltersChange: (next: TestFilters) => void;\n onExpandAllChange: (next: boolean | null) => void;\n onRerun?: (node: Test) => void;\n onStop?: (node: Test) => void;\n\n /** Domain node adapters, or a pre-built registry. Defaults to default-only. */\n adapters?: TestNodeAdapter[] | TestNodeAdapterRegistry;\n /** Extra header content (e.g. export/download controls owned by the host). */\n headerSlot?: ReactNode;\n /** Initial left-pane width percentage. */\n defaultSplit?: number;\n startTime?: number | null;\n endTime?: number | null;\n className?: string;\n};\n\nfunction counts(tests: Test[]): StatusCounts {\n return tests.reduce(\n (acc, t) => {\n const s = sumNonTaskTests(t);\n (Object.keys(acc) as (keyof StatusCounts)[]).forEach((k) => (acc[k] += s[k]));\n return acc;\n },\n { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 },\n );\n}\n\nfunction toRegistry(\n adapters: TestNodeAdapter[] | TestNodeAdapterRegistry | undefined,\n): TestNodeAdapterRegistry {\n if (adapters instanceof TestNodeAdapterRegistry) return adapters;\n return createTestRunnerRegistry(adapters ?? []);\n}\n\n/**\n * Top-level, pure-presentational test runner: a summary/filter header over a\n * resizable tree + detail split. All data and handlers flow in via props and\n * are exposed to sub-components through a single `TestRunnerContext`. Domain\n * rendering is pluggable via node adapters; no fetch/SSE/routing lives here.\n */\nexport function TestRunner({\n tests,\n selected,\n filters,\n expandAll,\n done,\n runMeta,\n status,\n statusText,\n now,\n busy,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n adapters,\n headerSlot,\n defaultSplit = 45,\n startTime,\n endTime,\n className,\n}: TestRunnerProps) {\n const registry = useMemo(() => toRegistry(adapters), [adapters]);\n\n const ctx: TestRunnerContext = useMemo(\n () => ({\n tests,\n selected,\n runMeta,\n status,\n done,\n now,\n filters,\n expandAll,\n busy: busy ?? {},\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n adapters: registry,\n }),\n [\n tests,\n selected,\n runMeta,\n status,\n done,\n now,\n filters,\n expandAll,\n busy,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n registry,\n ],\n );\n\n const frameworks = useMemo(() => collectFrameworks(tests), [tests]);\n const filterCounts = useMemo(() => counts(tests), [tests]);\n const hasContent = tests.length > 0;\n\n return (\n <TestRunnerProvider value={ctx}>\n <div className={cn(\"flex h-full min-h-0 flex-col bg-background\", className)}>\n <div className=\"border-b border-border bg-card px-density-4 py-density-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <h1 className=\"inline-flex items-center gap-1.5 text-lg font-bold\">\n <Icon icon={UiBeaker} className=\"text-primary\" />\n Test Results\n </h1>\n {statusText && <span className=\"text-sm text-muted-foreground\">{statusText}</span>}\n </div>\n <div className=\"flex items-center gap-3\">\n {headerSlot}\n <TestRunSummary\n tests={tests}\n startTime={startTime}\n endTime={endTime}\n done={done}\n now={now}\n runMeta={runMeta}\n />\n </div>\n </div>\n {hasContent && (\n <div className=\"mt-2\">\n <TestFilterBar\n filters={filters}\n onChange={onFiltersChange}\n counts={filterCounts}\n frameworks={frameworks}\n />\n </div>\n )}\n </div>\n\n <SplitPane\n className=\"min-h-0 flex-1\"\n defaultSplit={defaultSplit}\n minLeft={25}\n minRight={30}\n leftClass=\"border-r-2 border-border bg-muted/30\"\n left={<TestTree />}\n right={<TestDetailPanel />}\n />\n </div>\n </TestRunnerProvider>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const Tree = require("../Tree.cjs");
5
+ const utils = require("../../lib/utils.cjs");
6
+ const context = require("./context.cjs");
7
+ const status = require("./status.cjs");
8
+ const TestTreeNode = require("./TestTreeNode.cjs");
9
+ const nodeKey = /* @__PURE__ */ (() => {
10
+ const cache = /* @__PURE__ */ new WeakMap();
11
+ let counter = 0;
12
+ return (node) => {
13
+ if (node.route_path) return node.route_path;
14
+ let key = cache.get(node);
15
+ if (!key) {
16
+ key = `tn-${counter++}-${node.name}`;
17
+ cache.set(node, key);
18
+ }
19
+ return key;
20
+ };
21
+ })();
22
+ function TestTree({ className }) {
23
+ const runner = context.useTestRunner();
24
+ return /* @__PURE__ */ jsxRuntime.jsx(
25
+ Tree.Tree,
26
+ {
27
+ className: utils.cn("min-h-0", className),
28
+ roots: runner.tests,
29
+ getKey: nodeKey,
30
+ getChildren: (node) => node.children,
31
+ selected: runner.selected,
32
+ onSelect: (node) => runner.onSelect(node === runner.selected ? null : node),
33
+ expandAll: runner.expandAll,
34
+ onExpandAllChange: runner.onExpandAllChange,
35
+ defaultOpen: (node, depth) => depth < 1 || status.hasFailed(node),
36
+ getSearchText: (node) => `${node.name} ${node.framework ?? ""}`,
37
+ renderRow: ({ node, selected }) => /* @__PURE__ */ jsxRuntime.jsx(TestTreeNode.TestTreeNode, { node, selected })
38
+ }
39
+ );
40
+ }
41
+ exports.TestTree = TestTree;
42
+ //# sourceMappingURL=TestTree.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestTree.cjs","sources":["../../../src/data/test-runner/TestTree.tsx"],"sourcesContent":["import { Tree } from \"../Tree\";\nimport { cn } from \"../../lib/utils\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport { hasFailed } from \"./status\";\nimport { TestTreeNode } from \"./TestTreeNode\";\n\nconst nodeKey = (() => {\n // Tests carry no guaranteed id; derive a stable per-render key from route_path\n // when present, else the node's name path. A WeakMap keeps keys stable across\n // re-renders for the same object identity.\n const cache = new WeakMap<Test, string>();\n let counter = 0;\n return (node: Test): string => {\n if (node.route_path) return node.route_path;\n let key = cache.get(node);\n if (!key) {\n key = `tn-${counter++}-${node.name}`;\n cache.set(node, key);\n }\n return key;\n };\n})();\n\n/**\n * The left pane: the test forest rendered through the clicky `Tree`. Selection,\n * handlers and adapters all come from the runner context; failing branches and\n * the top level open by default.\n */\nexport function TestTree({ className }: { className?: string }) {\n const runner = useTestRunner();\n return (\n <Tree<Test>\n className={cn(\"min-h-0\", className)}\n roots={runner.tests}\n getKey={nodeKey}\n getChildren={(node) => node.children}\n selected={runner.selected}\n onSelect={(node) => runner.onSelect(node === runner.selected ? null : node)}\n expandAll={runner.expandAll}\n onExpandAllChange={runner.onExpandAllChange}\n defaultOpen={(node, depth) => depth < 1 || hasFailed(node)}\n getSearchText={(node) => `${node.name} ${node.framework ?? \"\"}`}\n renderRow={({ node, selected }) => <TestTreeNode node={node} selected={selected} />}\n />\n );\n}\n"],"names":["useTestRunner","jsx","Tree","cn","hasFailed","TestTreeNode"],"mappings":";;;;;;;;AAOA,MAAM,UAAW,uBAAM;AAIrB,QAAM,4BAAY,QAAA;AAClB,MAAI,UAAU;AACd,SAAO,CAAC,SAAuB;AAC7B,QAAI,KAAK,WAAY,QAAO,KAAK;AACjC,QAAI,MAAM,MAAM,IAAI,IAAI;AACxB,QAAI,CAAC,KAAK;AACR,YAAM,MAAM,SAAS,IAAI,KAAK,IAAI;AAClC,YAAM,IAAI,MAAM,GAAG;AAAA,IACrB;AACA,WAAO;AAAA,EACT;AACF,GAAA;AAOO,SAAS,SAAS,EAAE,aAAqC;AAC9D,QAAM,SAASA,QAAAA,cAAA;AACf,SACEC,2BAAAA;AAAAA,IAACC,KAAAA;AAAAA,IAAA;AAAA,MACC,WAAWC,MAAAA,GAAG,WAAW,SAAS;AAAA,MAClC,OAAO,OAAO;AAAA,MACd,QAAQ;AAAA,MACR,aAAa,CAAC,SAAS,KAAK;AAAA,MAC5B,UAAU,OAAO;AAAA,MACjB,UAAU,CAAC,SAAS,OAAO,SAAS,SAAS,OAAO,WAAW,OAAO,IAAI;AAAA,MAC1E,WAAW,OAAO;AAAA,MAClB,mBAAmB,OAAO;AAAA,MAC1B,aAAa,CAAC,MAAM,UAAU,QAAQ,KAAKC,OAAAA,UAAU,IAAI;AAAA,MACzD,eAAe,CAAC,SAAS,GAAG,KAAK,IAAI,IAAI,KAAK,aAAa,EAAE;AAAA,MAC7D,WAAW,CAAC,EAAE,MAAM,eAAeH,2BAAAA,IAACI,aAAAA,cAAA,EAAa,MAAY,SAAA,CAAoB;AAAA,IAAA;AAAA,EAAA;AAGvF;;"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The left pane: the test forest rendered through the clicky `Tree`. Selection,
3
+ * handlers and adapters all come from the runner context; failing branches and
4
+ * the top level open by default.
5
+ */
6
+ export declare function TestTree({ className }: {
7
+ className?: string;
8
+ }): import("react/jsx-runtime").JSX.Element;
9
+ //# sourceMappingURL=TestTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestTree.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestTree.tsx"],"names":[],"mappings":"AAwBA;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAiB7D"}
@@ -0,0 +1,42 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Tree } from "../Tree.js";
3
+ import { cn } from "../../lib/utils.js";
4
+ import { useTestRunner } from "./context.js";
5
+ import { hasFailed } from "./status.js";
6
+ import { TestTreeNode } from "./TestTreeNode.js";
7
+ const nodeKey = /* @__PURE__ */ (() => {
8
+ const cache = /* @__PURE__ */ new WeakMap();
9
+ let counter = 0;
10
+ return (node) => {
11
+ if (node.route_path) return node.route_path;
12
+ let key = cache.get(node);
13
+ if (!key) {
14
+ key = `tn-${counter++}-${node.name}`;
15
+ cache.set(node, key);
16
+ }
17
+ return key;
18
+ };
19
+ })();
20
+ function TestTree({ className }) {
21
+ const runner = useTestRunner();
22
+ return /* @__PURE__ */ jsx(
23
+ Tree,
24
+ {
25
+ className: cn("min-h-0", className),
26
+ roots: runner.tests,
27
+ getKey: nodeKey,
28
+ getChildren: (node) => node.children,
29
+ selected: runner.selected,
30
+ onSelect: (node) => runner.onSelect(node === runner.selected ? null : node),
31
+ expandAll: runner.expandAll,
32
+ onExpandAllChange: runner.onExpandAllChange,
33
+ defaultOpen: (node, depth) => depth < 1 || hasFailed(node),
34
+ getSearchText: (node) => `${node.name} ${node.framework ?? ""}`,
35
+ renderRow: ({ node, selected }) => /* @__PURE__ */ jsx(TestTreeNode, { node, selected })
36
+ }
37
+ );
38
+ }
39
+ export {
40
+ TestTree
41
+ };
42
+ //# sourceMappingURL=TestTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestTree.js","sources":["../../../src/data/test-runner/TestTree.tsx"],"sourcesContent":["import { Tree } from \"../Tree\";\nimport { cn } from \"../../lib/utils\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport { hasFailed } from \"./status\";\nimport { TestTreeNode } from \"./TestTreeNode\";\n\nconst nodeKey = (() => {\n // Tests carry no guaranteed id; derive a stable per-render key from route_path\n // when present, else the node's name path. A WeakMap keeps keys stable across\n // re-renders for the same object identity.\n const cache = new WeakMap<Test, string>();\n let counter = 0;\n return (node: Test): string => {\n if (node.route_path) return node.route_path;\n let key = cache.get(node);\n if (!key) {\n key = `tn-${counter++}-${node.name}`;\n cache.set(node, key);\n }\n return key;\n };\n})();\n\n/**\n * The left pane: the test forest rendered through the clicky `Tree`. Selection,\n * handlers and adapters all come from the runner context; failing branches and\n * the top level open by default.\n */\nexport function TestTree({ className }: { className?: string }) {\n const runner = useTestRunner();\n return (\n <Tree<Test>\n className={cn(\"min-h-0\", className)}\n roots={runner.tests}\n getKey={nodeKey}\n getChildren={(node) => node.children}\n selected={runner.selected}\n onSelect={(node) => runner.onSelect(node === runner.selected ? null : node)}\n expandAll={runner.expandAll}\n onExpandAllChange={runner.onExpandAllChange}\n defaultOpen={(node, depth) => depth < 1 || hasFailed(node)}\n getSearchText={(node) => `${node.name} ${node.framework ?? \"\"}`}\n renderRow={({ node, selected }) => <TestTreeNode node={node} selected={selected} />}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;AAOA,MAAM,UAAW,uBAAM;AAIrB,QAAM,4BAAY,QAAA;AAClB,MAAI,UAAU;AACd,SAAO,CAAC,SAAuB;AAC7B,QAAI,KAAK,WAAY,QAAO,KAAK;AACjC,QAAI,MAAM,MAAM,IAAI,IAAI;AACxB,QAAI,CAAC,KAAK;AACR,YAAM,MAAM,SAAS,IAAI,KAAK,IAAI;AAClC,YAAM,IAAI,MAAM,GAAG;AAAA,IACrB;AACA,WAAO;AAAA,EACT;AACF,GAAA;AAOO,SAAS,SAAS,EAAE,aAAqC;AAC9D,QAAM,SAAS,cAAA;AACf,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,GAAG,WAAW,SAAS;AAAA,MAClC,OAAO,OAAO;AAAA,MACd,QAAQ;AAAA,MACR,aAAa,CAAC,SAAS,KAAK;AAAA,MAC5B,UAAU,OAAO;AAAA,MACjB,UAAU,CAAC,SAAS,OAAO,SAAS,SAAS,OAAO,WAAW,OAAO,IAAI;AAAA,MAC1E,WAAW,OAAO;AAAA,MAClB,mBAAmB,OAAO;AAAA,MAC1B,aAAa,CAAC,MAAM,UAAU,QAAQ,KAAK,UAAU,IAAI;AAAA,MACzD,eAAe,CAAC,SAAS,GAAG,KAAK,IAAI,IAAI,KAAK,aAAa,EAAE;AAAA,MAC7D,WAAW,CAAC,EAAE,MAAM,eAAe,oBAAC,cAAA,EAAa,MAAY,SAAA,CAAoB;AAAA,IAAA;AAAA,EAAA;AAGvF;"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const Icon = require("../Icon.cjs");
5
+ const Badge = require("../Badge.cjs");
6
+ const IconButton = require("../../components/IconButton.cjs");
7
+ const utils = require("../../lib/utils.cjs");
8
+ const context = require("./context.cjs");
9
+ const status = require("./status.cjs");
10
+ const frameworkIcon = require("./frameworkIcon.cjs");
11
+ const UiStop = require("../../icons/components/UiStop.cjs");
12
+ const UiRefresh = require("../../icons/components/UiRefresh.cjs");
13
+ const NAME_TONE = {
14
+ failed: "text-red-600 dark:text-red-400",
15
+ timedout: "text-amber-600 dark:text-amber-400",
16
+ warned: "text-amber-600 dark:text-amber-400",
17
+ running: "text-blue-600 dark:text-blue-400",
18
+ skipped: "text-yellow-700 dark:text-yellow-500",
19
+ pending: "text-muted-foreground",
20
+ passed: "text-foreground"
21
+ };
22
+ function CountBadges({ node }) {
23
+ const s = status.sum(node);
24
+ if (s.total === 0) return null;
25
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex shrink-0 items-center gap-1", children: [
26
+ s.passed > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "success", variant: "solid", size: "xxs", count: s.passed }),
27
+ s.failed > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "danger", variant: "solid", size: "xxs", count: s.failed }),
28
+ s.timedout > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "warning", variant: "solid", size: "xxs", count: s.timedout }),
29
+ s.skipped > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "warning", variant: "soft", size: "xxs", count: s.skipped }),
30
+ s.running > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "info", variant: "solid", size: "xxs", count: s.running }),
31
+ s.pending > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge.Badge, { tone: "neutral", variant: "soft", size: "xxs", count: s.pending })
32
+ ] });
33
+ }
34
+ function TestTreeNode({ node, selected }) {
35
+ var _a, _b, _c;
36
+ const runner = context.useTestRunner();
37
+ const hasChildren = (((_a = node.children) == null ? void 0 : _a.length) ?? 0) > 0;
38
+ const status$1 = status.testStatus(node);
39
+ const fwIcon = frameworkIcon.frameworkIcon(node.framework);
40
+ const fwAdapter = runner.adapters.resolveFrameworkIcon(node, runner);
41
+ const fwNode = (_b = fwAdapter == null ? void 0 : fwAdapter.renderFrameworkIcon) == null ? void 0 : _b.call(fwAdapter, { node, runner });
42
+ const leadingAdapter = runner.adapters.resolveRowLeading(node, runner);
43
+ const dur = node.duration || (hasChildren ? status.totalDuration(node) : 0);
44
+ const canRerun = !!runner.onRerun && node.framework !== "task";
45
+ const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;
46
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
47
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: status.statusIconFor(node), className: utils.cn("shrink-0", node.running && "animate-spin") }),
48
+ fwNode != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex shrink-0 items-center text-sm opacity-60", children: fwNode }) : fwIcon && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: fwIcon, className: "shrink-0 text-sm opacity-60" }),
49
+ (_c = leadingAdapter == null ? void 0 : leadingAdapter.renderRowLeading) == null ? void 0 : _c.call(leadingAdapter, { node, runner }),
50
+ /* @__PURE__ */ jsxRuntime.jsx(
51
+ "span",
52
+ {
53
+ className: utils.cn(
54
+ "truncate",
55
+ selected ? "font-semibold" : "font-medium",
56
+ status$1 && NAME_TONE[status$1] || "text-foreground"
57
+ ),
58
+ children: status.humanizeName(node.name, node.framework)
59
+ }
60
+ ),
61
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
62
+ node.progress && node.progress.total > 0 && !node.passed && !node.failed && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex shrink-0 items-center gap-1 text-xs text-blue-600 dark:text-blue-400", children: [
63
+ node.progress.phase && /* @__PURE__ */ jsxRuntime.jsx("span", { children: node.progress.phase }),
64
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums", children: [
65
+ node.progress.done,
66
+ "/",
67
+ node.progress.total
68
+ ] })
69
+ ] }),
70
+ dur > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: status.formatTestDuration(dur) }),
71
+ hasChildren && /* @__PURE__ */ jsxRuntime.jsx(CountBadges, { node }),
72
+ canStop && /* @__PURE__ */ jsxRuntime.jsx(
73
+ IconButton.IconButton,
74
+ {
75
+ icon: UiStop.UiStop,
76
+ label: "Stop",
77
+ disabled: runner.busy.stop,
78
+ onClick: (e) => {
79
+ e.stopPropagation();
80
+ runner.onStop(node);
81
+ }
82
+ }
83
+ ),
84
+ canRerun && /* @__PURE__ */ jsxRuntime.jsx(
85
+ IconButton.IconButton,
86
+ {
87
+ icon: UiRefresh.UiRefresh,
88
+ label: "Rerun",
89
+ disabled: runner.busy.rerun,
90
+ onClick: (e) => {
91
+ e.stopPropagation();
92
+ runner.onRerun(node);
93
+ }
94
+ }
95
+ )
96
+ ] });
97
+ }
98
+ exports.TestTreeNode = TestTreeNode;
99
+ //# sourceMappingURL=TestTreeNode.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestTreeNode.cjs","sources":["../../../src/data/test-runner/TestTreeNode.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { Badge } from \"../Badge\";\nimport { IconButton } from \"../../components/IconButton\";\nimport { UiRefresh, UiStop } from \"../../icons\";\nimport { cn } from \"../../lib/utils\";\nimport type { Test } from \"./types\";\nimport { useTestRunner } from \"./context\";\nimport {\n formatTestDuration,\n humanizeName,\n statusIconFor,\n sum,\n testStatus,\n totalDuration,\n} from \"./status\";\nimport { frameworkIcon } from \"./frameworkIcon\";\n\nconst NAME_TONE: Record<string, string> = {\n failed: \"text-red-600 dark:text-red-400\",\n timedout: \"text-amber-600 dark:text-amber-400\",\n warned: \"text-amber-600 dark:text-amber-400\",\n running: \"text-blue-600 dark:text-blue-400\",\n skipped: \"text-yellow-700 dark:text-yellow-500\",\n pending: \"text-muted-foreground\",\n passed: \"text-foreground\",\n};\n\n/** Per-status roll-up count badges shown on a container row. */\nfunction CountBadges({ node }: { node: Test }) {\n const s = sum(node);\n if (s.total === 0) return null;\n return (\n <span className=\"flex shrink-0 items-center gap-1\">\n {s.passed > 0 && <Badge tone=\"success\" variant=\"solid\" size=\"xxs\" count={s.passed} />}\n {s.failed > 0 && <Badge tone=\"danger\" variant=\"solid\" size=\"xxs\" count={s.failed} />}\n {s.timedout > 0 && <Badge tone=\"warning\" variant=\"solid\" size=\"xxs\" count={s.timedout} />}\n {s.skipped > 0 && <Badge tone=\"warning\" variant=\"soft\" size=\"xxs\" count={s.skipped} />}\n {s.running > 0 && <Badge tone=\"info\" variant=\"solid\" size=\"xxs\" count={s.running} />}\n {s.pending > 0 && <Badge tone=\"neutral\" variant=\"soft\" size=\"xxs\" count={s.pending} />}\n </span>\n );\n}\n\n/**\n * Renders one test tree row: status icon, optional adapter decoration + framework\n * icon, status-colored name, duration, per-status count badges, and (when the\n * runner exposes the handlers) rerun/stop buttons. Designed as the `renderRow`\n * for the clicky `Tree`.\n */\nexport function TestTreeNode({ node, selected }: { node: Test; selected: boolean }) {\n const runner = useTestRunner();\n const hasChildren = (node.children?.length ?? 0) > 0;\n const status = testStatus(node);\n const fwIcon = frameworkIcon(node.framework);\n const fwAdapter = runner.adapters.resolveFrameworkIcon(node, runner);\n const fwNode = fwAdapter?.renderFrameworkIcon?.({ node, runner });\n const leadingAdapter = runner.adapters.resolveRowLeading(node, runner);\n const dur = node.duration || (hasChildren ? totalDuration(node) : 0);\n const canRerun = !!runner.onRerun && node.framework !== \"task\";\n const canStop = !!runner.onStop && !!node.can_stop && !!node.task_id;\n\n return (\n <>\n <Icon icon={statusIconFor(node)} className={cn(\"shrink-0\", node.running && \"animate-spin\")} />\n\n {fwNode != null ? (\n <span className=\"flex shrink-0 items-center text-sm opacity-60\">{fwNode}</span>\n ) : (\n fwIcon && <Icon icon={fwIcon} className=\"shrink-0 text-sm opacity-60\" />\n )}\n\n {leadingAdapter?.renderRowLeading?.({ node, runner })}\n\n <span\n className={cn(\n \"truncate\",\n selected ? \"font-semibold\" : \"font-medium\",\n (status && NAME_TONE[status]) || \"text-foreground\",\n )}\n >\n {humanizeName(node.name, node.framework)}\n </span>\n\n <span className=\"flex-1\" />\n\n {node.progress && node.progress.total > 0 && !node.passed && !node.failed && (\n <span className=\"flex shrink-0 items-center gap-1 text-xs text-blue-600 dark:text-blue-400\">\n {node.progress.phase && <span>{node.progress.phase}</span>}\n <span className=\"tabular-nums\">\n {node.progress.done}/{node.progress.total}\n </span>\n </span>\n )}\n\n {dur > 0 && (\n <span className=\"shrink-0 text-xs text-muted-foreground\">{formatTestDuration(dur)}</span>\n )}\n\n {hasChildren && <CountBadges node={node} />}\n\n {canStop && (\n <IconButton\n icon={UiStop}\n label=\"Stop\"\n disabled={runner.busy.stop}\n onClick={(e) => {\n e.stopPropagation();\n runner.onStop!(node);\n }}\n />\n )}\n\n {canRerun && (\n <IconButton\n icon={UiRefresh}\n label=\"Rerun\"\n disabled={runner.busy.rerun}\n onClick={(e) => {\n e.stopPropagation();\n runner.onRerun!(node);\n }}\n />\n )}\n </>\n );\n}\n"],"names":["sum","jsxs","jsx","Badge","useTestRunner","status","testStatus","frameworkIcon","totalDuration","Fragment","Icon","statusIconFor","cn","humanizeName","formatTestDuration","IconButton","UiStop","UiRefresh"],"mappings":"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"}