@flanksource/clicky-ui 0.3.30 → 0.3.32
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/comments/CommentCard.cjs +156 -273
- package/dist/comments/CommentCard.cjs.map +1 -1
- package/dist/comments/CommentCard.d.ts +5 -1
- package/dist/comments/CommentCard.d.ts.map +1 -1
- package/dist/comments/CommentCard.js +151 -268
- package/dist/comments/CommentCard.js.map +1 -1
- package/dist/comments/CommentCardParts.cjs +115 -0
- package/dist/comments/CommentCardParts.cjs.map +1 -0
- package/dist/comments/CommentCardParts.d.ts +21 -0
- package/dist/comments/CommentCardParts.d.ts.map +1 -0
- package/dist/comments/CommentCardParts.js +115 -0
- package/dist/comments/CommentCardParts.js.map +1 -0
- package/dist/comments/CommentProvider.cjs +3 -0
- package/dist/comments/CommentProvider.cjs.map +1 -1
- package/dist/comments/CommentProvider.d.ts +1 -1
- package/dist/comments/CommentProvider.d.ts.map +1 -1
- package/dist/comments/CommentProvider.js +3 -0
- package/dist/comments/CommentProvider.js.map +1 -1
- package/dist/comments/CommentSidePanel.cjs +55 -22
- package/dist/comments/CommentSidePanel.cjs.map +1 -1
- package/dist/comments/CommentSidePanel.d.ts +3 -1
- package/dist/comments/CommentSidePanel.d.ts.map +1 -1
- package/dist/comments/CommentSidePanel.js +56 -23
- package/dist/comments/CommentSidePanel.js.map +1 -1
- package/dist/comments/CommentThread.cjs +2 -0
- package/dist/comments/CommentThread.cjs.map +1 -1
- package/dist/comments/CommentThread.d.ts +2 -1
- package/dist/comments/CommentThread.d.ts.map +1 -1
- package/dist/comments/CommentThread.js +2 -0
- package/dist/comments/CommentThread.js.map +1 -1
- package/dist/comments/CommentThreadList.cjs +52 -34
- package/dist/comments/CommentThreadList.cjs.map +1 -1
- package/dist/comments/CommentThreadList.d.ts +5 -2
- package/dist/comments/CommentThreadList.d.ts.map +1 -1
- package/dist/comments/CommentThreadList.js +52 -34
- package/dist/comments/CommentThreadList.js.map +1 -1
- package/dist/comments/comment-types.cjs +16 -4
- package/dist/comments/comment-types.cjs.map +1 -1
- package/dist/comments/comment-types.d.ts +11 -1
- package/dist/comments/comment-types.d.ts.map +1 -1
- package/dist/comments/comment-types.js +16 -4
- package/dist/comments/comment-types.js.map +1 -1
- package/dist/comments/comment-utils.cjs +31 -1
- package/dist/comments/comment-utils.cjs.map +1 -1
- package/dist/comments/comment-utils.d.ts +9 -2
- package/dist/comments/comment-utils.d.ts.map +1 -1
- package/dist/comments/comment-utils.js +31 -1
- package/dist/comments/comment-utils.js.map +1 -1
- package/dist/comments.cjs +3 -0
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js +4 -1
- package/dist/components/ComboboxMenu.cjs +4 -1
- package/dist/components/ComboboxMenu.cjs.map +1 -1
- package/dist/components/ComboboxMenu.d.ts.map +1 -1
- package/dist/components/ComboboxMenu.js +5 -2
- package/dist/components/ComboboxMenu.js.map +1 -1
- package/dist/components/ComboboxTags.cjs +5 -1
- package/dist/components/ComboboxTags.cjs.map +1 -1
- package/dist/components/ComboboxTags.d.ts.map +1 -1
- package/dist/components/ComboboxTags.js +6 -2
- package/dist/components/ComboboxTags.js.map +1 -1
- package/dist/components/ConnectionLoggingPolicy.cjs +24 -35
- package/dist/components/ConnectionLoggingPolicy.cjs.map +1 -1
- package/dist/components/ConnectionLoggingPolicy.d.ts.map +1 -1
- package/dist/components/ConnectionLoggingPolicy.js +24 -35
- package/dist/components/ConnectionLoggingPolicy.js.map +1 -1
- package/dist/components/MultiSelect.cjs.map +1 -1
- package/dist/components/MultiSelect.d.ts +7 -0
- package/dist/components/MultiSelect.d.ts.map +1 -1
- package/dist/components/MultiSelect.js.map +1 -1
- package/dist/components/filter-bar-multi.cjs +10 -1
- package/dist/components/filter-bar-multi.cjs.map +1 -1
- package/dist/components/filter-bar-multi.d.ts.map +1 -1
- package/dist/components/filter-bar-multi.js +11 -2
- package/dist/components/filter-bar-multi.js.map +1 -1
- package/dist/data/ai/SessionViewer.model.cjs +23 -8
- package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.model.d.ts +8 -0
- package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.model.js +23 -8
- package/dist/data/ai/SessionViewer.model.js.map +1 -1
- package/dist/data/ai/SessionViewer.rows.cjs +9 -1
- package/dist/data/ai/SessionViewer.rows.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.rows.js +9 -1
- package/dist/data/ai/SessionViewer.rows.js.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs +24 -2
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.cjs.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.d.ts.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.js +25 -3
- package/dist/data/ai/SpecRuntimeEditor/VerifySection.js.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/summaries.cjs.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/summaries.d.ts.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor/summaries.js.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor.model.cjs +4 -0
- package/dist/data/ai/SpecRuntimeEditor.model.cjs.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor.model.d.ts +4 -0
- package/dist/data/ai/SpecRuntimeEditor.model.d.ts.map +1 -1
- package/dist/data/ai/SpecRuntimeEditor.model.js +4 -0
- package/dist/data/ai/SpecRuntimeEditor.model.js.map +1 -1
- package/dist/data/test-runner/TestRunner.cjs +6 -4
- package/dist/data/test-runner/TestRunner.cjs.map +1 -1
- package/dist/data/test-runner/TestRunner.d.ts +10 -1
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunner.js +6 -4
- package/dist/data/test-runner/TestRunner.js.map +1 -1
- package/dist/data/test-runner/status.cjs +1 -1
- package/dist/data/test-runner/status.cjs.map +1 -1
- package/dist/data/test-runner/status.js +1 -1
- package/dist/data/test-runner/status.js.map +1 -1
- package/dist/data/test-runner/types.d.ts +6 -0
- package/dist/data/test-runner/types.d.ts.map +1 -1
- package/dist/data/verification/VerificationResults.cjs +99 -0
- package/dist/data/verification/VerificationResults.cjs.map +1 -0
- package/dist/data/verification/VerificationResults.d.ts +27 -0
- package/dist/data/verification/VerificationResults.d.ts.map +1 -0
- package/dist/data/verification/VerificationResults.js +99 -0
- package/dist/data/verification/VerificationResults.js.map +1 -0
- package/dist/data/verification/adapterViews.cjs +116 -0
- package/dist/data/verification/adapterViews.cjs.map +1 -0
- package/dist/data/verification/adapterViews.d.ts +13 -0
- package/dist/data/verification/adapterViews.d.ts.map +1 -0
- package/dist/data/verification/adapterViews.js +116 -0
- package/dist/data/verification/adapterViews.js.map +1 -0
- package/dist/data/verification/adapters.cjs +26 -0
- package/dist/data/verification/adapters.cjs.map +1 -0
- package/dist/data/verification/adapters.d.ts +6 -0
- package/dist/data/verification/adapters.d.ts.map +1 -0
- package/dist/data/verification/adapters.js +26 -0
- package/dist/data/verification/adapters.js.map +1 -0
- package/dist/data/verification/index.d.ts +5 -0
- package/dist/data/verification/index.d.ts.map +1 -0
- package/dist/data/verification/verify-report.cjs +58 -0
- package/dist/data/verification/verify-report.cjs.map +1 -0
- package/dist/data/verification/verify-report.d.ts +96 -0
- package/dist/data/verification/verify-report.d.ts.map +1 -0
- package/dist/data/verification/verify-report.js +58 -0
- package/dist/data/verification/verify-report.js.map +1 -0
- package/dist/data/verification/verifyReportTests.cjs +84 -0
- package/dist/data/verification/verifyReportTests.cjs.map +1 -0
- package/dist/data/verification/verifyReportTests.d.ts +13 -0
- package/dist/data/verification/verifyReportTests.d.ts.map +1 -0
- package/dist/data/verification/verifyReportTests.js +84 -0
- package/dist/data/verification/verifyReportTests.js.map +1 -0
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +10 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +1 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +10 -0
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +13 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1 +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,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtC,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;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,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;AASF;;;;;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,SAAS,EACT,WAAW,EACX,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,MAAM,EACN,QAAQ,EACR,KAAK,EACL,WAAkB,EAClB,UAAU,EACV,UAAkB,EAClB,YAAiB,EACjB,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE,eAAe,+
|
|
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;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC;IAClB,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,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtC,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;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,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;AASF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,SAAS,EACT,IAAI,EACJ,OAAO,EACP,MAAM,EACN,UAAU,EACV,GAAG,EACH,IAAI,EACJ,SAAS,EACT,WAAW,EACX,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,OAAO,EACP,MAAM,EACN,QAAQ,EACR,KAAK,EACL,WAAkB,EAClB,UAAU,EACV,UAAkB,EAClB,YAAiB,EACjB,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE,eAAe,+BAyGjB"}
|
|
@@ -17,6 +17,7 @@ function toRegistry(adapters) {
|
|
|
17
17
|
}
|
|
18
18
|
function TestRunner({
|
|
19
19
|
tests,
|
|
20
|
+
allTests,
|
|
20
21
|
selected,
|
|
21
22
|
filters,
|
|
22
23
|
expandAll,
|
|
@@ -84,9 +85,10 @@ function TestRunner({
|
|
|
84
85
|
registry
|
|
85
86
|
]
|
|
86
87
|
);
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
88
|
+
const summaryTests = allTests ?? tests;
|
|
89
|
+
const frameworks = useMemo(() => collectFrameworks(summaryTests), [summaryTests]);
|
|
90
|
+
const filterCounts = useMemo(() => aggregateStatusCounts(summaryTests), [summaryTests]);
|
|
91
|
+
const hasContent = summaryTests.length > 0;
|
|
90
92
|
return /* @__PURE__ */ jsx(TestRunnerProvider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col bg-background", className), children: [
|
|
91
93
|
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
|
|
92
94
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
@@ -102,7 +104,7 @@ function TestRunner({
|
|
|
102
104
|
showSummary && /* @__PURE__ */ jsx(
|
|
103
105
|
TestRunSummary,
|
|
104
106
|
{
|
|
105
|
-
tests,
|
|
107
|
+
tests: summaryTests,
|
|
106
108
|
startTime,
|
|
107
109
|
endTime,
|
|
108
110
|
done,
|
|
@@ -1 +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 { aggregateStatusCounts, collectFrameworks } 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 /** Controlled active detail-tab id; omit to let the detail pane own tab state. */\n activeTab?: string;\n /** Called with the picked tab id when `activeTab` is controlled. */\n onTabChange?: (tabId: string) => void;\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 /**\n * Header title. Defaults (when omitted) to the \"Test Results\" beaker heading.\n * Pass `null` to render no title — e.g. a host that owns the title in its own\n * dialog/page header and embeds the runner below it.\n */\n title?: ReactNode;\n /**\n * Render the built-in run summary (counts + progress) in the header. Defaults\n * to true; pass false when the host shows the summary itself (e.g. in a dialog\n * header) to avoid duplicating it.\n */\n showSummary?: boolean;\n /** Extra header content (e.g. export/download controls owned by the host). */\n headerSlot?: ReactNode;\n /**\n * Suppresses the entire built-in header bar (title, summary, filter row).\n * Defaults to false; pass true when a host renders its own combined header\n * (e.g. a dialog header that already embeds `TestRunSummary` and filters) so\n * the runner doesn't render a second bordered bar underneath it.\n */\n hideHeader?: boolean;\n /** Initial left-pane width percentage. */\n defaultSplit?: number;\n startTime?: number | null;\n endTime?: number | null;\n className?: string;\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 activeTab,\n onTabChange,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n adapters,\n title,\n showSummary = true,\n headerSlot,\n hideHeader = false,\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 activeTab,\n onTabChange,\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 activeTab,\n onTabChange,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n registry,\n ],\n );\n\n const frameworks = useMemo(() => collectFrameworks(
|
|
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 { aggregateStatusCounts, collectFrameworks } 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 /**\n * The full, unfiltered forest driving the filter bar's pills/counts and the\n * header summary — falls back to `tests` when omitted (every existing\n * caller pre-filters `tests` itself). Pass this explicitly when `tests` is a\n * filtered subset of a larger report, so toggling a status filter (e.g.\n * excluding the only failed node) can't make its own pill — or the whole\n * filter bar, once every pill's count reads zero — disappear.\n */\n allTests?: 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 /** Controlled active detail-tab id; omit to let the detail pane own tab state. */\n activeTab?: string;\n /** Called with the picked tab id when `activeTab` is controlled. */\n onTabChange?: (tabId: string) => void;\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 /**\n * Header title. Defaults (when omitted) to the \"Test Results\" beaker heading.\n * Pass `null` to render no title — e.g. a host that owns the title in its own\n * dialog/page header and embeds the runner below it.\n */\n title?: ReactNode;\n /**\n * Render the built-in run summary (counts + progress) in the header. Defaults\n * to true; pass false when the host shows the summary itself (e.g. in a dialog\n * header) to avoid duplicating it.\n */\n showSummary?: boolean;\n /** Extra header content (e.g. export/download controls owned by the host). */\n headerSlot?: ReactNode;\n /**\n * Suppresses the entire built-in header bar (title, summary, filter row).\n * Defaults to false; pass true when a host renders its own combined header\n * (e.g. a dialog header that already embeds `TestRunSummary` and filters) so\n * the runner doesn't render a second bordered bar underneath it.\n */\n hideHeader?: boolean;\n /** Initial left-pane width percentage. */\n defaultSplit?: number;\n startTime?: number | null;\n endTime?: number | null;\n className?: string;\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 allTests,\n selected,\n filters,\n expandAll,\n done,\n runMeta,\n status,\n statusText,\n now,\n busy,\n activeTab,\n onTabChange,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n adapters,\n title,\n showSummary = true,\n headerSlot,\n hideHeader = false,\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 activeTab,\n onTabChange,\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 activeTab,\n onTabChange,\n onSelect,\n onFiltersChange,\n onExpandAllChange,\n onRerun,\n onStop,\n registry,\n ],\n );\n\n const summaryTests = allTests ?? tests;\n const frameworks = useMemo(() => collectFrameworks(summaryTests), [summaryTests]);\n const filterCounts = useMemo(() => aggregateStatusCounts(summaryTests), [summaryTests]);\n const hasContent = summaryTests.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 {!hideHeader && (\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 {title === undefined ? (\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 ) : (\n title\n )}\n {statusText && <span className=\"text-sm text-muted-foreground\">{statusText}</span>}\n </div>\n <div className=\"flex items-center gap-3\">\n {headerSlot}\n {showSummary && (\n <TestRunSummary\n tests={summaryTests}\n startTime={startTime}\n endTime={endTime}\n done={done}\n now={now}\n runMeta={runMeta}\n />\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\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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"}
|
|
@@ -36,7 +36,7 @@ const countsFromSummary = (summary) => ({
|
|
|
36
36
|
skipped: summary.Skipped,
|
|
37
37
|
pending: summary.Pending || 0,
|
|
38
38
|
running: summary.Running || 0,
|
|
39
|
-
timedout: 0
|
|
39
|
+
timedout: summary.Timedout || 0
|
|
40
40
|
});
|
|
41
41
|
const countsFromLeaf = (t) => {
|
|
42
42
|
const isTimedOut = !!t.timed_out;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.cjs","sources":["../../../src/data/test-runner/status.ts"],"sourcesContent":["// Pure status/roll-up helpers for the test runner — ported from Gavel's\n// utils.ts (Tests-only subset). No JSX here. The two status mappers\n// (statusIconFor / statusToneFor) replace Gavel's raw-Tailwind statusIcon /\n// statusColor with clicky's icon set and semantic Badge tones.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport type { BadgeTone } from \"../Badge\";\nimport {\n UiClass,\n UiError,\n UiHourglass,\n UiLoader,\n UiPass,\n UiPause,\n UiWarningTriangle,\n} from \"../../icons\";\nimport type { Test, TestSummary } from \"./types\";\nimport { matchesFilterState, type FilterState } from \"./filterState\";\n\n// --- roll-up counts ------------------------------------------------------\n\n/**\n * Tallied verdict breakdown a `Test` subtree rolls up to. `warned` is amber and\n * orthogonal to a real failure — a warned-only leaf counts in total + warned,\n * never in failed.\n */\nexport type StatusCounts = {\n total: number;\n passed: number;\n failed: number;\n warned: number;\n skipped: number;\n pending: number;\n running: number;\n timedout: number;\n};\n\nconst emptyCounts = (): StatusCounts => ({\n total: 0,\n passed: 0,\n failed: 0,\n warned: 0,\n skipped: 0,\n pending: 0,\n running: 0,\n timedout: 0,\n});\n\nconst addCounts = (r: StatusCounts, s: StatusCounts) => {\n r.total += s.total;\n r.passed += s.passed;\n r.failed += s.failed;\n r.warned += s.warned;\n r.skipped += s.skipped;\n r.pending += s.pending;\n r.running += s.running;\n r.timedout += s.timedout;\n};\n\nconst countsFromSummary = (summary: TestSummary): StatusCounts => ({\n total: summary.Total,\n passed: summary.Passed,\n failed: summary.Failed,\n warned: summary.Warned || 0,\n skipped: summary.Skipped,\n pending: summary.Pending || 0,\n running: summary.Running || 0,\n timedout: 0,\n});\n\nconst countsFromLeaf = (t: Test): StatusCounts => {\n const isTimedOut = !!t.timed_out;\n const counted =\n isTimedOut || t.passed || t.failed || t.warned || t.skipped || t.pending || t.running;\n return {\n total: counted ? 1 : 0,\n passed: !isTimedOut && t.passed ? 1 : 0,\n failed: !isTimedOut && t.failed ? 1 : 0,\n warned: !isTimedOut && t.warned ? 1 : 0,\n skipped: !isTimedOut && t.skipped ? 1 : 0,\n pending: !isTimedOut && t.pending ? 1 : 0,\n running: !isTimedOut && t.running ? 1 : 0,\n timedout: isTimedOut ? 1 : 0,\n };\n};\n\nexport function sum(t: Test): StatusCounts {\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sum(c));\n return r;\n}\n\n/** Roll up a forest's non-task status counts. Shared by `TestRunner`'s filter\n * bar and `TestRunSummary`'s chips so both read the same tally. */\nexport function aggregateStatusCounts(tests: Test[]): StatusCounts {\n const r = emptyCounts();\n for (const t of tests) addCounts(r, sumNonTaskTests(t));\n return r;\n}\n\n/** Ordered status key → label list, driving both the filter bar's pills and a\n * host's status filter option list, so the seven rows aren't hand-duplicated. */\nexport const STATUS_LABELS: { key: keyof StatusCounts; label: string }[] = [\n { key: \"failed\", label: \"Failed\" },\n { key: \"warned\", label: \"Warned\" },\n { key: \"timedout\", label: \"Timed out\" },\n { key: \"passed\", label: \"Passed\" },\n { key: \"skipped\", label: \"Skipped\" },\n { key: \"running\", label: \"Running\" },\n { key: \"pending\", label: \"Queued\" },\n];\n\n/** Like {@link sum} but excludes `task`-framework pseudo-nodes from the tally. */\nexport function sumNonTaskTests(t: Test): StatusCounts {\n if (t.framework === \"task\") {\n const r = emptyCounts();\n for (const c of t.children || []) addCounts(r, sumNonTaskTests(c));\n return r;\n }\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sumNonTaskTests(c));\n return r;\n}\n\nexport function hasFailed(t: Test): boolean {\n if (t.failed) return true;\n return (t.children || []).some(hasFailed);\n}\n\nexport function hasPending(t: Test): boolean {\n if (t.pending) return true;\n return (t.children || []).some(hasPending);\n}\n\nfunction hasTimedOutDescendant(t: Test): boolean {\n if (t.timed_out) return true;\n return (t.children || []).some(hasTimedOutDescendant);\n}\n\nexport function totalDuration(t: Test): number {\n if (t.duration && t.duration > 0) return t.duration;\n let d = 0;\n for (const c of t.children || []) d += totalDuration(c);\n return d;\n}\n\n// --- status classification ----------------------------------------------\n\nexport type TestStatus =\n | \"passed\"\n | \"failed\"\n | \"warned\"\n | \"skipped\"\n | \"pending\"\n | \"running\"\n | \"timedout\";\n\n/** Resolve a leaf node's single status, or null for a status-less container. */\nexport function testStatus(t: Test): TestStatus | null {\n if (t.timed_out) return \"timedout\";\n if (t.running) return \"running\";\n if (t.pending) return \"pending\";\n if (t.failed) return \"failed\";\n if (t.warned) return \"warned\";\n if (t.skipped) return \"skipped\";\n if (t.passed) return \"passed\";\n return null;\n}\n\n/** Pick the clicky icon for a node, rolling up container state. */\nexport function statusIconFor(t: Test): StaticIconComponent {\n if (t.timed_out) return UiHourglass;\n if (t.running) return UiLoader;\n if (t.pending) return UiPause;\n if (t.failed) return UiError;\n if (t.warned) return UiWarningTriangle;\n if (t.skipped) return UiPause;\n if (t.passed) return UiPass;\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return UiHourglass;\n if (s.failed > 0) return UiError;\n if (s.warned > 0) return UiWarningTriangle;\n if (s.running > 0) return UiLoader;\n if (s.pending > 0) return UiPause;\n return UiPass;\n }\n return UiClass;\n}\n\n/** Map a node's status to a semantic Badge tone, rolling up container state. */\nexport function statusToneFor(t: Test): BadgeTone {\n if (t.timed_out) return \"warning\";\n if (t.running) return \"info\";\n if (t.pending) return \"neutral\";\n if (t.failed) return \"danger\";\n if (t.warned) return \"warning\";\n if (t.skipped) return \"warning\";\n if (t.passed) return \"success\";\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return \"warning\";\n if (s.failed > 0) return \"danger\";\n if (s.warned > 0) return \"warning\";\n if (s.running > 0) return \"info\";\n if (s.pending > 0) return \"neutral\";\n return \"success\";\n }\n return \"neutral\";\n}\n\n// --- formatting ----------------------------------------------------------\n\n/**\n * Format a nanosecond duration as \"123ms\" or \"1.2s\". Empty for non-positive.\n * Named distinctly from clicky's ms/s `formatDuration` (lib/format) since the\n * test-runner wire format is nanoseconds.\n */\nexport function formatTestDuration(ns: number): string {\n if (ns <= 0) return \"\";\n const ms = ns / 1e6;\n if (ms < 1000) return `${ms.toFixed(0)}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction compactWithUnit(value: number, unit: string): string {\n const rounded =\n Math.abs(value) >= 10 ? Math.round(value).toString() : value.toFixed(1).replace(/\\.0$/, \"\");\n return `${rounded}${unit}`;\n}\n\n/** Compact integer counts: <1000 raw, then \"k\", then \"M\". */\nexport function formatCount(n: number): string {\n const abs = Math.abs(n);\n if (abs < 1000) return String(n);\n if (abs < 1_000_000) return compactWithUnit(n / 1000, \"k\");\n return compactWithUnit(n / 1_000_000, \"M\");\n}\n\n/** Strip Go's `Test` prefix and split CamelCase / subtest names into words. */\nexport function humanizeName(name: string, framework?: string): string {\n if (!name) return \"\";\n if (framework !== \"go test\") return name;\n if (name.endsWith(\"/\")) return name;\n return name\n .split(\"/\")\n .map((p, i) => {\n if (i === 0) {\n return p.replace(/^Test/, \"\").replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n }\n return p.replace(/_/g, \" \");\n })\n .join(\" / \");\n}\n\n// --- tree transforms -----------------------------------------------------\n\n/** Unique non-task frameworks present in the forest, sorted. */\nexport function collectFrameworks(tests: Test[]): string[] {\n const set = new Set<string>();\n const walk = (t: Test) => {\n if (t.framework && t.framework !== \"task\") set.add(t.framework);\n (t.children || []).forEach(walk);\n };\n tests.forEach(walk);\n return Array.from(set).sort();\n}\n\nfunction joinChainNames(parent: string | undefined, child: string | undefined): string {\n const p = (parent ?? \"\").trim();\n const c = (child ?? \"\").trim();\n if (!p) return c;\n if (!c) return p;\n return `${p} > ${c}`;\n}\n\nfunction isCollapsibleContainer(t: Test): boolean {\n return !(t.passed || t.failed || t.skipped || t.pending);\n}\n\n// Re-deriving a collapsed/filtered node invalidates any cached summary; drop it\n// so callers re-roll-up from the surviving children.\nfunction withoutSummary(t: Test): Omit<Test, \"summary\"> & { summary?: never } {\n const { summary: _summary, ...rest } = t;\n return rest;\n}\n\nfunction collapseNode(t: Test): Test {\n const children = t.children ?? [];\n if (children.length === 0) return t;\n\n const collapsedChildren = children.map(collapseNode);\n\n const onlyChild = collapsedChildren.length === 1 ? collapsedChildren[0] : undefined;\n if (onlyChild && isCollapsibleContainer(t)) {\n return { ...withoutSummary(onlyChild), name: joinChainNames(t.name, onlyChild.name) };\n }\n\n return { ...t, children: collapsedChildren };\n}\n\n/**\n * Compress any status-less container with exactly one child into that child,\n * joining names with \" > \". Recursive, so chains collapse into a single row.\n */\nexport function collapseSingleChildChains(tests: Test[]): Test[] {\n return tests.map(collapseNode);\n}\n\nfunction matchesLeaf(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): boolean {\n const s = testStatus(t);\n if (s === null) return false;\n return matchesFilterState(s, statusFilter) && matchesFilterState(t.framework, frameworkFilter);\n}\n\nfunction filterNode(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test | null {\n if (t.children && t.children.length > 0) {\n const filtered = t.children\n .map((c) => filterNode(c, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n if (filtered.length === 0) return null;\n return { ...withoutSummary(t), children: filtered };\n }\n return matchesLeaf(t, statusFilter, frameworkFilter) ? t : null;\n}\n\n/** Prune leaves not matching the status/framework filters; drop empty branches. */\nexport function filterTests(\n tests: Test[],\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test[] {\n if (statusFilter.size === 0 && frameworkFilter.size === 0) return tests;\n return tests\n .map((t) => filterNode(t, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n}\n"],"names":["r","UiHourglass","UiLoader","UiPause","UiError","UiWarningTriangle","UiPass","UiClass","matchesFilterState"],"mappings":";;;;;;;;;;AAqCA,MAAM,cAAc,OAAqB;AAAA,EACvC,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AACZ;AAEA,MAAM,YAAY,CAAC,GAAiB,MAAoB;AACtD,IAAE,SAAS,EAAE;AACb,IAAE,UAAU,EAAE;AACd,IAAE,UAAU,EAAE;AACd,IAAE,UAAU,EAAE;AACd,IAAE,WAAW,EAAE;AACf,IAAE,WAAW,EAAE;AACf,IAAE,WAAW,EAAE;AACf,IAAE,YAAY,EAAE;AAClB;AAEA,MAAM,oBAAoB,CAAC,aAAwC;AAAA,EACjE,OAAO,QAAQ;AAAA,EACf,QAAQ,QAAQ;AAAA,EAChB,QAAQ,QAAQ;AAAA,EAChB,QAAQ,QAAQ,UAAU;AAAA,EAC1B,SAAS,QAAQ;AAAA,EACjB,SAAS,QAAQ,WAAW;AAAA,EAC5B,SAAS,QAAQ,WAAW;AAAA,EAC5B,UAAU;AACZ;AAEA,MAAM,iBAAiB,CAAC,MAA0B;AAChD,QAAM,aAAa,CAAC,CAAC,EAAE;AACvB,QAAM,UACJ,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE;AAChF,SAAO;AAAA,IACL,OAAO,UAAU,IAAI;AAAA,IACrB,QAAQ,CAAC,cAAc,EAAE,SAAS,IAAI;AAAA,IACtC,QAAQ,CAAC,cAAc,EAAE,SAAS,IAAI;AAAA,IACtC,QAAQ,CAAC,cAAc,EAAE,SAAS,IAAI;AAAA,IACtC,SAAS,CAAC,cAAc,EAAE,UAAU,IAAI;AAAA,IACxC,SAAS,CAAC,cAAc,EAAE,UAAU,IAAI;AAAA,IACxC,SAAS,CAAC,cAAc,EAAE,UAAU,IAAI;AAAA,IACxC,UAAU,aAAa,IAAI;AAAA,EAAA;AAE/B;AAEO,SAAS,IAAI,GAAuB;AACzC,MAAI,EAAE,QAAS,QAAO,kBAAkB,EAAE,OAAO;AACjD,MAAI,CAAC,EAAE,YAAY,EAAE,SAAS,WAAW,EAAG,QAAO,eAAe,CAAC;AACnE,QAAM,IAAI,YAAA;AACV,aAAW,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAAC;AAC/C,SAAO;AACT;AAIO,SAAS,sBAAsB,OAA6B;AACjE,QAAM,IAAI,YAAA;AACV,aAAW,KAAK,MAAO,WAAU,GAAG,gBAAgB,CAAC,CAAC;AACtD,SAAO;AACT;AAIO,MAAM,gBAA8D;AAAA,EACzE,EAAE,KAAK,UAAU,OAAO,SAAA;AAAA,EACxB,EAAE,KAAK,UAAU,OAAO,SAAA;AAAA,EACxB,EAAE,KAAK,YAAY,OAAO,YAAA;AAAA,EAC1B,EAAE,KAAK,UAAU,OAAO,SAAA;AAAA,EACxB,EAAE,KAAK,WAAW,OAAO,UAAA;AAAA,EACzB,EAAE,KAAK,WAAW,OAAO,UAAA;AAAA,EACzB,EAAE,KAAK,WAAW,OAAO,SAAA;AAC3B;AAGO,SAAS,gBAAgB,GAAuB;AACrD,MAAI,EAAE,cAAc,QAAQ;AAC1B,UAAMA,KAAI,YAAA;AACV,eAAW,KAAK,EAAE,YAAY,CAAA,EAAI,WAAUA,IAAG,gBAAgB,CAAC,CAAC;AACjE,WAAOA;AAAAA,EACT;AACA,MAAI,EAAE,QAAS,QAAO,kBAAkB,EAAE,OAAO;AACjD,MAAI,CAAC,EAAE,YAAY,EAAE,SAAS,WAAW,EAAG,QAAO,eAAe,CAAC;AACnE,QAAM,IAAI,YAAA;AACV,aAAW,KAAK,EAAE,oBAAoB,GAAG,gBAAgB,CAAC,CAAC;AAC3D,SAAO;AACT;AAEO,SAAS,UAAU,GAAkB;AAC1C,MAAI,EAAE,OAAQ,QAAO;AACrB,UAAQ,EAAE,YAAY,CAAA,GAAI,KAAK,SAAS;AAC1C;AAEO,SAAS,WAAW,GAAkB;AAC3C,MAAI,EAAE,QAAS,QAAO;AACtB,UAAQ,EAAE,YAAY,CAAA,GAAI,KAAK,UAAU;AAC3C;AAEA,SAAS,sBAAsB,GAAkB;AAC/C,MAAI,EAAE,UAAW,QAAO;AACxB,UAAQ,EAAE,YAAY,CAAA,GAAI,KAAK,qBAAqB;AACtD;AAEO,SAAS,cAAc,GAAiB;AAC7C,MAAI,EAAE,YAAY,EAAE,WAAW,UAAU,EAAE;AAC3C,MAAI,IAAI;AACR,aAAW,KAAK,EAAE,YAAY,CAAA,EAAI,MAAK,cAAc,CAAC;AACtD,SAAO;AACT;AAcO,SAAS,WAAW,GAA4B;AACrD,MAAI,EAAE,UAAW,QAAO;AACxB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,SAAO;AACT;AAGO,SAAS,cAAc,GAA8B;AAC1D,MAAI,EAAE,UAAW,QAAOC,YAAAA;AACxB,MAAI,EAAE,QAAS,QAAOC,SAAAA;AACtB,MAAI,EAAE,QAAS,QAAOC,QAAAA;AACtB,MAAI,EAAE,OAAQ,QAAOC,QAAAA;AACrB,MAAI,EAAE,OAAQ,QAAOC,kBAAAA;AACrB,MAAI,EAAE,QAAS,QAAOF,QAAAA;AACtB,MAAI,EAAE,OAAQ,QAAOG,OAAAA;AACrB,MAAI,EAAE,YAAY,EAAE,SAAS,SAAS,GAAG;AACvC,UAAM,IAAI,IAAI,CAAC;AACf,QAAI,sBAAsB,CAAC,EAAG,QAAOL,YAAAA;AACrC,QAAI,EAAE,SAAS,EAAG,QAAOG,QAAAA;AACzB,QAAI,EAAE,SAAS,EAAG,QAAOC,kBAAAA;AACzB,QAAI,EAAE,UAAU,EAAG,QAAOH,SAAAA;AAC1B,QAAI,EAAE,UAAU,EAAG,QAAOC,QAAAA;AAC1B,WAAOG,OAAAA;AAAAA,EACT;AACA,SAAOC,QAAAA;AACT;AAGO,SAAS,cAAc,GAAoB;AAChD,MAAI,EAAE,UAAW,QAAO;AACxB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,YAAY,EAAE,SAAS,SAAS,GAAG;AACvC,UAAM,IAAI,IAAI,CAAC;AACf,QAAI,sBAAsB,CAAC,EAAG,QAAO;AACrC,QAAI,EAAE,SAAS,EAAG,QAAO;AACzB,QAAI,EAAE,SAAS,EAAG,QAAO;AACzB,QAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,QAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASO,SAAS,mBAAmB,IAAoB;AACrD,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,KAAK,KAAK;AAChB,MAAI,KAAK,IAAM,QAAO,GAAG,GAAG,QAAQ,CAAC,CAAC;AACtC,SAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AAClC;AAEA,SAAS,gBAAgB,OAAe,MAAsB;AAC5D,QAAM,UACJ,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,EAAE,SAAA,IAAa,MAAM,QAAQ,CAAC,EAAE,QAAQ,QAAQ,EAAE;AAC5F,SAAO,GAAG,OAAO,GAAG,IAAI;AAC1B;AAGO,SAAS,YAAY,GAAmB;AAC7C,QAAM,MAAM,KAAK,IAAI,CAAC;AACtB,MAAI,MAAM,IAAM,QAAO,OAAO,CAAC;AAC/B,MAAI,MAAM,IAAW,QAAO,gBAAgB,IAAI,KAAM,GAAG;AACzD,SAAO,gBAAgB,IAAI,KAAW,GAAG;AAC3C;AAGO,SAAS,aAAa,MAAc,WAA4B;AACrE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,cAAc,UAAW,QAAO;AACpC,MAAI,KAAK,SAAS,GAAG,EAAG,QAAO;AAC/B,SAAO,KACJ,MAAM,GAAG,EACT,IAAI,CAAC,GAAG,MAAM;AACb,QAAI,MAAM,GAAG;AACX,aAAO,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,sBAAsB,OAAO;AAAA,IACrE;AACA,WAAO,EAAE,QAAQ,MAAM,GAAG;AAAA,EAC5B,CAAC,EACA,KAAK,KAAK;AACf;AAKO,SAAS,kBAAkB,OAAyB;AACzD,QAAM,0BAAU,IAAA;AAChB,QAAM,OAAO,CAAC,MAAY;AACxB,QAAI,EAAE,aAAa,EAAE,cAAc,OAAQ,KAAI,IAAI,EAAE,SAAS;AAC9D,KAAC,EAAE,YAAY,IAAI,QAAQ,IAAI;AAAA,EACjC;AACA,QAAM,QAAQ,IAAI;AAClB,SAAO,MAAM,KAAK,GAAG,EAAE,KAAA;AACzB;AAEA,SAAS,eAAe,QAA4B,OAAmC;AACrF,QAAM,KAAK,UAAU,IAAI,KAAA;AACzB,QAAM,KAAK,SAAS,IAAI,KAAA;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,GAAG,CAAC,MAAM,CAAC;AACpB;AAEA,SAAS,uBAAuB,GAAkB;AAChD,SAAO,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE;AAClD;AAIA,SAAS,eAAe,GAAsD;AAC5E,QAAM,EAAE,SAAS,UAAU,GAAG,SAAS;AACvC,SAAO;AACT;AAEA,SAAS,aAAa,GAAe;AACnC,QAAM,WAAW,EAAE,YAAY,CAAA;AAC/B,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAM,oBAAoB,SAAS,IAAI,YAAY;AAEnD,QAAM,YAAY,kBAAkB,WAAW,IAAI,kBAAkB,CAAC,IAAI;AAC1E,MAAI,aAAa,uBAAuB,CAAC,GAAG;AAC1C,WAAO,EAAE,GAAG,eAAe,SAAS,GAAG,MAAM,eAAe,EAAE,MAAM,UAAU,IAAI,EAAA;AAAA,EACpF;AAEA,SAAO,EAAE,GAAG,GAAG,UAAU,kBAAA;AAC3B;AAMO,SAAS,0BAA0B,OAAuB;AAC/D,SAAO,MAAM,IAAI,YAAY;AAC/B;AAEA,SAAS,YACP,GACA,cACA,iBACS;AACT,QAAM,IAAI,WAAW,CAAC;AACtB,MAAI,MAAM,KAAM,QAAO;AACvB,SAAOC,YAAAA,mBAAmB,GAAG,YAAY,KAAKA,YAAAA,mBAAmB,EAAE,WAAW,eAAe;AAC/F;AAEA,SAAS,WACP,GACA,cACA,iBACa;AACb,MAAI,EAAE,YAAY,EAAE,SAAS,SAAS,GAAG;AACvC,UAAM,WAAW,EAAE,SAChB,IAAI,CAAC,MAAM,WAAW,GAAG,cAAc,eAAe,CAAC,EACvD,OAAO,OAAO;AACjB,QAAI,SAAS,WAAW,EAAG,QAAO;AAClC,WAAO,EAAE,GAAG,eAAe,CAAC,GAAG,UAAU,SAAA;AAAA,EAC3C;AACA,SAAO,YAAY,GAAG,cAAc,eAAe,IAAI,IAAI;AAC7D;AAGO,SAAS,YACd,OACA,cACA,iBACQ;AACR,MAAI,aAAa,SAAS,KAAK,gBAAgB,SAAS,EAAG,QAAO;AAClE,SAAO,MACJ,IAAI,CAAC,MAAM,WAAW,GAAG,cAAc,eAAe,CAAC,EACvD,OAAO,OAAO;AACnB;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"status.cjs","sources":["../../../src/data/test-runner/status.ts"],"sourcesContent":["// Pure status/roll-up helpers for the test runner — ported from Gavel's\n// utils.ts (Tests-only subset). No JSX here. The two status mappers\n// (statusIconFor / statusToneFor) replace Gavel's raw-Tailwind statusIcon /\n// statusColor with clicky's icon set and semantic Badge tones.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport type { BadgeTone } from \"../Badge\";\nimport {\n UiClass,\n UiError,\n UiHourglass,\n UiLoader,\n UiPass,\n UiPause,\n UiWarningTriangle,\n} from \"../../icons\";\nimport type { Test, TestSummary } from \"./types\";\nimport { matchesFilterState, type FilterState } from \"./filterState\";\n\n// --- roll-up counts ------------------------------------------------------\n\n/**\n * Tallied verdict breakdown a `Test` subtree rolls up to. `warned` is amber and\n * orthogonal to a real failure — a warned-only leaf counts in total + warned,\n * never in failed.\n */\nexport type StatusCounts = {\n total: number;\n passed: number;\n failed: number;\n warned: number;\n skipped: number;\n pending: number;\n running: number;\n timedout: number;\n};\n\nconst emptyCounts = (): StatusCounts => ({\n total: 0,\n passed: 0,\n failed: 0,\n warned: 0,\n skipped: 0,\n pending: 0,\n running: 0,\n timedout: 0,\n});\n\nconst addCounts = (r: StatusCounts, s: StatusCounts) => {\n r.total += s.total;\n r.passed += s.passed;\n r.failed += s.failed;\n r.warned += s.warned;\n r.skipped += s.skipped;\n r.pending += s.pending;\n r.running += s.running;\n r.timedout += s.timedout;\n};\n\nconst countsFromSummary = (summary: TestSummary): StatusCounts => ({\n total: summary.Total,\n passed: summary.Passed,\n failed: summary.Failed,\n warned: summary.Warned || 0,\n skipped: summary.Skipped,\n pending: summary.Pending || 0,\n running: summary.Running || 0,\n timedout: summary.Timedout || 0,\n});\n\nconst countsFromLeaf = (t: Test): StatusCounts => {\n const isTimedOut = !!t.timed_out;\n const counted =\n isTimedOut || t.passed || t.failed || t.warned || t.skipped || t.pending || t.running;\n return {\n total: counted ? 1 : 0,\n passed: !isTimedOut && t.passed ? 1 : 0,\n failed: !isTimedOut && t.failed ? 1 : 0,\n warned: !isTimedOut && t.warned ? 1 : 0,\n skipped: !isTimedOut && t.skipped ? 1 : 0,\n pending: !isTimedOut && t.pending ? 1 : 0,\n running: !isTimedOut && t.running ? 1 : 0,\n timedout: isTimedOut ? 1 : 0,\n };\n};\n\nexport function sum(t: Test): StatusCounts {\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sum(c));\n return r;\n}\n\n/** Roll up a forest's non-task status counts. Shared by `TestRunner`'s filter\n * bar and `TestRunSummary`'s chips so both read the same tally. */\nexport function aggregateStatusCounts(tests: Test[]): StatusCounts {\n const r = emptyCounts();\n for (const t of tests) addCounts(r, sumNonTaskTests(t));\n return r;\n}\n\n/** Ordered status key → label list, driving both the filter bar's pills and a\n * host's status filter option list, so the seven rows aren't hand-duplicated. */\nexport const STATUS_LABELS: { key: keyof StatusCounts; label: string }[] = [\n { key: \"failed\", label: \"Failed\" },\n { key: \"warned\", label: \"Warned\" },\n { key: \"timedout\", label: \"Timed out\" },\n { key: \"passed\", label: \"Passed\" },\n { key: \"skipped\", label: \"Skipped\" },\n { key: \"running\", label: \"Running\" },\n { key: \"pending\", label: \"Queued\" },\n];\n\n/** Like {@link sum} but excludes `task`-framework pseudo-nodes from the tally. */\nexport function sumNonTaskTests(t: Test): StatusCounts {\n if (t.framework === \"task\") {\n const r = emptyCounts();\n for (const c of t.children || []) addCounts(r, sumNonTaskTests(c));\n return r;\n }\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sumNonTaskTests(c));\n return r;\n}\n\nexport function hasFailed(t: Test): boolean {\n if (t.failed) return true;\n return (t.children || []).some(hasFailed);\n}\n\nexport function hasPending(t: Test): boolean {\n if (t.pending) return true;\n return (t.children || []).some(hasPending);\n}\n\nfunction hasTimedOutDescendant(t: Test): boolean {\n if (t.timed_out) return true;\n return (t.children || []).some(hasTimedOutDescendant);\n}\n\nexport function totalDuration(t: Test): number {\n if (t.duration && t.duration > 0) return t.duration;\n let d = 0;\n for (const c of t.children || []) d += totalDuration(c);\n return d;\n}\n\n// --- status classification ----------------------------------------------\n\nexport type TestStatus =\n | \"passed\"\n | \"failed\"\n | \"warned\"\n | \"skipped\"\n | \"pending\"\n | \"running\"\n | \"timedout\";\n\n/** Resolve a leaf node's single status, or null for a status-less container. */\nexport function testStatus(t: Test): TestStatus | null {\n if (t.timed_out) return \"timedout\";\n if (t.running) return \"running\";\n if (t.pending) return \"pending\";\n if (t.failed) return \"failed\";\n if (t.warned) return \"warned\";\n if (t.skipped) return \"skipped\";\n if (t.passed) return \"passed\";\n return null;\n}\n\n/** Pick the clicky icon for a node, rolling up container state. */\nexport function statusIconFor(t: Test): StaticIconComponent {\n if (t.timed_out) return UiHourglass;\n if (t.running) return UiLoader;\n if (t.pending) return UiPause;\n if (t.failed) return UiError;\n if (t.warned) return UiWarningTriangle;\n if (t.skipped) return UiPause;\n if (t.passed) return UiPass;\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return UiHourglass;\n if (s.failed > 0) return UiError;\n if (s.warned > 0) return UiWarningTriangle;\n if (s.running > 0) return UiLoader;\n if (s.pending > 0) return UiPause;\n return UiPass;\n }\n return UiClass;\n}\n\n/** Map a node's status to a semantic Badge tone, rolling up container state. */\nexport function statusToneFor(t: Test): BadgeTone {\n if (t.timed_out) return \"warning\";\n if (t.running) return \"info\";\n if (t.pending) return \"neutral\";\n if (t.failed) return \"danger\";\n if (t.warned) return \"warning\";\n if (t.skipped) return \"warning\";\n if (t.passed) return \"success\";\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return \"warning\";\n if (s.failed > 0) return \"danger\";\n if (s.warned > 0) return \"warning\";\n if (s.running > 0) return \"info\";\n if (s.pending > 0) return \"neutral\";\n return \"success\";\n }\n return \"neutral\";\n}\n\n// --- formatting ----------------------------------------------------------\n\n/**\n * Format a nanosecond duration as \"123ms\" or \"1.2s\". Empty for non-positive.\n * Named distinctly from clicky's ms/s `formatDuration` (lib/format) since the\n * test-runner wire format is nanoseconds.\n */\nexport function formatTestDuration(ns: number): string {\n if (ns <= 0) return \"\";\n const ms = ns / 1e6;\n if (ms < 1000) return `${ms.toFixed(0)}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction compactWithUnit(value: number, unit: string): string {\n const rounded =\n Math.abs(value) >= 10 ? Math.round(value).toString() : value.toFixed(1).replace(/\\.0$/, \"\");\n return `${rounded}${unit}`;\n}\n\n/** Compact integer counts: <1000 raw, then \"k\", then \"M\". */\nexport function formatCount(n: number): string {\n const abs = Math.abs(n);\n if (abs < 1000) return String(n);\n if (abs < 1_000_000) return compactWithUnit(n / 1000, \"k\");\n return compactWithUnit(n / 1_000_000, \"M\");\n}\n\n/** Strip Go's `Test` prefix and split CamelCase / subtest names into words. */\nexport function humanizeName(name: string, framework?: string): string {\n if (!name) return \"\";\n if (framework !== \"go test\") return name;\n if (name.endsWith(\"/\")) return name;\n return name\n .split(\"/\")\n .map((p, i) => {\n if (i === 0) {\n return p.replace(/^Test/, \"\").replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n }\n return p.replace(/_/g, \" \");\n })\n .join(\" / \");\n}\n\n// --- tree transforms -----------------------------------------------------\n\n/** Unique non-task frameworks present in the forest, sorted. */\nexport function collectFrameworks(tests: Test[]): string[] {\n const set = new Set<string>();\n const walk = (t: Test) => {\n if (t.framework && t.framework !== \"task\") set.add(t.framework);\n (t.children || []).forEach(walk);\n };\n tests.forEach(walk);\n return Array.from(set).sort();\n}\n\nfunction joinChainNames(parent: string | undefined, child: string | undefined): string {\n const p = (parent ?? \"\").trim();\n const c = (child ?? \"\").trim();\n if (!p) return c;\n if (!c) return p;\n return `${p} > ${c}`;\n}\n\nfunction isCollapsibleContainer(t: Test): boolean {\n return !(t.passed || t.failed || t.skipped || t.pending);\n}\n\n// Re-deriving a collapsed/filtered node invalidates any cached summary; drop it\n// so callers re-roll-up from the surviving children.\nfunction withoutSummary(t: Test): Omit<Test, \"summary\"> & { summary?: never } {\n const { summary: _summary, ...rest } = t;\n return rest;\n}\n\nfunction collapseNode(t: Test): Test {\n const children = t.children ?? [];\n if (children.length === 0) return t;\n\n const collapsedChildren = children.map(collapseNode);\n\n const onlyChild = collapsedChildren.length === 1 ? collapsedChildren[0] : undefined;\n if (onlyChild && isCollapsibleContainer(t)) {\n return { ...withoutSummary(onlyChild), name: joinChainNames(t.name, onlyChild.name) };\n }\n\n return { ...t, children: collapsedChildren };\n}\n\n/**\n * Compress any status-less container with exactly one child into that child,\n * joining names with \" > \". Recursive, so chains collapse into a single row.\n */\nexport function collapseSingleChildChains(tests: Test[]): Test[] {\n return tests.map(collapseNode);\n}\n\nfunction matchesLeaf(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): boolean {\n const s = testStatus(t);\n if (s === null) return false;\n return matchesFilterState(s, statusFilter) && matchesFilterState(t.framework, frameworkFilter);\n}\n\nfunction filterNode(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test | null {\n if (t.children && t.children.length > 0) {\n const filtered = t.children\n .map((c) => filterNode(c, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n if (filtered.length === 0) return null;\n return { ...withoutSummary(t), children: filtered };\n }\n return matchesLeaf(t, statusFilter, frameworkFilter) ? t : null;\n}\n\n/** Prune leaves not matching the status/framework filters; drop empty branches. */\nexport function filterTests(\n tests: Test[],\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test[] {\n if (statusFilter.size === 0 && frameworkFilter.size === 0) return tests;\n return tests\n .map((t) => filterNode(t, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n}\n"],"names":["r","UiHourglass","UiLoader","UiPause","UiError","UiWarningTriangle","UiPass","UiClass","matchesFilterState"],"mappings":";;;;;;;;;;AAqCA,MAAM,cAAc,OAAqB;AAAA,EACvC,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AACZ;AAEA,MAAM,YAAY,CAAC,GAAiB,MAAoB;AACtD,IAAE,SAAS,EAAE;AACb,IAAE,UAAU,EAAE;AACd,IAAE,UAAU,EAAE;AACd,IAAE,UAAU,EAAE;AACd,IAAE,WAAW,EAAE;AACf,IAAE,WAAW,EAAE;AACf,IAAE,WAAW,EAAE;AACf,IAAE,YAAY,EAAE;AAClB;AAEA,MAAM,oBAAoB,CAAC,aAAwC;AAAA,EACjE,OAAO,QAAQ;AAAA,EACf,QAAQ,QAAQ;AAAA,EAChB,QAAQ,QAAQ;AAAA,EAChB,QAAQ,QAAQ,UAAU;AAAA,EAC1B,SAAS,QAAQ;AAAA,EACjB,SAAS,QAAQ,WAAW;AAAA,EAC5B,SAAS,QAAQ,WAAW;AAAA,EAC5B,UAAU,QAAQ,YAAY;AAChC;AAEA,MAAM,iBAAiB,CAAC,MAA0B;AAChD,QAAM,aAAa,CAAC,CAAC,EAAE;AACvB,QAAM,UACJ,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE;AAChF,SAAO;AAAA,IACL,OAAO,UAAU,IAAI;AAAA,IACrB,QAAQ,CAAC,cAAc,EAAE,SAAS,IAAI;AAAA,IACtC,QAAQ,CAAC,cAAc,EAAE,SAAS,IAAI;AAAA,IACtC,QAAQ,CAAC,cAAc,EAAE,SAAS,IAAI;AAAA,IACtC,SAAS,CAAC,cAAc,EAAE,UAAU,IAAI;AAAA,IACxC,SAAS,CAAC,cAAc,EAAE,UAAU,IAAI;AAAA,IACxC,SAAS,CAAC,cAAc,EAAE,UAAU,IAAI;AAAA,IACxC,UAAU,aAAa,IAAI;AAAA,EAAA;AAE/B;AAEO,SAAS,IAAI,GAAuB;AACzC,MAAI,EAAE,QAAS,QAAO,kBAAkB,EAAE,OAAO;AACjD,MAAI,CAAC,EAAE,YAAY,EAAE,SAAS,WAAW,EAAG,QAAO,eAAe,CAAC;AACnE,QAAM,IAAI,YAAA;AACV,aAAW,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC,CAAC;AAC/C,SAAO;AACT;AAIO,SAAS,sBAAsB,OAA6B;AACjE,QAAM,IAAI,YAAA;AACV,aAAW,KAAK,MAAO,WAAU,GAAG,gBAAgB,CAAC,CAAC;AACtD,SAAO;AACT;AAIO,MAAM,gBAA8D;AAAA,EACzE,EAAE,KAAK,UAAU,OAAO,SAAA;AAAA,EACxB,EAAE,KAAK,UAAU,OAAO,SAAA;AAAA,EACxB,EAAE,KAAK,YAAY,OAAO,YAAA;AAAA,EAC1B,EAAE,KAAK,UAAU,OAAO,SAAA;AAAA,EACxB,EAAE,KAAK,WAAW,OAAO,UAAA;AAAA,EACzB,EAAE,KAAK,WAAW,OAAO,UAAA;AAAA,EACzB,EAAE,KAAK,WAAW,OAAO,SAAA;AAC3B;AAGO,SAAS,gBAAgB,GAAuB;AACrD,MAAI,EAAE,cAAc,QAAQ;AAC1B,UAAMA,KAAI,YAAA;AACV,eAAW,KAAK,EAAE,YAAY,CAAA,EAAI,WAAUA,IAAG,gBAAgB,CAAC,CAAC;AACjE,WAAOA;AAAAA,EACT;AACA,MAAI,EAAE,QAAS,QAAO,kBAAkB,EAAE,OAAO;AACjD,MAAI,CAAC,EAAE,YAAY,EAAE,SAAS,WAAW,EAAG,QAAO,eAAe,CAAC;AACnE,QAAM,IAAI,YAAA;AACV,aAAW,KAAK,EAAE,oBAAoB,GAAG,gBAAgB,CAAC,CAAC;AAC3D,SAAO;AACT;AAEO,SAAS,UAAU,GAAkB;AAC1C,MAAI,EAAE,OAAQ,QAAO;AACrB,UAAQ,EAAE,YAAY,CAAA,GAAI,KAAK,SAAS;AAC1C;AAEO,SAAS,WAAW,GAAkB;AAC3C,MAAI,EAAE,QAAS,QAAO;AACtB,UAAQ,EAAE,YAAY,CAAA,GAAI,KAAK,UAAU;AAC3C;AAEA,SAAS,sBAAsB,GAAkB;AAC/C,MAAI,EAAE,UAAW,QAAO;AACxB,UAAQ,EAAE,YAAY,CAAA,GAAI,KAAK,qBAAqB;AACtD;AAEO,SAAS,cAAc,GAAiB;AAC7C,MAAI,EAAE,YAAY,EAAE,WAAW,UAAU,EAAE;AAC3C,MAAI,IAAI;AACR,aAAW,KAAK,EAAE,YAAY,CAAA,EAAI,MAAK,cAAc,CAAC;AACtD,SAAO;AACT;AAcO,SAAS,WAAW,GAA4B;AACrD,MAAI,EAAE,UAAW,QAAO;AACxB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,SAAO;AACT;AAGO,SAAS,cAAc,GAA8B;AAC1D,MAAI,EAAE,UAAW,QAAOC,YAAAA;AACxB,MAAI,EAAE,QAAS,QAAOC,SAAAA;AACtB,MAAI,EAAE,QAAS,QAAOC,QAAAA;AACtB,MAAI,EAAE,OAAQ,QAAOC,QAAAA;AACrB,MAAI,EAAE,OAAQ,QAAOC,kBAAAA;AACrB,MAAI,EAAE,QAAS,QAAOF,QAAAA;AACtB,MAAI,EAAE,OAAQ,QAAOG,OAAAA;AACrB,MAAI,EAAE,YAAY,EAAE,SAAS,SAAS,GAAG;AACvC,UAAM,IAAI,IAAI,CAAC;AACf,QAAI,sBAAsB,CAAC,EAAG,QAAOL,YAAAA;AACrC,QAAI,EAAE,SAAS,EAAG,QAAOG,QAAAA;AACzB,QAAI,EAAE,SAAS,EAAG,QAAOC,kBAAAA;AACzB,QAAI,EAAE,UAAU,EAAG,QAAOH,SAAAA;AAC1B,QAAI,EAAE,UAAU,EAAG,QAAOC,QAAAA;AAC1B,WAAOG,OAAAA;AAAAA,EACT;AACA,SAAOC,QAAAA;AACT;AAGO,SAAS,cAAc,GAAoB;AAChD,MAAI,EAAE,UAAW,QAAO;AACxB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,QAAS,QAAO;AACtB,MAAI,EAAE,OAAQ,QAAO;AACrB,MAAI,EAAE,YAAY,EAAE,SAAS,SAAS,GAAG;AACvC,UAAM,IAAI,IAAI,CAAC;AACf,QAAI,sBAAsB,CAAC,EAAG,QAAO;AACrC,QAAI,EAAE,SAAS,EAAG,QAAO;AACzB,QAAI,EAAE,SAAS,EAAG,QAAO;AACzB,QAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,QAAI,EAAE,UAAU,EAAG,QAAO;AAC1B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AASO,SAAS,mBAAmB,IAAoB;AACrD,MAAI,MAAM,EAAG,QAAO;AACpB,QAAM,KAAK,KAAK;AAChB,MAAI,KAAK,IAAM,QAAO,GAAG,GAAG,QAAQ,CAAC,CAAC;AACtC,SAAO,IAAI,KAAK,KAAM,QAAQ,CAAC,CAAC;AAClC;AAEA,SAAS,gBAAgB,OAAe,MAAsB;AAC5D,QAAM,UACJ,KAAK,IAAI,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,EAAE,SAAA,IAAa,MAAM,QAAQ,CAAC,EAAE,QAAQ,QAAQ,EAAE;AAC5F,SAAO,GAAG,OAAO,GAAG,IAAI;AAC1B;AAGO,SAAS,YAAY,GAAmB;AAC7C,QAAM,MAAM,KAAK,IAAI,CAAC;AACtB,MAAI,MAAM,IAAM,QAAO,OAAO,CAAC;AAC/B,MAAI,MAAM,IAAW,QAAO,gBAAgB,IAAI,KAAM,GAAG;AACzD,SAAO,gBAAgB,IAAI,KAAW,GAAG;AAC3C;AAGO,SAAS,aAAa,MAAc,WAA4B;AACrE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,cAAc,UAAW,QAAO;AACpC,MAAI,KAAK,SAAS,GAAG,EAAG,QAAO;AAC/B,SAAO,KACJ,MAAM,GAAG,EACT,IAAI,CAAC,GAAG,MAAM;AACb,QAAI,MAAM,GAAG;AACX,aAAO,EAAE,QAAQ,SAAS,EAAE,EAAE,QAAQ,sBAAsB,OAAO;AAAA,IACrE;AACA,WAAO,EAAE,QAAQ,MAAM,GAAG;AAAA,EAC5B,CAAC,EACA,KAAK,KAAK;AACf;AAKO,SAAS,kBAAkB,OAAyB;AACzD,QAAM,0BAAU,IAAA;AAChB,QAAM,OAAO,CAAC,MAAY;AACxB,QAAI,EAAE,aAAa,EAAE,cAAc,OAAQ,KAAI,IAAI,EAAE,SAAS;AAC9D,KAAC,EAAE,YAAY,IAAI,QAAQ,IAAI;AAAA,EACjC;AACA,QAAM,QAAQ,IAAI;AAClB,SAAO,MAAM,KAAK,GAAG,EAAE,KAAA;AACzB;AAEA,SAAS,eAAe,QAA4B,OAAmC;AACrF,QAAM,KAAK,UAAU,IAAI,KAAA;AACzB,QAAM,KAAK,SAAS,IAAI,KAAA;AACxB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,GAAG,CAAC,MAAM,CAAC;AACpB;AAEA,SAAS,uBAAuB,GAAkB;AAChD,SAAO,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE;AAClD;AAIA,SAAS,eAAe,GAAsD;AAC5E,QAAM,EAAE,SAAS,UAAU,GAAG,SAAS;AACvC,SAAO;AACT;AAEA,SAAS,aAAa,GAAe;AACnC,QAAM,WAAW,EAAE,YAAY,CAAA;AAC/B,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAM,oBAAoB,SAAS,IAAI,YAAY;AAEnD,QAAM,YAAY,kBAAkB,WAAW,IAAI,kBAAkB,CAAC,IAAI;AAC1E,MAAI,aAAa,uBAAuB,CAAC,GAAG;AAC1C,WAAO,EAAE,GAAG,eAAe,SAAS,GAAG,MAAM,eAAe,EAAE,MAAM,UAAU,IAAI,EAAA;AAAA,EACpF;AAEA,SAAO,EAAE,GAAG,GAAG,UAAU,kBAAA;AAC3B;AAMO,SAAS,0BAA0B,OAAuB;AAC/D,SAAO,MAAM,IAAI,YAAY;AAC/B;AAEA,SAAS,YACP,GACA,cACA,iBACS;AACT,QAAM,IAAI,WAAW,CAAC;AACtB,MAAI,MAAM,KAAM,QAAO;AACvB,SAAOC,YAAAA,mBAAmB,GAAG,YAAY,KAAKA,YAAAA,mBAAmB,EAAE,WAAW,eAAe;AAC/F;AAEA,SAAS,WACP,GACA,cACA,iBACa;AACb,MAAI,EAAE,YAAY,EAAE,SAAS,SAAS,GAAG;AACvC,UAAM,WAAW,EAAE,SAChB,IAAI,CAAC,MAAM,WAAW,GAAG,cAAc,eAAe,CAAC,EACvD,OAAO,OAAO;AACjB,QAAI,SAAS,WAAW,EAAG,QAAO;AAClC,WAAO,EAAE,GAAG,eAAe,CAAC,GAAG,UAAU,SAAA;AAAA,EAC3C;AACA,SAAO,YAAY,GAAG,cAAc,eAAe,IAAI,IAAI;AAC7D;AAGO,SAAS,YACd,OACA,cACA,iBACQ;AACR,MAAI,aAAa,SAAS,KAAK,gBAAgB,SAAS,EAAG,QAAO;AAClE,SAAO,MACJ,IAAI,CAAC,MAAM,WAAW,GAAG,cAAc,eAAe,CAAC,EACvD,OAAO,OAAO;AACnB;;;;;;;;;;;;;;;;;"}
|
|
@@ -34,7 +34,7 @@ const countsFromSummary = (summary) => ({
|
|
|
34
34
|
skipped: summary.Skipped,
|
|
35
35
|
pending: summary.Pending || 0,
|
|
36
36
|
running: summary.Running || 0,
|
|
37
|
-
timedout: 0
|
|
37
|
+
timedout: summary.Timedout || 0
|
|
38
38
|
});
|
|
39
39
|
const countsFromLeaf = (t) => {
|
|
40
40
|
const isTimedOut = !!t.timed_out;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sources":["../../../src/data/test-runner/status.ts"],"sourcesContent":["// Pure status/roll-up helpers for the test runner — ported from Gavel's\n// utils.ts (Tests-only subset). No JSX here. The two status mappers\n// (statusIconFor / statusToneFor) replace Gavel's raw-Tailwind statusIcon /\n// statusColor with clicky's icon set and semantic Badge tones.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport type { BadgeTone } from \"../Badge\";\nimport {\n UiClass,\n UiError,\n UiHourglass,\n UiLoader,\n UiPass,\n UiPause,\n UiWarningTriangle,\n} from \"../../icons\";\nimport type { Test, TestSummary } from \"./types\";\nimport { matchesFilterState, type FilterState } from \"./filterState\";\n\n// --- roll-up counts ------------------------------------------------------\n\n/**\n * Tallied verdict breakdown a `Test` subtree rolls up to. `warned` is amber and\n * orthogonal to a real failure — a warned-only leaf counts in total + warned,\n * never in failed.\n */\nexport type StatusCounts = {\n total: number;\n passed: number;\n failed: number;\n warned: number;\n skipped: number;\n pending: number;\n running: number;\n timedout: number;\n};\n\nconst emptyCounts = (): StatusCounts => ({\n total: 0,\n passed: 0,\n failed: 0,\n warned: 0,\n skipped: 0,\n pending: 0,\n running: 0,\n timedout: 0,\n});\n\nconst addCounts = (r: StatusCounts, s: StatusCounts) => {\n r.total += s.total;\n r.passed += s.passed;\n r.failed += s.failed;\n r.warned += s.warned;\n r.skipped += s.skipped;\n r.pending += s.pending;\n r.running += s.running;\n r.timedout += s.timedout;\n};\n\nconst countsFromSummary = (summary: TestSummary): StatusCounts => ({\n total: summary.Total,\n passed: summary.Passed,\n failed: summary.Failed,\n warned: summary.Warned || 0,\n skipped: summary.Skipped,\n pending: summary.Pending || 0,\n running: summary.Running || 0,\n timedout: 0,\n});\n\nconst countsFromLeaf = (t: Test): StatusCounts => {\n const isTimedOut = !!t.timed_out;\n const counted =\n isTimedOut || t.passed || t.failed || t.warned || t.skipped || t.pending || t.running;\n return {\n total: counted ? 1 : 0,\n passed: !isTimedOut && t.passed ? 1 : 0,\n failed: !isTimedOut && t.failed ? 1 : 0,\n warned: !isTimedOut && t.warned ? 1 : 0,\n skipped: !isTimedOut && t.skipped ? 1 : 0,\n pending: !isTimedOut && t.pending ? 1 : 0,\n running: !isTimedOut && t.running ? 1 : 0,\n timedout: isTimedOut ? 1 : 0,\n };\n};\n\nexport function sum(t: Test): StatusCounts {\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sum(c));\n return r;\n}\n\n/** Roll up a forest's non-task status counts. Shared by `TestRunner`'s filter\n * bar and `TestRunSummary`'s chips so both read the same tally. */\nexport function aggregateStatusCounts(tests: Test[]): StatusCounts {\n const r = emptyCounts();\n for (const t of tests) addCounts(r, sumNonTaskTests(t));\n return r;\n}\n\n/** Ordered status key → label list, driving both the filter bar's pills and a\n * host's status filter option list, so the seven rows aren't hand-duplicated. */\nexport const STATUS_LABELS: { key: keyof StatusCounts; label: string }[] = [\n { key: \"failed\", label: \"Failed\" },\n { key: \"warned\", label: \"Warned\" },\n { key: \"timedout\", label: \"Timed out\" },\n { key: \"passed\", label: \"Passed\" },\n { key: \"skipped\", label: \"Skipped\" },\n { key: \"running\", label: \"Running\" },\n { key: \"pending\", label: \"Queued\" },\n];\n\n/** Like {@link sum} but excludes `task`-framework pseudo-nodes from the tally. */\nexport function sumNonTaskTests(t: Test): StatusCounts {\n if (t.framework === \"task\") {\n const r = emptyCounts();\n for (const c of t.children || []) addCounts(r, sumNonTaskTests(c));\n return r;\n }\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sumNonTaskTests(c));\n return r;\n}\n\nexport function hasFailed(t: Test): boolean {\n if (t.failed) return true;\n return (t.children || []).some(hasFailed);\n}\n\nexport function hasPending(t: Test): boolean {\n if (t.pending) return true;\n return (t.children || []).some(hasPending);\n}\n\nfunction hasTimedOutDescendant(t: Test): boolean {\n if (t.timed_out) return true;\n return (t.children || []).some(hasTimedOutDescendant);\n}\n\nexport function totalDuration(t: Test): number {\n if (t.duration && t.duration > 0) return t.duration;\n let d = 0;\n for (const c of t.children || []) d += totalDuration(c);\n return d;\n}\n\n// --- status classification ----------------------------------------------\n\nexport type TestStatus =\n | \"passed\"\n | \"failed\"\n | \"warned\"\n | \"skipped\"\n | \"pending\"\n | \"running\"\n | \"timedout\";\n\n/** Resolve a leaf node's single status, or null for a status-less container. */\nexport function testStatus(t: Test): TestStatus | null {\n if (t.timed_out) return \"timedout\";\n if (t.running) return \"running\";\n if (t.pending) return \"pending\";\n if (t.failed) return \"failed\";\n if (t.warned) return \"warned\";\n if (t.skipped) return \"skipped\";\n if (t.passed) return \"passed\";\n return null;\n}\n\n/** Pick the clicky icon for a node, rolling up container state. */\nexport function statusIconFor(t: Test): StaticIconComponent {\n if (t.timed_out) return UiHourglass;\n if (t.running) return UiLoader;\n if (t.pending) return UiPause;\n if (t.failed) return UiError;\n if (t.warned) return UiWarningTriangle;\n if (t.skipped) return UiPause;\n if (t.passed) return UiPass;\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return UiHourglass;\n if (s.failed > 0) return UiError;\n if (s.warned > 0) return UiWarningTriangle;\n if (s.running > 0) return UiLoader;\n if (s.pending > 0) return UiPause;\n return UiPass;\n }\n return UiClass;\n}\n\n/** Map a node's status to a semantic Badge tone, rolling up container state. */\nexport function statusToneFor(t: Test): BadgeTone {\n if (t.timed_out) return \"warning\";\n if (t.running) return \"info\";\n if (t.pending) return \"neutral\";\n if (t.failed) return \"danger\";\n if (t.warned) return \"warning\";\n if (t.skipped) return \"warning\";\n if (t.passed) return \"success\";\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return \"warning\";\n if (s.failed > 0) return \"danger\";\n if (s.warned > 0) return \"warning\";\n if (s.running > 0) return \"info\";\n if (s.pending > 0) return \"neutral\";\n return \"success\";\n }\n return \"neutral\";\n}\n\n// --- formatting ----------------------------------------------------------\n\n/**\n * Format a nanosecond duration as \"123ms\" or \"1.2s\". Empty for non-positive.\n * Named distinctly from clicky's ms/s `formatDuration` (lib/format) since the\n * test-runner wire format is nanoseconds.\n */\nexport function formatTestDuration(ns: number): string {\n if (ns <= 0) return \"\";\n const ms = ns / 1e6;\n if (ms < 1000) return `${ms.toFixed(0)}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction compactWithUnit(value: number, unit: string): string {\n const rounded =\n Math.abs(value) >= 10 ? Math.round(value).toString() : value.toFixed(1).replace(/\\.0$/, \"\");\n return `${rounded}${unit}`;\n}\n\n/** Compact integer counts: <1000 raw, then \"k\", then \"M\". */\nexport function formatCount(n: number): string {\n const abs = Math.abs(n);\n if (abs < 1000) return String(n);\n if (abs < 1_000_000) return compactWithUnit(n / 1000, \"k\");\n return compactWithUnit(n / 1_000_000, \"M\");\n}\n\n/** Strip Go's `Test` prefix and split CamelCase / subtest names into words. */\nexport function humanizeName(name: string, framework?: string): string {\n if (!name) return \"\";\n if (framework !== \"go test\") return name;\n if (name.endsWith(\"/\")) return name;\n return name\n .split(\"/\")\n .map((p, i) => {\n if (i === 0) {\n return p.replace(/^Test/, \"\").replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n }\n return p.replace(/_/g, \" \");\n })\n .join(\" / \");\n}\n\n// --- tree transforms -----------------------------------------------------\n\n/** Unique non-task frameworks present in the forest, sorted. */\nexport function collectFrameworks(tests: Test[]): string[] {\n const set = new Set<string>();\n const walk = (t: Test) => {\n if (t.framework && t.framework !== \"task\") set.add(t.framework);\n (t.children || []).forEach(walk);\n };\n tests.forEach(walk);\n return Array.from(set).sort();\n}\n\nfunction joinChainNames(parent: string | undefined, child: string | undefined): string {\n const p = (parent ?? \"\").trim();\n const c = (child ?? \"\").trim();\n if (!p) return c;\n if (!c) return p;\n return `${p} > ${c}`;\n}\n\nfunction isCollapsibleContainer(t: Test): boolean {\n return !(t.passed || t.failed || t.skipped || t.pending);\n}\n\n// Re-deriving a collapsed/filtered node invalidates any cached summary; drop it\n// so callers re-roll-up from the surviving children.\nfunction withoutSummary(t: Test): Omit<Test, \"summary\"> & { summary?: never } {\n const { summary: _summary, ...rest } = t;\n return rest;\n}\n\nfunction collapseNode(t: Test): Test {\n const children = t.children ?? [];\n if (children.length === 0) return t;\n\n const collapsedChildren = children.map(collapseNode);\n\n const onlyChild = collapsedChildren.length === 1 ? collapsedChildren[0] : undefined;\n if (onlyChild && isCollapsibleContainer(t)) {\n return { ...withoutSummary(onlyChild), name: joinChainNames(t.name, onlyChild.name) };\n }\n\n return { ...t, children: collapsedChildren };\n}\n\n/**\n * Compress any status-less container with exactly one child into that child,\n * joining names with \" > \". Recursive, so chains collapse into a single row.\n */\nexport function collapseSingleChildChains(tests: Test[]): Test[] {\n return tests.map(collapseNode);\n}\n\nfunction matchesLeaf(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): boolean {\n const s = testStatus(t);\n if (s === null) return false;\n return matchesFilterState(s, statusFilter) && matchesFilterState(t.framework, frameworkFilter);\n}\n\nfunction filterNode(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test | null {\n if (t.children && t.children.length > 0) {\n const filtered = t.children\n .map((c) => filterNode(c, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n if (filtered.length === 0) return null;\n return { ...withoutSummary(t), children: filtered };\n }\n return matchesLeaf(t, statusFilter, frameworkFilter) ? t : null;\n}\n\n/** Prune leaves not matching the status/framework filters; drop empty branches. */\nexport function filterTests(\n tests: Test[],\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test[] {\n if (statusFilter.size === 0 && frameworkFilter.size === 0) return tests;\n return tests\n .map((t) => filterNode(t, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n}\n"],"names":["r"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"status.js","sources":["../../../src/data/test-runner/status.ts"],"sourcesContent":["// Pure status/roll-up helpers for the test runner — ported from Gavel's\n// utils.ts (Tests-only subset). No JSX here. The two status mappers\n// (statusIconFor / statusToneFor) replace Gavel's raw-Tailwind statusIcon /\n// statusColor with clicky's icon set and semantic Badge tones.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport type { BadgeTone } from \"../Badge\";\nimport {\n UiClass,\n UiError,\n UiHourglass,\n UiLoader,\n UiPass,\n UiPause,\n UiWarningTriangle,\n} from \"../../icons\";\nimport type { Test, TestSummary } from \"./types\";\nimport { matchesFilterState, type FilterState } from \"./filterState\";\n\n// --- roll-up counts ------------------------------------------------------\n\n/**\n * Tallied verdict breakdown a `Test` subtree rolls up to. `warned` is amber and\n * orthogonal to a real failure — a warned-only leaf counts in total + warned,\n * never in failed.\n */\nexport type StatusCounts = {\n total: number;\n passed: number;\n failed: number;\n warned: number;\n skipped: number;\n pending: number;\n running: number;\n timedout: number;\n};\n\nconst emptyCounts = (): StatusCounts => ({\n total: 0,\n passed: 0,\n failed: 0,\n warned: 0,\n skipped: 0,\n pending: 0,\n running: 0,\n timedout: 0,\n});\n\nconst addCounts = (r: StatusCounts, s: StatusCounts) => {\n r.total += s.total;\n r.passed += s.passed;\n r.failed += s.failed;\n r.warned += s.warned;\n r.skipped += s.skipped;\n r.pending += s.pending;\n r.running += s.running;\n r.timedout += s.timedout;\n};\n\nconst countsFromSummary = (summary: TestSummary): StatusCounts => ({\n total: summary.Total,\n passed: summary.Passed,\n failed: summary.Failed,\n warned: summary.Warned || 0,\n skipped: summary.Skipped,\n pending: summary.Pending || 0,\n running: summary.Running || 0,\n timedout: summary.Timedout || 0,\n});\n\nconst countsFromLeaf = (t: Test): StatusCounts => {\n const isTimedOut = !!t.timed_out;\n const counted =\n isTimedOut || t.passed || t.failed || t.warned || t.skipped || t.pending || t.running;\n return {\n total: counted ? 1 : 0,\n passed: !isTimedOut && t.passed ? 1 : 0,\n failed: !isTimedOut && t.failed ? 1 : 0,\n warned: !isTimedOut && t.warned ? 1 : 0,\n skipped: !isTimedOut && t.skipped ? 1 : 0,\n pending: !isTimedOut && t.pending ? 1 : 0,\n running: !isTimedOut && t.running ? 1 : 0,\n timedout: isTimedOut ? 1 : 0,\n };\n};\n\nexport function sum(t: Test): StatusCounts {\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sum(c));\n return r;\n}\n\n/** Roll up a forest's non-task status counts. Shared by `TestRunner`'s filter\n * bar and `TestRunSummary`'s chips so both read the same tally. */\nexport function aggregateStatusCounts(tests: Test[]): StatusCounts {\n const r = emptyCounts();\n for (const t of tests) addCounts(r, sumNonTaskTests(t));\n return r;\n}\n\n/** Ordered status key → label list, driving both the filter bar's pills and a\n * host's status filter option list, so the seven rows aren't hand-duplicated. */\nexport const STATUS_LABELS: { key: keyof StatusCounts; label: string }[] = [\n { key: \"failed\", label: \"Failed\" },\n { key: \"warned\", label: \"Warned\" },\n { key: \"timedout\", label: \"Timed out\" },\n { key: \"passed\", label: \"Passed\" },\n { key: \"skipped\", label: \"Skipped\" },\n { key: \"running\", label: \"Running\" },\n { key: \"pending\", label: \"Queued\" },\n];\n\n/** Like {@link sum} but excludes `task`-framework pseudo-nodes from the tally. */\nexport function sumNonTaskTests(t: Test): StatusCounts {\n if (t.framework === \"task\") {\n const r = emptyCounts();\n for (const c of t.children || []) addCounts(r, sumNonTaskTests(c));\n return r;\n }\n if (t.summary) return countsFromSummary(t.summary);\n if (!t.children || t.children.length === 0) return countsFromLeaf(t);\n const r = emptyCounts();\n for (const c of t.children) addCounts(r, sumNonTaskTests(c));\n return r;\n}\n\nexport function hasFailed(t: Test): boolean {\n if (t.failed) return true;\n return (t.children || []).some(hasFailed);\n}\n\nexport function hasPending(t: Test): boolean {\n if (t.pending) return true;\n return (t.children || []).some(hasPending);\n}\n\nfunction hasTimedOutDescendant(t: Test): boolean {\n if (t.timed_out) return true;\n return (t.children || []).some(hasTimedOutDescendant);\n}\n\nexport function totalDuration(t: Test): number {\n if (t.duration && t.duration > 0) return t.duration;\n let d = 0;\n for (const c of t.children || []) d += totalDuration(c);\n return d;\n}\n\n// --- status classification ----------------------------------------------\n\nexport type TestStatus =\n | \"passed\"\n | \"failed\"\n | \"warned\"\n | \"skipped\"\n | \"pending\"\n | \"running\"\n | \"timedout\";\n\n/** Resolve a leaf node's single status, or null for a status-less container. */\nexport function testStatus(t: Test): TestStatus | null {\n if (t.timed_out) return \"timedout\";\n if (t.running) return \"running\";\n if (t.pending) return \"pending\";\n if (t.failed) return \"failed\";\n if (t.warned) return \"warned\";\n if (t.skipped) return \"skipped\";\n if (t.passed) return \"passed\";\n return null;\n}\n\n/** Pick the clicky icon for a node, rolling up container state. */\nexport function statusIconFor(t: Test): StaticIconComponent {\n if (t.timed_out) return UiHourglass;\n if (t.running) return UiLoader;\n if (t.pending) return UiPause;\n if (t.failed) return UiError;\n if (t.warned) return UiWarningTriangle;\n if (t.skipped) return UiPause;\n if (t.passed) return UiPass;\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return UiHourglass;\n if (s.failed > 0) return UiError;\n if (s.warned > 0) return UiWarningTriangle;\n if (s.running > 0) return UiLoader;\n if (s.pending > 0) return UiPause;\n return UiPass;\n }\n return UiClass;\n}\n\n/** Map a node's status to a semantic Badge tone, rolling up container state. */\nexport function statusToneFor(t: Test): BadgeTone {\n if (t.timed_out) return \"warning\";\n if (t.running) return \"info\";\n if (t.pending) return \"neutral\";\n if (t.failed) return \"danger\";\n if (t.warned) return \"warning\";\n if (t.skipped) return \"warning\";\n if (t.passed) return \"success\";\n if (t.children && t.children.length > 0) {\n const s = sum(t);\n if (hasTimedOutDescendant(t)) return \"warning\";\n if (s.failed > 0) return \"danger\";\n if (s.warned > 0) return \"warning\";\n if (s.running > 0) return \"info\";\n if (s.pending > 0) return \"neutral\";\n return \"success\";\n }\n return \"neutral\";\n}\n\n// --- formatting ----------------------------------------------------------\n\n/**\n * Format a nanosecond duration as \"123ms\" or \"1.2s\". Empty for non-positive.\n * Named distinctly from clicky's ms/s `formatDuration` (lib/format) since the\n * test-runner wire format is nanoseconds.\n */\nexport function formatTestDuration(ns: number): string {\n if (ns <= 0) return \"\";\n const ms = ns / 1e6;\n if (ms < 1000) return `${ms.toFixed(0)}ms`;\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction compactWithUnit(value: number, unit: string): string {\n const rounded =\n Math.abs(value) >= 10 ? Math.round(value).toString() : value.toFixed(1).replace(/\\.0$/, \"\");\n return `${rounded}${unit}`;\n}\n\n/** Compact integer counts: <1000 raw, then \"k\", then \"M\". */\nexport function formatCount(n: number): string {\n const abs = Math.abs(n);\n if (abs < 1000) return String(n);\n if (abs < 1_000_000) return compactWithUnit(n / 1000, \"k\");\n return compactWithUnit(n / 1_000_000, \"M\");\n}\n\n/** Strip Go's `Test` prefix and split CamelCase / subtest names into words. */\nexport function humanizeName(name: string, framework?: string): string {\n if (!name) return \"\";\n if (framework !== \"go test\") return name;\n if (name.endsWith(\"/\")) return name;\n return name\n .split(\"/\")\n .map((p, i) => {\n if (i === 0) {\n return p.replace(/^Test/, \"\").replace(/([a-z0-9])([A-Z])/g, \"$1 $2\");\n }\n return p.replace(/_/g, \" \");\n })\n .join(\" / \");\n}\n\n// --- tree transforms -----------------------------------------------------\n\n/** Unique non-task frameworks present in the forest, sorted. */\nexport function collectFrameworks(tests: Test[]): string[] {\n const set = new Set<string>();\n const walk = (t: Test) => {\n if (t.framework && t.framework !== \"task\") set.add(t.framework);\n (t.children || []).forEach(walk);\n };\n tests.forEach(walk);\n return Array.from(set).sort();\n}\n\nfunction joinChainNames(parent: string | undefined, child: string | undefined): string {\n const p = (parent ?? \"\").trim();\n const c = (child ?? \"\").trim();\n if (!p) return c;\n if (!c) return p;\n return `${p} > ${c}`;\n}\n\nfunction isCollapsibleContainer(t: Test): boolean {\n return !(t.passed || t.failed || t.skipped || t.pending);\n}\n\n// Re-deriving a collapsed/filtered node invalidates any cached summary; drop it\n// so callers re-roll-up from the surviving children.\nfunction withoutSummary(t: Test): Omit<Test, \"summary\"> & { summary?: never } {\n const { summary: _summary, ...rest } = t;\n return rest;\n}\n\nfunction collapseNode(t: Test): Test {\n const children = t.children ?? [];\n if (children.length === 0) return t;\n\n const collapsedChildren = children.map(collapseNode);\n\n const onlyChild = collapsedChildren.length === 1 ? collapsedChildren[0] : undefined;\n if (onlyChild && isCollapsibleContainer(t)) {\n return { ...withoutSummary(onlyChild), name: joinChainNames(t.name, onlyChild.name) };\n }\n\n return { ...t, children: collapsedChildren };\n}\n\n/**\n * Compress any status-less container with exactly one child into that child,\n * joining names with \" > \". Recursive, so chains collapse into a single row.\n */\nexport function collapseSingleChildChains(tests: Test[]): Test[] {\n return tests.map(collapseNode);\n}\n\nfunction matchesLeaf(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): boolean {\n const s = testStatus(t);\n if (s === null) return false;\n return matchesFilterState(s, statusFilter) && matchesFilterState(t.framework, frameworkFilter);\n}\n\nfunction filterNode(\n t: Test,\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test | null {\n if (t.children && t.children.length > 0) {\n const filtered = t.children\n .map((c) => filterNode(c, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n if (filtered.length === 0) return null;\n return { ...withoutSummary(t), children: filtered };\n }\n return matchesLeaf(t, statusFilter, frameworkFilter) ? t : null;\n}\n\n/** Prune leaves not matching the status/framework filters; drop empty branches. */\nexport function filterTests(\n tests: Test[],\n statusFilter: FilterState<string>,\n frameworkFilter: FilterState<string>,\n): Test[] {\n if (statusFilter.size === 0 && frameworkFilter.size === 0) return tests;\n return tests\n .map((t) => filterNode(t, statusFilter, frameworkFilter))\n .filter(Boolean) as Test[];\n}\n"],"names":["r"],"mappings":"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"}
|
|
@@ -78,6 +78,12 @@ export interface TestSummary {
|
|
|
78
78
|
Skipped: number;
|
|
79
79
|
Pending: number;
|
|
80
80
|
Running?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Timed-out count. Optional because gavel's Go `TestSummary`
|
|
83
|
+
* (testrunner/parsers) has no matching field yet — a projection that never
|
|
84
|
+
* observes a timed-out node simply omits the key.
|
|
85
|
+
*/
|
|
86
|
+
Timedout?: number;
|
|
81
87
|
Duration: number;
|
|
82
88
|
}
|
|
83
89
|
export interface Snapshot {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
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 utils = require("../../lib/utils.cjs");
|
|
6
|
+
const adapters = require("./adapters.cjs");
|
|
7
|
+
const verifyReportTests = require("./verifyReportTests.cjs");
|
|
8
|
+
const filterState = require("../test-runner/filterState.cjs");
|
|
9
|
+
const status = require("../test-runner/status.cjs");
|
|
10
|
+
const TestRunner = require("../test-runner/TestRunner.cjs");
|
|
11
|
+
const DEFAULT_ADAPTERS = adapters.verificationAdapters();
|
|
12
|
+
function selectionKey(node) {
|
|
13
|
+
return node.task_id ?? node.name;
|
|
14
|
+
}
|
|
15
|
+
function findBySelectionKey(nodes, key) {
|
|
16
|
+
for (const node of nodes) {
|
|
17
|
+
if (selectionKey(node) === key) return node;
|
|
18
|
+
if (node.children) {
|
|
19
|
+
const found = findBySelectionKey(node.children, key);
|
|
20
|
+
if (found) return found;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function VerificationResults({
|
|
26
|
+
report,
|
|
27
|
+
done,
|
|
28
|
+
title = null,
|
|
29
|
+
className,
|
|
30
|
+
adapters: adapters2 = DEFAULT_ADAPTERS,
|
|
31
|
+
emptyText = "No verification has run yet",
|
|
32
|
+
selected: controlledSelected,
|
|
33
|
+
onSelect: controlledOnSelect,
|
|
34
|
+
filters: controlledFilters,
|
|
35
|
+
onFiltersChange: controlledOnFiltersChange
|
|
36
|
+
}) {
|
|
37
|
+
const tests = react.useMemo(() => report ? verifyReportTests.verifyReportTests(report) : [], [report]);
|
|
38
|
+
const [selectedKey, setSelectedKey] = react.useState(null);
|
|
39
|
+
const [internalFilters, setInternalFilters] = react.useState(() => filterState.emptyTestFilters());
|
|
40
|
+
const [expandAll, setExpandAll] = react.useState(null);
|
|
41
|
+
const filtersControlled = controlledFilters !== void 0;
|
|
42
|
+
const filters = filtersControlled ? controlledFilters : internalFilters;
|
|
43
|
+
const setFilters = (next) => {
|
|
44
|
+
if (filtersControlled) controlledOnFiltersChange == null ? void 0 : controlledOnFiltersChange(next);
|
|
45
|
+
else setInternalFilters(next);
|
|
46
|
+
};
|
|
47
|
+
const selectionControlled = controlledSelected !== void 0;
|
|
48
|
+
const uncontrolledSelected = react.useMemo(
|
|
49
|
+
() => selectedKey ? findBySelectionKey(tests, selectedKey) : null,
|
|
50
|
+
[tests, selectedKey]
|
|
51
|
+
);
|
|
52
|
+
const selected = selectionControlled ? controlledSelected : uncontrolledSelected;
|
|
53
|
+
const setSelected = (node) => {
|
|
54
|
+
if (selectionControlled) controlledOnSelect == null ? void 0 : controlledOnSelect(node);
|
|
55
|
+
else setSelectedKey(node ? selectionKey(node) : null);
|
|
56
|
+
};
|
|
57
|
+
const visible = react.useMemo(
|
|
58
|
+
() => status.filterTests(tests, filters.status, filters.framework),
|
|
59
|
+
[tests, filters]
|
|
60
|
+
);
|
|
61
|
+
const startTime = (report == null ? void 0 : report.started_at) ? Date.parse(report.started_at) : void 0;
|
|
62
|
+
const endTime = (report == null ? void 0 : report.finished_at) ? Date.parse(report.finished_at) : void 0;
|
|
63
|
+
const verdict = report && (report.reason || report.feedback) ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 space-y-2 border-b border-border px-3 py-2", children: [
|
|
64
|
+
report.reason && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-foreground", children: report.reason }),
|
|
65
|
+
report.feedback && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap rounded bg-muted p-2 font-mono text-[11px]", children: report.feedback })
|
|
66
|
+
] }) : null;
|
|
67
|
+
if (!report || tests.length === 0) {
|
|
68
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex flex-col", className), children: [
|
|
69
|
+
verdict,
|
|
70
|
+
!verdict && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 py-4 text-xs text-muted-foreground", children: emptyText })
|
|
71
|
+
] });
|
|
72
|
+
}
|
|
73
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex h-[30rem] min-h-80 flex-col", className), children: [
|
|
74
|
+
verdict,
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
76
|
+
TestRunner.TestRunner,
|
|
77
|
+
{
|
|
78
|
+
className: "min-h-0 flex-1",
|
|
79
|
+
tests: visible,
|
|
80
|
+
allTests: tests,
|
|
81
|
+
selected,
|
|
82
|
+
filters,
|
|
83
|
+
expandAll,
|
|
84
|
+
done: done ?? report.state !== "running",
|
|
85
|
+
status: { running: report.state === "running" },
|
|
86
|
+
...report.state === "running" ? { statusText: "Running verification…" } : {},
|
|
87
|
+
...startTime !== void 0 ? { startTime } : {},
|
|
88
|
+
...endTime !== void 0 ? { endTime } : {},
|
|
89
|
+
title,
|
|
90
|
+
adapters: adapters2,
|
|
91
|
+
onSelect: setSelected,
|
|
92
|
+
onFiltersChange: setFilters,
|
|
93
|
+
onExpandAllChange: setExpandAll
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
] });
|
|
97
|
+
}
|
|
98
|
+
exports.VerificationResults = VerificationResults;
|
|
99
|
+
//# sourceMappingURL=VerificationResults.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VerificationResults.cjs","sources":["../../../src/data/verification/VerificationResults.tsx"],"sourcesContent":["// Props-only VerificationResults: renders a captain VerifyReport through the\n// shared TestRunner, so both the captain webapp and gavel pr/ui can drop it in\n// without forking a data-fetching or routing dependency. Ported from gavel's\n// VerificationFixtureResults.tsx.\n\nimport { useMemo, useState, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport {\n emptyTestFilters,\n filterTests,\n TestRunner,\n type Test,\n type TestFilters,\n type TestNodeAdapterRegistry,\n} from \"../test-runner\";\nimport { verificationAdapters } from \"./adapters\";\nimport { verifyReportTests } from \"./verifyReportTests\";\nimport type { VerifyReport } from \"./verify-report\";\n\nexport type VerificationResultsProps = {\n report: VerifyReport | null;\n /** Overrides the run-complete state. Defaults to `report.state !== \"running\"`. */\n done?: boolean;\n /** Header title, passed through to TestRunner. Defaults to no title. */\n title?: ReactNode | null;\n className?: string;\n adapters?: TestNodeAdapterRegistry;\n emptyText?: string;\n /**\n * Controlled selection — omit to let the component own selection state\n * (keyed by a stable task_id/name so the same row stays selected across a\n * re-created report of the same shape, e.g. after a live-update refresh).\n * Pass both this and `onSelect` for a host that persists selection at the\n * route level.\n */\n selected?: Test | null;\n onSelect?: (node: Test | null) => void;\n /** Controlled status/framework filters — omit to let the component own filter state. */\n filters?: TestFilters;\n onFiltersChange?: (next: TestFilters) => void;\n};\n\nconst DEFAULT_ADAPTERS = verificationAdapters();\n\n/**\n * A node's identity across a re-created forest: its own `task_id` when\n * present, else its name. Object identity can't be used — `verifyReportTests`\n * builds a brand-new `Test[]` from every report, so the previously-selected\n * object never appears in a fresh forest even when the underlying node is\n * unchanged.\n */\nfunction selectionKey(node: Test): string {\n return node.task_id ?? node.name;\n}\n\nfunction findBySelectionKey(nodes: Test[], key: string): Test | null {\n for (const node of nodes) {\n if (selectionKey(node) === key) return node;\n if (node.children) {\n const found = findBySelectionKey(node.children, key);\n if (found) return found;\n }\n }\n return null;\n}\n\nexport function VerificationResults({\n report,\n done,\n title = null,\n className,\n adapters = DEFAULT_ADAPTERS,\n emptyText = \"No verification has run yet\",\n selected: controlledSelected,\n onSelect: controlledOnSelect,\n filters: controlledFilters,\n onFiltersChange: controlledOnFiltersChange,\n}: VerificationResultsProps) {\n const tests = useMemo(() => (report ? verifyReportTests(report) : []), [report]);\n\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n const [internalFilters, setInternalFilters] = useState<TestFilters>(() => emptyTestFilters());\n const [expandAll, setExpandAll] = useState<boolean | null>(null);\n\n const filtersControlled = controlledFilters !== undefined;\n const filters = filtersControlled ? controlledFilters : internalFilters;\n const setFilters = (next: TestFilters) => {\n if (filtersControlled) controlledOnFiltersChange?.(next);\n else setInternalFilters(next);\n };\n\n const selectionControlled = controlledSelected !== undefined;\n const uncontrolledSelected = useMemo(\n () => (selectedKey ? findBySelectionKey(tests, selectedKey) : null),\n [tests, selectedKey],\n );\n const selected = selectionControlled ? controlledSelected : uncontrolledSelected;\n const setSelected = (node: Test | null) => {\n if (selectionControlled) controlledOnSelect?.(node);\n else setSelectedKey(node ? selectionKey(node) : null);\n };\n\n const visible = useMemo(\n () => filterTests(tests, filters.status, filters.framework),\n [tests, filters],\n );\n\n const startTime = report?.started_at ? Date.parse(report.started_at) : undefined;\n const endTime = report?.finished_at ? Date.parse(report.finished_at) : undefined;\n\n // A cmd/AI verifier's reason and feedback are the only place its verdict\n // survives once its nodes are empty (e.g. an error before any check ran), so\n // they're shown above the runner whenever present — not only in that empty\n // case — since a partial pass can carry a reason alongside real test nodes.\n const verdict =\n report && (report.reason || report.feedback) ? (\n <div className=\"shrink-0 space-y-2 border-b border-border px-3 py-2\">\n {report.reason && <p className=\"text-xs text-foreground\">{report.reason}</p>}\n {report.feedback && (\n <pre className=\"max-h-40 overflow-auto whitespace-pre-wrap rounded bg-muted p-2 font-mono text-[11px]\">\n {report.feedback}\n </pre>\n )}\n </div>\n ) : null;\n\n if (!report || tests.length === 0) {\n return (\n <div className={cn(\"flex flex-col\", className)}>\n {verdict}\n {!verdict && <p className=\"px-3 py-4 text-xs text-muted-foreground\">{emptyText}</p>}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex h-[30rem] min-h-80 flex-col\", className)}>\n {verdict}\n <TestRunner\n className=\"min-h-0 flex-1\"\n tests={visible}\n allTests={tests}\n selected={selected}\n filters={filters}\n expandAll={expandAll}\n done={done ?? report.state !== \"running\"}\n status={{ running: report.state === \"running\" }}\n {...(report.state === \"running\" ? { statusText: \"Running verification…\" } : {})}\n {...(startTime !== undefined ? { startTime } : {})}\n {...(endTime !== undefined ? { endTime } : {})}\n title={title}\n adapters={adapters}\n onSelect={setSelected}\n onFiltersChange={setFilters}\n onExpandAllChange={setExpandAll}\n />\n </div>\n );\n}\n"],"names":["verificationAdapters","adapters","useMemo","verifyReportTests","useState","emptyTestFilters","filterTests","jsxs","jsx","cn","TestRunner"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ReactNode } from '../../../../../node_modules/@types/react/index.d.ts';
|
|
2
|
+
import { Test, TestFilters, TestNodeAdapterRegistry } from '../test-runner';
|
|
3
|
+
import { VerifyReport } from './verify-report';
|
|
4
|
+
export type VerificationResultsProps = {
|
|
5
|
+
report: VerifyReport | null;
|
|
6
|
+
/** Overrides the run-complete state. Defaults to `report.state !== "running"`. */
|
|
7
|
+
done?: boolean;
|
|
8
|
+
/** Header title, passed through to TestRunner. Defaults to no title. */
|
|
9
|
+
title?: ReactNode | null;
|
|
10
|
+
className?: string;
|
|
11
|
+
adapters?: TestNodeAdapterRegistry;
|
|
12
|
+
emptyText?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Controlled selection — omit to let the component own selection state
|
|
15
|
+
* (keyed by a stable task_id/name so the same row stays selected across a
|
|
16
|
+
* re-created report of the same shape, e.g. after a live-update refresh).
|
|
17
|
+
* Pass both this and `onSelect` for a host that persists selection at the
|
|
18
|
+
* route level.
|
|
19
|
+
*/
|
|
20
|
+
selected?: Test | null;
|
|
21
|
+
onSelect?: (node: Test | null) => void;
|
|
22
|
+
/** Controlled status/framework filters — omit to let the component own filter state. */
|
|
23
|
+
filters?: TestFilters;
|
|
24
|
+
onFiltersChange?: (next: TestFilters) => void;
|
|
25
|
+
};
|
|
26
|
+
export declare function VerificationResults({ report, done, title, className, adapters, emptyText, selected: controlledSelected, onSelect: controlledOnSelect, filters: controlledFilters, onFiltersChange: controlledOnFiltersChange, }: VerificationResultsProps): import("react").JSX.Element;
|
|
27
|
+
//# sourceMappingURL=VerificationResults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VerificationResults.d.ts","sourceRoot":"","sources":["../../../src/data/verification/VerificationResults.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1D,OAAO,EAIL,KAAK,IAAI,EACT,KAAK,WAAW,EAChB,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,kFAAkF;IAClF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wEAAwE;IACxE,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IACvC,wFAAwF;IACxF,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CAC/C,CAAC;AA0BF,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,IAAI,EACJ,KAAY,EACZ,SAAS,EACT,QAA2B,EAC3B,SAAyC,EACzC,QAAQ,EAAE,kBAAkB,EAC5B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,iBAAiB,EAC1B,eAAe,EAAE,yBAAyB,GAC3C,EAAE,wBAAwB,+BAiF1B"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from "react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { verificationAdapters } from "./adapters.js";
|
|
5
|
+
import { verifyReportTests } from "./verifyReportTests.js";
|
|
6
|
+
import { emptyTestFilters } from "../test-runner/filterState.js";
|
|
7
|
+
import { filterTests } from "../test-runner/status.js";
|
|
8
|
+
import { TestRunner } from "../test-runner/TestRunner.js";
|
|
9
|
+
const DEFAULT_ADAPTERS = verificationAdapters();
|
|
10
|
+
function selectionKey(node) {
|
|
11
|
+
return node.task_id ?? node.name;
|
|
12
|
+
}
|
|
13
|
+
function findBySelectionKey(nodes, key) {
|
|
14
|
+
for (const node of nodes) {
|
|
15
|
+
if (selectionKey(node) === key) return node;
|
|
16
|
+
if (node.children) {
|
|
17
|
+
const found = findBySelectionKey(node.children, key);
|
|
18
|
+
if (found) return found;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function VerificationResults({
|
|
24
|
+
report,
|
|
25
|
+
done,
|
|
26
|
+
title = null,
|
|
27
|
+
className,
|
|
28
|
+
adapters = DEFAULT_ADAPTERS,
|
|
29
|
+
emptyText = "No verification has run yet",
|
|
30
|
+
selected: controlledSelected,
|
|
31
|
+
onSelect: controlledOnSelect,
|
|
32
|
+
filters: controlledFilters,
|
|
33
|
+
onFiltersChange: controlledOnFiltersChange
|
|
34
|
+
}) {
|
|
35
|
+
const tests = useMemo(() => report ? verifyReportTests(report) : [], [report]);
|
|
36
|
+
const [selectedKey, setSelectedKey] = useState(null);
|
|
37
|
+
const [internalFilters, setInternalFilters] = useState(() => emptyTestFilters());
|
|
38
|
+
const [expandAll, setExpandAll] = useState(null);
|
|
39
|
+
const filtersControlled = controlledFilters !== void 0;
|
|
40
|
+
const filters = filtersControlled ? controlledFilters : internalFilters;
|
|
41
|
+
const setFilters = (next) => {
|
|
42
|
+
if (filtersControlled) controlledOnFiltersChange == null ? void 0 : controlledOnFiltersChange(next);
|
|
43
|
+
else setInternalFilters(next);
|
|
44
|
+
};
|
|
45
|
+
const selectionControlled = controlledSelected !== void 0;
|
|
46
|
+
const uncontrolledSelected = useMemo(
|
|
47
|
+
() => selectedKey ? findBySelectionKey(tests, selectedKey) : null,
|
|
48
|
+
[tests, selectedKey]
|
|
49
|
+
);
|
|
50
|
+
const selected = selectionControlled ? controlledSelected : uncontrolledSelected;
|
|
51
|
+
const setSelected = (node) => {
|
|
52
|
+
if (selectionControlled) controlledOnSelect == null ? void 0 : controlledOnSelect(node);
|
|
53
|
+
else setSelectedKey(node ? selectionKey(node) : null);
|
|
54
|
+
};
|
|
55
|
+
const visible = useMemo(
|
|
56
|
+
() => filterTests(tests, filters.status, filters.framework),
|
|
57
|
+
[tests, filters]
|
|
58
|
+
);
|
|
59
|
+
const startTime = (report == null ? void 0 : report.started_at) ? Date.parse(report.started_at) : void 0;
|
|
60
|
+
const endTime = (report == null ? void 0 : report.finished_at) ? Date.parse(report.finished_at) : void 0;
|
|
61
|
+
const verdict = report && (report.reason || report.feedback) ? /* @__PURE__ */ jsxs("div", { className: "shrink-0 space-y-2 border-b border-border px-3 py-2", children: [
|
|
62
|
+
report.reason && /* @__PURE__ */ jsx("p", { className: "text-xs text-foreground", children: report.reason }),
|
|
63
|
+
report.feedback && /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap rounded bg-muted p-2 font-mono text-[11px]", children: report.feedback })
|
|
64
|
+
] }) : null;
|
|
65
|
+
if (!report || tests.length === 0) {
|
|
66
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
|
|
67
|
+
verdict,
|
|
68
|
+
!verdict && /* @__PURE__ */ jsx("p", { className: "px-3 py-4 text-xs text-muted-foreground", children: emptyText })
|
|
69
|
+
] });
|
|
70
|
+
}
|
|
71
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex h-[30rem] min-h-80 flex-col", className), children: [
|
|
72
|
+
verdict,
|
|
73
|
+
/* @__PURE__ */ jsx(
|
|
74
|
+
TestRunner,
|
|
75
|
+
{
|
|
76
|
+
className: "min-h-0 flex-1",
|
|
77
|
+
tests: visible,
|
|
78
|
+
allTests: tests,
|
|
79
|
+
selected,
|
|
80
|
+
filters,
|
|
81
|
+
expandAll,
|
|
82
|
+
done: done ?? report.state !== "running",
|
|
83
|
+
status: { running: report.state === "running" },
|
|
84
|
+
...report.state === "running" ? { statusText: "Running verification…" } : {},
|
|
85
|
+
...startTime !== void 0 ? { startTime } : {},
|
|
86
|
+
...endTime !== void 0 ? { endTime } : {},
|
|
87
|
+
title,
|
|
88
|
+
adapters,
|
|
89
|
+
onSelect: setSelected,
|
|
90
|
+
onFiltersChange: setFilters,
|
|
91
|
+
onExpandAllChange: setExpandAll
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
] });
|
|
95
|
+
}
|
|
96
|
+
export {
|
|
97
|
+
VerificationResults
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=VerificationResults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VerificationResults.js","sources":["../../../src/data/verification/VerificationResults.tsx"],"sourcesContent":["// Props-only VerificationResults: renders a captain VerifyReport through the\n// shared TestRunner, so both the captain webapp and gavel pr/ui can drop it in\n// without forking a data-fetching or routing dependency. Ported from gavel's\n// VerificationFixtureResults.tsx.\n\nimport { useMemo, useState, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport {\n emptyTestFilters,\n filterTests,\n TestRunner,\n type Test,\n type TestFilters,\n type TestNodeAdapterRegistry,\n} from \"../test-runner\";\nimport { verificationAdapters } from \"./adapters\";\nimport { verifyReportTests } from \"./verifyReportTests\";\nimport type { VerifyReport } from \"./verify-report\";\n\nexport type VerificationResultsProps = {\n report: VerifyReport | null;\n /** Overrides the run-complete state. Defaults to `report.state !== \"running\"`. */\n done?: boolean;\n /** Header title, passed through to TestRunner. Defaults to no title. */\n title?: ReactNode | null;\n className?: string;\n adapters?: TestNodeAdapterRegistry;\n emptyText?: string;\n /**\n * Controlled selection — omit to let the component own selection state\n * (keyed by a stable task_id/name so the same row stays selected across a\n * re-created report of the same shape, e.g. after a live-update refresh).\n * Pass both this and `onSelect` for a host that persists selection at the\n * route level.\n */\n selected?: Test | null;\n onSelect?: (node: Test | null) => void;\n /** Controlled status/framework filters — omit to let the component own filter state. */\n filters?: TestFilters;\n onFiltersChange?: (next: TestFilters) => void;\n};\n\nconst DEFAULT_ADAPTERS = verificationAdapters();\n\n/**\n * A node's identity across a re-created forest: its own `task_id` when\n * present, else its name. Object identity can't be used — `verifyReportTests`\n * builds a brand-new `Test[]` from every report, so the previously-selected\n * object never appears in a fresh forest even when the underlying node is\n * unchanged.\n */\nfunction selectionKey(node: Test): string {\n return node.task_id ?? node.name;\n}\n\nfunction findBySelectionKey(nodes: Test[], key: string): Test | null {\n for (const node of nodes) {\n if (selectionKey(node) === key) return node;\n if (node.children) {\n const found = findBySelectionKey(node.children, key);\n if (found) return found;\n }\n }\n return null;\n}\n\nexport function VerificationResults({\n report,\n done,\n title = null,\n className,\n adapters = DEFAULT_ADAPTERS,\n emptyText = \"No verification has run yet\",\n selected: controlledSelected,\n onSelect: controlledOnSelect,\n filters: controlledFilters,\n onFiltersChange: controlledOnFiltersChange,\n}: VerificationResultsProps) {\n const tests = useMemo(() => (report ? verifyReportTests(report) : []), [report]);\n\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n const [internalFilters, setInternalFilters] = useState<TestFilters>(() => emptyTestFilters());\n const [expandAll, setExpandAll] = useState<boolean | null>(null);\n\n const filtersControlled = controlledFilters !== undefined;\n const filters = filtersControlled ? controlledFilters : internalFilters;\n const setFilters = (next: TestFilters) => {\n if (filtersControlled) controlledOnFiltersChange?.(next);\n else setInternalFilters(next);\n };\n\n const selectionControlled = controlledSelected !== undefined;\n const uncontrolledSelected = useMemo(\n () => (selectedKey ? findBySelectionKey(tests, selectedKey) : null),\n [tests, selectedKey],\n );\n const selected = selectionControlled ? controlledSelected : uncontrolledSelected;\n const setSelected = (node: Test | null) => {\n if (selectionControlled) controlledOnSelect?.(node);\n else setSelectedKey(node ? selectionKey(node) : null);\n };\n\n const visible = useMemo(\n () => filterTests(tests, filters.status, filters.framework),\n [tests, filters],\n );\n\n const startTime = report?.started_at ? Date.parse(report.started_at) : undefined;\n const endTime = report?.finished_at ? Date.parse(report.finished_at) : undefined;\n\n // A cmd/AI verifier's reason and feedback are the only place its verdict\n // survives once its nodes are empty (e.g. an error before any check ran), so\n // they're shown above the runner whenever present — not only in that empty\n // case — since a partial pass can carry a reason alongside real test nodes.\n const verdict =\n report && (report.reason || report.feedback) ? (\n <div className=\"shrink-0 space-y-2 border-b border-border px-3 py-2\">\n {report.reason && <p className=\"text-xs text-foreground\">{report.reason}</p>}\n {report.feedback && (\n <pre className=\"max-h-40 overflow-auto whitespace-pre-wrap rounded bg-muted p-2 font-mono text-[11px]\">\n {report.feedback}\n </pre>\n )}\n </div>\n ) : null;\n\n if (!report || tests.length === 0) {\n return (\n <div className={cn(\"flex flex-col\", className)}>\n {verdict}\n {!verdict && <p className=\"px-3 py-4 text-xs text-muted-foreground\">{emptyText}</p>}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex h-[30rem] min-h-80 flex-col\", className)}>\n {verdict}\n <TestRunner\n className=\"min-h-0 flex-1\"\n tests={visible}\n allTests={tests}\n selected={selected}\n filters={filters}\n expandAll={expandAll}\n done={done ?? report.state !== \"running\"}\n status={{ running: report.state === \"running\" }}\n {...(report.state === \"running\" ? { statusText: \"Running verification…\" } : {})}\n {...(startTime !== undefined ? { startTime } : {})}\n {...(endTime !== undefined ? { endTime } : {})}\n title={title}\n adapters={adapters}\n onSelect={setSelected}\n onFiltersChange={setFilters}\n onExpandAllChange={setExpandAll}\n />\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|