@flanksource/clicky-ui 0.3.9 → 0.3.10
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/NOTICE.md +4 -0
- package/dist/data/test-runner/TestFilterBar.cjs +14 -9
- package/dist/data/test-runner/TestFilterBar.cjs.map +1 -1
- package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -1
- package/dist/data/test-runner/TestFilterBar.js +15 -10
- package/dist/data/test-runner/TestFilterBar.js.map +1 -1
- package/dist/data/test-runner/TestRunSummary.cjs +1 -11
- package/dist/data/test-runner/TestRunSummary.cjs.map +1 -1
- package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunSummary.js +2 -12
- package/dist/data/test-runner/TestRunSummary.js.map +1 -1
- package/dist/data/test-runner/TestRunner.cjs +3 -12
- package/dist/data/test-runner/TestRunner.cjs.map +1 -1
- package/dist/data/test-runner/TestRunner.d.ts +8 -1
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunner.js +4 -13
- package/dist/data/test-runner/TestRunner.js.map +1 -1
- package/dist/data/test-runner/filterState.cjs +8 -0
- package/dist/data/test-runner/filterState.cjs.map +1 -1
- package/dist/data/test-runner/filterState.d.ts +2 -0
- package/dist/data/test-runner/filterState.d.ts.map +1 -1
- package/dist/data/test-runner/filterState.js +9 -1
- package/dist/data/test-runner/filterState.js.map +1 -1
- package/dist/data/test-runner/index.d.ts +2 -2
- package/dist/data/test-runner/index.d.ts.map +1 -1
- package/dist/data/test-runner/status.cjs +16 -0
- package/dist/data/test-runner/status.cjs.map +1 -1
- package/dist/data/test-runner/status.d.ts +9 -0
- package/dist/data/test-runner/status.d.ts.map +1 -1
- package/dist/data/test-runner/status.js +16 -0
- package/dist/data/test-runner/status.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +27 -15
- package/dist/data.cjs.map +1 -1
- package/dist/data.js +14 -2
- package/dist/data.js.map +1 -1
- package/dist/icons/components/UiDebugContinue.cjs +10 -0
- package/dist/icons/components/UiDebugContinue.cjs.map +1 -0
- package/dist/icons/components/UiDebugContinue.d.ts +9 -0
- package/dist/icons/components/UiDebugContinue.d.ts.map +1 -0
- package/dist/icons/components/UiDebugContinue.js +10 -0
- package/dist/icons/components/UiDebugContinue.js.map +1 -0
- package/dist/icons/components/UiDebugStepInto.cjs +10 -0
- package/dist/icons/components/UiDebugStepInto.cjs.map +1 -0
- package/dist/icons/components/UiDebugStepInto.d.ts +9 -0
- package/dist/icons/components/UiDebugStepInto.d.ts.map +1 -0
- package/dist/icons/components/UiDebugStepInto.js +10 -0
- package/dist/icons/components/UiDebugStepInto.js.map +1 -0
- package/dist/icons/components/UiDebugStepOut.cjs +10 -0
- package/dist/icons/components/UiDebugStepOut.cjs.map +1 -0
- package/dist/icons/components/UiDebugStepOut.d.ts +9 -0
- package/dist/icons/components/UiDebugStepOut.d.ts.map +1 -0
- package/dist/icons/components/UiDebugStepOut.js +10 -0
- package/dist/icons/components/UiDebugStepOut.js.map +1 -0
- package/dist/icons/components/UiDebugStop.cjs +10 -0
- package/dist/icons/components/UiDebugStop.cjs.map +1 -0
- package/dist/icons/components/UiDebugStop.d.ts +9 -0
- package/dist/icons/components/UiDebugStop.d.ts.map +1 -0
- package/dist/icons/components/UiDebugStop.js +10 -0
- package/dist/icons/components/UiDebugStop.js.map +1 -0
- package/dist/icons/index.d.ts +4 -0
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons.cjs +8 -0
- package/dist/icons.cjs.map +1 -1
- package/dist/icons.js +8 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +27 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/NOTICE.md
CHANGED
|
@@ -58,6 +58,10 @@ of its upstream source.
|
|
|
58
58
|
| UiRestart | codicon:debug-restart |
|
|
59
59
|
| UiStackFrameDot | codicon:debug-stackframe-dot |
|
|
60
60
|
| UiDebugStepOver | codicon:debug-step-over |
|
|
61
|
+
| UiDebugContinue | codicon:debug-continue |
|
|
62
|
+
| UiDebugStepInto | codicon:debug-step-into |
|
|
63
|
+
| UiDebugStepOut | codicon:debug-step-out |
|
|
64
|
+
| UiDebugStop | codicon:debug-stop |
|
|
61
65
|
| UiPlayCircle | ph:play-circle-light |
|
|
62
66
|
| UiPlayCircleFilled | ph:play-circle-fill |
|
|
63
67
|
| UiResizeVertical | ph:arrows-in-line-vertical-light |
|
|
@@ -8,15 +8,20 @@ const status = require("./status.cjs");
|
|
|
8
8
|
const filterState = require("./filterState.cjs");
|
|
9
9
|
const UiAdd = require("../../icons/components/UiAdd.cjs");
|
|
10
10
|
const UiRemove = require("../../icons/components/UiRemove.cjs");
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
const STATUS_BADGES = {
|
|
12
|
+
failed: "bg-red-500",
|
|
13
|
+
warned: "bg-amber-400",
|
|
14
|
+
timedout: "bg-amber-500",
|
|
15
|
+
passed: "bg-green-500",
|
|
16
|
+
skipped: "bg-yellow-400",
|
|
17
|
+
running: "bg-blue-500",
|
|
18
|
+
pending: "bg-muted-foreground",
|
|
19
|
+
total: "bg-muted-foreground"
|
|
20
|
+
};
|
|
21
|
+
const STATUS_DEFS = status.STATUS_LABELS.map((d) => ({
|
|
22
|
+
...d,
|
|
23
|
+
badge: STATUS_BADGES[d.key]
|
|
24
|
+
}));
|
|
20
25
|
function pillClass(mode) {
|
|
21
26
|
if (mode === "include") return "bg-accent border-border font-medium text-foreground";
|
|
22
27
|
if (mode === "exclude") return "bg-foreground text-background border-foreground font-medium";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestFilterBar.cjs","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst
|
|
1
|
+
{"version":3,"file":"TestFilterBar.cjs","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, STATUS_LABELS, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst STATUS_BADGES: Record<keyof StatusCounts, string> = {\n failed: \"bg-red-500\",\n warned: \"bg-amber-400\",\n timedout: \"bg-amber-500\",\n passed: \"bg-green-500\",\n skipped: \"bg-yellow-400\",\n running: \"bg-blue-500\",\n pending: \"bg-muted-foreground\",\n total: \"bg-muted-foreground\",\n};\n\nconst STATUS_DEFS: StatusDef[] = STATUS_LABELS.map((d) => ({\n ...d,\n badge: STATUS_BADGES[d.key],\n}));\n\nfunction pillClass(mode: FilterMode | undefined): string {\n if (mode === \"include\") return \"bg-accent border-border font-medium text-foreground\";\n if (mode === \"exclude\") return \"bg-foreground text-background border-foreground font-medium\";\n return \"border-border text-muted-foreground hover:bg-accent\";\n}\n\nfunction StateMarker({ mode }: { mode: FilterMode | undefined }) {\n if (mode === \"include\") return <Icon icon={UiAdd} className=\"text-xs\" />;\n if (mode === \"exclude\") return <Icon icon={UiRemove} className=\"text-xs\" />;\n return <span className=\"h-2 w-2 rounded-full bg-current opacity-30\" aria-hidden />;\n}\n\n/**\n * Tri-state status + framework filter pills. Named TestFilterBar to avoid the\n * pre-existing generic `FilterBar` export in clicky-ui/components.\n */\nexport function TestFilterBar({\n filters,\n onChange,\n counts,\n frameworks,\n className,\n}: TestFilterBarProps) {\n const hasActive = filters.status.size > 0 || filters.framework.size > 0;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {STATUS_DEFS.map((sf) => {\n const count = counts[sf.key];\n if (count === 0) return null;\n const mode = filters.status.get(sf.key);\n return (\n <button\n key={sf.key}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() => onChange({ ...filters, status: cycleFilterState(filters.status, sf.key) })}\n title={`${sf.label}: ${mode ?? \"neutral\"}`}\n >\n <span\n className={cn(\n \"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-bold text-white\",\n sf.badge,\n )}\n >\n {formatCount(count)}\n </span>\n <StateMarker mode={mode} />\n {sf.label}\n </button>\n );\n })}\n\n {frameworks.length > 1 && (\n <>\n <span className=\"mx-0.5 text-border\">|</span>\n {frameworks.map((fw) => {\n const icon = frameworkIcon(fw);\n const mode = filters.framework.get(fw);\n return (\n <button\n key={fw}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() =>\n onChange({ ...filters, framework: cycleFilterState(filters.framework, fw) })\n }\n title={`${fw}: ${mode ?? \"neutral\"}`}\n >\n <StateMarker mode={mode} />\n {icon && <Icon icon={icon} className=\"text-sm\" />}\n {fw}\n </button>\n );\n })}\n </>\n )}\n\n {hasActive && (\n <button\n type=\"button\"\n className=\"ml-1 text-xs text-muted-foreground hover:text-foreground\"\n onClick={() => onChange({ status: new Map(), framework: new Map() })}\n >\n Clear\n </button>\n )}\n </div>\n );\n}\n"],"names":["STATUS_LABELS","jsx","Icon","UiAdd","UiRemove","cn","jsxs","cycleFilterState","Fragment","frameworkIcon"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestFilterBar.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"TestFilterBar.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAgCF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,SAAS,GACV,EAAE,kBAAkB,2CAyEpB"}
|
|
@@ -2,19 +2,24 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
|
2
2
|
import { Icon } from "../Icon.js";
|
|
3
3
|
import { cn } from "../../lib/utils.js";
|
|
4
4
|
import { frameworkIcon } from "./frameworkIcon.js";
|
|
5
|
-
import { formatCount } from "./status.js";
|
|
5
|
+
import { formatCount, STATUS_LABELS } from "./status.js";
|
|
6
6
|
import { cycleFilterState } from "./filterState.js";
|
|
7
7
|
import { UiAdd } from "../../icons/components/UiAdd.js";
|
|
8
8
|
import { UiRemove } from "../../icons/components/UiRemove.js";
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
const STATUS_BADGES = {
|
|
10
|
+
failed: "bg-red-500",
|
|
11
|
+
warned: "bg-amber-400",
|
|
12
|
+
timedout: "bg-amber-500",
|
|
13
|
+
passed: "bg-green-500",
|
|
14
|
+
skipped: "bg-yellow-400",
|
|
15
|
+
running: "bg-blue-500",
|
|
16
|
+
pending: "bg-muted-foreground",
|
|
17
|
+
total: "bg-muted-foreground"
|
|
18
|
+
};
|
|
19
|
+
const STATUS_DEFS = STATUS_LABELS.map((d) => ({
|
|
20
|
+
...d,
|
|
21
|
+
badge: STATUS_BADGES[d.key]
|
|
22
|
+
}));
|
|
18
23
|
function pillClass(mode) {
|
|
19
24
|
if (mode === "include") return "bg-accent border-border font-medium text-foreground";
|
|
20
25
|
if (mode === "exclude") return "bg-foreground text-background border-foreground font-medium";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestFilterBar.js","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst
|
|
1
|
+
{"version":3,"file":"TestFilterBar.js","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, STATUS_LABELS, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst STATUS_BADGES: Record<keyof StatusCounts, string> = {\n failed: \"bg-red-500\",\n warned: \"bg-amber-400\",\n timedout: \"bg-amber-500\",\n passed: \"bg-green-500\",\n skipped: \"bg-yellow-400\",\n running: \"bg-blue-500\",\n pending: \"bg-muted-foreground\",\n total: \"bg-muted-foreground\",\n};\n\nconst STATUS_DEFS: StatusDef[] = STATUS_LABELS.map((d) => ({\n ...d,\n badge: STATUS_BADGES[d.key],\n}));\n\nfunction pillClass(mode: FilterMode | undefined): string {\n if (mode === \"include\") return \"bg-accent border-border font-medium text-foreground\";\n if (mode === \"exclude\") return \"bg-foreground text-background border-foreground font-medium\";\n return \"border-border text-muted-foreground hover:bg-accent\";\n}\n\nfunction StateMarker({ mode }: { mode: FilterMode | undefined }) {\n if (mode === \"include\") return <Icon icon={UiAdd} className=\"text-xs\" />;\n if (mode === \"exclude\") return <Icon icon={UiRemove} className=\"text-xs\" />;\n return <span className=\"h-2 w-2 rounded-full bg-current opacity-30\" aria-hidden />;\n}\n\n/**\n * Tri-state status + framework filter pills. Named TestFilterBar to avoid the\n * pre-existing generic `FilterBar` export in clicky-ui/components.\n */\nexport function TestFilterBar({\n filters,\n onChange,\n counts,\n frameworks,\n className,\n}: TestFilterBarProps) {\n const hasActive = filters.status.size > 0 || filters.framework.size > 0;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {STATUS_DEFS.map((sf) => {\n const count = counts[sf.key];\n if (count === 0) return null;\n const mode = filters.status.get(sf.key);\n return (\n <button\n key={sf.key}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() => onChange({ ...filters, status: cycleFilterState(filters.status, sf.key) })}\n title={`${sf.label}: ${mode ?? \"neutral\"}`}\n >\n <span\n className={cn(\n \"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-bold text-white\",\n sf.badge,\n )}\n >\n {formatCount(count)}\n </span>\n <StateMarker mode={mode} />\n {sf.label}\n </button>\n );\n })}\n\n {frameworks.length > 1 && (\n <>\n <span className=\"mx-0.5 text-border\">|</span>\n {frameworks.map((fw) => {\n const icon = frameworkIcon(fw);\n const mode = filters.framework.get(fw);\n return (\n <button\n key={fw}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() =>\n onChange({ ...filters, framework: cycleFilterState(filters.framework, fw) })\n }\n title={`${fw}: ${mode ?? \"neutral\"}`}\n >\n <StateMarker mode={mode} />\n {icon && <Icon icon={icon} className=\"text-sm\" />}\n {fw}\n </button>\n );\n })}\n </>\n )}\n\n {hasActive && (\n <button\n type=\"button\"\n className=\"ml-1 text-xs text-muted-foreground hover:text-foreground\"\n onClick={() => onChange({ status: new Map(), framework: new Map() })}\n >\n Clear\n </button>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -8,16 +8,6 @@ const status = require("./status.cjs");
|
|
|
8
8
|
const UiClock = require("../../icons/components/UiClock.cjs");
|
|
9
9
|
const UiCheck = require("../../icons/components/UiCheck.cjs");
|
|
10
10
|
const UiLoader = require("../../icons/components/UiLoader.cjs");
|
|
11
|
-
function totals(tests) {
|
|
12
|
-
return tests.reduce(
|
|
13
|
-
(acc, t) => {
|
|
14
|
-
const s = status.sumNonTaskTests(t);
|
|
15
|
-
Object.keys(acc).forEach((k) => acc[k] += s[k]);
|
|
16
|
-
return acc;
|
|
17
|
-
},
|
|
18
|
-
{ total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
11
|
function pct(part, total) {
|
|
22
12
|
return total > 0 ? Math.round(part / total * 100) : 0;
|
|
23
13
|
}
|
|
@@ -31,7 +21,7 @@ function TestRunSummary({
|
|
|
31
21
|
compact = false,
|
|
32
22
|
className
|
|
33
23
|
}) {
|
|
34
|
-
const t =
|
|
24
|
+
const t = status.aggregateStatusCounts(tests);
|
|
35
25
|
const clock = now ?? 0;
|
|
36
26
|
const end = done && endTime ? endTime : clock;
|
|
37
27
|
const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1e3).toFixed(1)}s` : "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestRunSummary.cjs","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport {
|
|
1
|
+
{"version":3,"file":"TestRunSummary.cjs","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport { aggregateStatusCounts } from \"./status\";\n\nexport type TestRunSummaryProps = {\n tests: Test[];\n /** Run start, epoch ms (host-provided). */\n startTime?: number | null | undefined;\n /** Run end, epoch ms; falls back to `now` while running. */\n endTime?: number | null | undefined;\n done: boolean;\n /** Injected clock (epoch ms) so the component stays pure. */\n now?: number | undefined;\n runMeta?: RunMeta | undefined;\n /**\n * Single-line layout for embedding in a host header (e.g. a dialog header):\n * counts + a short inline progress bar on one row, instead of the default\n * two-line right-aligned column.\n */\n compact?: boolean | undefined;\n className?: string | undefined;\n};\n\nfunction pct(part: number, total: number): number {\n return total > 0 ? Math.round((part / total) * 100) : 0;\n}\n\n/**\n * Header summary: per-status counts, elapsed time, a stacked progress bar, and\n * pass/fail/pending gauge cards. Pure — elapsed time comes from injected `now`.\n */\nexport function TestRunSummary({\n tests,\n startTime,\n endTime,\n done,\n now,\n runMeta,\n compact = false,\n className,\n}: TestRunSummaryProps) {\n const t = aggregateStatusCounts(tests);\n const clock = now ?? 0;\n const end = done && endTime ? endTime : clock;\n const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1000).toFixed(1)}s` : \"\";\n\n const segments: ProgressSegment[] = [\n { count: t.passed, color: \"bg-green-500\", label: \"passed\" },\n { count: t.skipped, color: \"bg-yellow-400\", label: \"skipped\" },\n { count: t.failed, color: \"bg-red-500\", label: \"failed\" },\n { count: t.timedout, color: \"bg-amber-500\", label: \"timed out\" },\n { count: t.running, color: \"bg-blue-400\", label: \"running\" },\n { count: t.pending, color: \"bg-muted-foreground/40\", label: \"queued\" },\n ];\n\n // Each status appears once, as a coloured count chip — no separate gauge-card\n // row repeating the same numbers as percentages.\n const chips: { n: number; label: string; tone: string }[] = [\n { n: t.passed, label: \"passed\", tone: \"text-green-600 dark:text-green-400\" },\n { n: t.failed, label: \"failed\", tone: \"text-red-600 dark:text-red-400\" },\n { n: t.timedout, label: \"timed out\", tone: \"text-amber-600 dark:text-amber-400\" },\n { n: t.skipped, label: \"skipped\", tone: \"text-yellow-700 dark:text-yellow-500\" },\n { n: t.running, label: \"running\", tone: \"text-blue-600 dark:text-blue-400\" },\n { n: t.pending, label: \"queued\", tone: \"text-muted-foreground\" },\n ];\n\n if (compact) {\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm text-muted-foreground\",\n className,\n )}\n >\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {t.total > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <ProgressBar className=\"w-24\" segments={segments} total={t.total} height=\"h-1.5\" />\n <span className=\"text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex min-w-[18rem] flex-col items-end gap-1.5\", className)}>\n <div className=\"flex flex-wrap items-center justify-end gap-x-3 gap-y-0.5 text-sm text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n\n {t.total > 0 && (\n <div className=\"flex w-72 items-center gap-2\">\n <ProgressBar className=\"flex-1\" segments={segments} total={t.total} height=\"h-2\" />\n <span className=\"shrink-0 text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"names":["aggregateStatusCounts","jsxs","cn","jsx","Icon","UiClock","ProgressBar","UiCheck","UiLoader"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestRunSummary.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;
|
|
1
|
+
{"version":3,"file":"TestRunSummary.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAMF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,SAAS,EACT,OAAO,EACP,IAAI,EACJ,GAAG,EACH,OAAO,EACP,OAAe,EACf,SAAS,GACV,EAAE,mBAAmB,2CAyGrB"}
|
|
@@ -2,20 +2,10 @@ import { jsxs, jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { Icon } from "../Icon.js";
|
|
3
3
|
import { ProgressBar } from "../ProgressBar.js";
|
|
4
4
|
import { cn } from "../../lib/utils.js";
|
|
5
|
-
import {
|
|
5
|
+
import { aggregateStatusCounts } from "./status.js";
|
|
6
6
|
import { UiClock } from "../../icons/components/UiClock.js";
|
|
7
7
|
import { UiCheck } from "../../icons/components/UiCheck.js";
|
|
8
8
|
import { UiLoader } from "../../icons/components/UiLoader.js";
|
|
9
|
-
function totals(tests) {
|
|
10
|
-
return tests.reduce(
|
|
11
|
-
(acc, t) => {
|
|
12
|
-
const s = sumNonTaskTests(t);
|
|
13
|
-
Object.keys(acc).forEach((k) => acc[k] += s[k]);
|
|
14
|
-
return acc;
|
|
15
|
-
},
|
|
16
|
-
{ total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
9
|
function pct(part, total) {
|
|
20
10
|
return total > 0 ? Math.round(part / total * 100) : 0;
|
|
21
11
|
}
|
|
@@ -29,7 +19,7 @@ function TestRunSummary({
|
|
|
29
19
|
compact = false,
|
|
30
20
|
className
|
|
31
21
|
}) {
|
|
32
|
-
const t =
|
|
22
|
+
const t = aggregateStatusCounts(tests);
|
|
33
23
|
const clock = now ?? 0;
|
|
34
24
|
const end = done && endTime ? endTime : clock;
|
|
35
25
|
const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1e3).toFixed(1)}s` : "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestRunSummary.js","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport {
|
|
1
|
+
{"version":3,"file":"TestRunSummary.js","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport { aggregateStatusCounts } from \"./status\";\n\nexport type TestRunSummaryProps = {\n tests: Test[];\n /** Run start, epoch ms (host-provided). */\n startTime?: number | null | undefined;\n /** Run end, epoch ms; falls back to `now` while running. */\n endTime?: number | null | undefined;\n done: boolean;\n /** Injected clock (epoch ms) so the component stays pure. */\n now?: number | undefined;\n runMeta?: RunMeta | undefined;\n /**\n * Single-line layout for embedding in a host header (e.g. a dialog header):\n * counts + a short inline progress bar on one row, instead of the default\n * two-line right-aligned column.\n */\n compact?: boolean | undefined;\n className?: string | undefined;\n};\n\nfunction pct(part: number, total: number): number {\n return total > 0 ? Math.round((part / total) * 100) : 0;\n}\n\n/**\n * Header summary: per-status counts, elapsed time, a stacked progress bar, and\n * pass/fail/pending gauge cards. Pure — elapsed time comes from injected `now`.\n */\nexport function TestRunSummary({\n tests,\n startTime,\n endTime,\n done,\n now,\n runMeta,\n compact = false,\n className,\n}: TestRunSummaryProps) {\n const t = aggregateStatusCounts(tests);\n const clock = now ?? 0;\n const end = done && endTime ? endTime : clock;\n const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1000).toFixed(1)}s` : \"\";\n\n const segments: ProgressSegment[] = [\n { count: t.passed, color: \"bg-green-500\", label: \"passed\" },\n { count: t.skipped, color: \"bg-yellow-400\", label: \"skipped\" },\n { count: t.failed, color: \"bg-red-500\", label: \"failed\" },\n { count: t.timedout, color: \"bg-amber-500\", label: \"timed out\" },\n { count: t.running, color: \"bg-blue-400\", label: \"running\" },\n { count: t.pending, color: \"bg-muted-foreground/40\", label: \"queued\" },\n ];\n\n // Each status appears once, as a coloured count chip — no separate gauge-card\n // row repeating the same numbers as percentages.\n const chips: { n: number; label: string; tone: string }[] = [\n { n: t.passed, label: \"passed\", tone: \"text-green-600 dark:text-green-400\" },\n { n: t.failed, label: \"failed\", tone: \"text-red-600 dark:text-red-400\" },\n { n: t.timedout, label: \"timed out\", tone: \"text-amber-600 dark:text-amber-400\" },\n { n: t.skipped, label: \"skipped\", tone: \"text-yellow-700 dark:text-yellow-500\" },\n { n: t.running, label: \"running\", tone: \"text-blue-600 dark:text-blue-400\" },\n { n: t.pending, label: \"queued\", tone: \"text-muted-foreground\" },\n ];\n\n if (compact) {\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm text-muted-foreground\",\n className,\n )}\n >\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {t.total > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <ProgressBar className=\"w-24\" segments={segments} total={t.total} height=\"h-1.5\" />\n <span className=\"text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex min-w-[18rem] flex-col items-end gap-1.5\", className)}>\n <div className=\"flex flex-wrap items-center justify-end gap-x-3 gap-y-0.5 text-sm text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n\n {t.total > 0 && (\n <div className=\"flex w-72 items-center gap-2\">\n <ProgressBar className=\"flex-1\" segments={segments} total={t.total} height=\"h-2\" />\n <span className=\"shrink-0 text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -13,16 +13,6 @@ const TestDetailPanel = require("./TestDetailPanel.cjs");
|
|
|
13
13
|
const TestRunSummary = require("./TestRunSummary.cjs");
|
|
14
14
|
const TestFilterBar = require("./TestFilterBar.cjs");
|
|
15
15
|
const UiBeaker = require("../../icons/components/UiBeaker.cjs");
|
|
16
|
-
function counts(tests) {
|
|
17
|
-
return tests.reduce(
|
|
18
|
-
(acc, t) => {
|
|
19
|
-
const s = status.sumNonTaskTests(t);
|
|
20
|
-
Object.keys(acc).forEach((k) => acc[k] += s[k]);
|
|
21
|
-
return acc;
|
|
22
|
-
},
|
|
23
|
-
{ total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
16
|
function toRegistry(adapters) {
|
|
27
17
|
if (adapters instanceof adapter.TestNodeAdapterRegistry) return adapters;
|
|
28
18
|
return adapter.createTestRunnerRegistry(adapters ?? []);
|
|
@@ -49,6 +39,7 @@ function TestRunner({
|
|
|
49
39
|
title,
|
|
50
40
|
showSummary = true,
|
|
51
41
|
headerSlot,
|
|
42
|
+
hideHeader = false,
|
|
52
43
|
defaultSplit = 45,
|
|
53
44
|
startTime,
|
|
54
45
|
endTime,
|
|
@@ -96,10 +87,10 @@ function TestRunner({
|
|
|
96
87
|
]
|
|
97
88
|
);
|
|
98
89
|
const frameworks = react.useMemo(() => status.collectFrameworks(tests), [tests]);
|
|
99
|
-
const filterCounts = react.useMemo(() =>
|
|
90
|
+
const filterCounts = react.useMemo(() => status.aggregateStatusCounts(tests), [tests]);
|
|
100
91
|
const hasContent = tests.length > 0;
|
|
101
92
|
return /* @__PURE__ */ jsxRuntime.jsx(context.TestRunnerProvider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex h-full min-h-0 flex-col bg-background", className), children: [
|
|
102
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
|
|
93
|
+
!hideHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
|
|
103
94
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
104
95
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
105
96
|
title === void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("h1", { className: "inline-flex items-center gap-1.5 text-lg font-bold", children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestRunner.cjs","sources":["../../../src/data/test-runner/TestRunner.tsx"],"sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { SplitPane } from \"../../layout/SplitPane\";\nimport { cn } from \"../../lib/utils\";\nimport { UiBeaker } from \"../../icons\";\nimport type { RunMeta, SnapshotStatus, Test } from \"./types\";\nimport {
|
|
1
|
+
{"version":3,"file":"TestRunner.cjs","sources":["../../../src/data/test-runner/TestRunner.tsx"],"sourcesContent":["import { useMemo, type ReactNode } from \"react\";\nimport { Icon } from \"../Icon\";\nimport { SplitPane } from \"../../layout/SplitPane\";\nimport { cn } from \"../../lib/utils\";\nimport { UiBeaker } from \"../../icons\";\nimport type { RunMeta, SnapshotStatus, Test } from \"./types\";\nimport { 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(tests), [tests]);\n const filterCounts = useMemo(() => aggregateStatusCounts(tests), [tests]);\n const hasContent = tests.length > 0;\n\n return (\n <TestRunnerProvider value={ctx}>\n <div className={cn(\"flex h-full min-h-0 flex-col bg-background\", className)}>\n {!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={tests}\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":["TestNodeAdapterRegistry","createTestRunnerRegistry","status","useMemo","collectFrameworks","aggregateStatusCounts","jsx","TestRunnerProvider","jsxs","cn","Icon","UiBeaker","TestRunSummary","TestFilterBar","SplitPane","TestTree","TestDetailPanel"],"mappings":"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"}
|
|
@@ -42,6 +42,13 @@ export type TestRunnerProps = {
|
|
|
42
42
|
showSummary?: boolean;
|
|
43
43
|
/** Extra header content (e.g. export/download controls owned by the host). */
|
|
44
44
|
headerSlot?: ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* Suppresses the entire built-in header bar (title, summary, filter row).
|
|
47
|
+
* Defaults to false; pass true when a host renders its own combined header
|
|
48
|
+
* (e.g. a dialog header that already embeds `TestRunSummary` and filters) so
|
|
49
|
+
* the runner doesn't render a second bordered bar underneath it.
|
|
50
|
+
*/
|
|
51
|
+
hideHeader?: boolean;
|
|
45
52
|
/** Initial left-pane width percentage. */
|
|
46
53
|
defaultSplit?: number;
|
|
47
54
|
startTime?: number | null;
|
|
@@ -54,5 +61,5 @@ export type TestRunnerProps = {
|
|
|
54
61
|
* are exposed to sub-components through a single `TestRunnerContext`. Domain
|
|
55
62
|
* rendering is pluggable via node adapters; no fetch/SSE/routing lives here.
|
|
56
63
|
*/
|
|
57
|
-
export declare function TestRunner({ tests, selected, filters, expandAll, done, runMeta, status, statusText, now, busy, activeTab, onTabChange, onSelect, onFiltersChange, onExpandAllChange, onRerun, onStop, adapters, title, showSummary, headerSlot, defaultSplit, startTime, endTime, className, }: TestRunnerProps): import("react/jsx-runtime").JSX.Element;
|
|
64
|
+
export declare function TestRunner({ tests, selected, filters, expandAll, done, runMeta, status, statusText, now, busy, activeTab, onTabChange, onSelect, onFiltersChange, onExpandAllChange, onRerun, onStop, adapters, title, showSummary, headerSlot, hideHeader, defaultSplit, startTime, endTime, className, }: TestRunnerProps): import("react/jsx-runtime").JSX.Element;
|
|
58
65
|
//# sourceMappingURL=TestRunner.d.ts.map
|
|
@@ -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,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;
|
|
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,2CAwGjB"}
|
|
@@ -3,7 +3,7 @@ import { useMemo } from "react";
|
|
|
3
3
|
import { Icon } from "../Icon.js";
|
|
4
4
|
import { SplitPane } from "../../layout/SplitPane.js";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
|
-
import { collectFrameworks,
|
|
6
|
+
import { collectFrameworks, aggregateStatusCounts } from "./status.js";
|
|
7
7
|
import { TestNodeAdapterRegistry, createTestRunnerRegistry } from "./adapter.js";
|
|
8
8
|
import { TestRunnerProvider } from "./context.js";
|
|
9
9
|
import { TestTree } from "./TestTree.js";
|
|
@@ -11,16 +11,6 @@ import { TestDetailPanel } from "./TestDetailPanel.js";
|
|
|
11
11
|
import { TestRunSummary } from "./TestRunSummary.js";
|
|
12
12
|
import { TestFilterBar } from "./TestFilterBar.js";
|
|
13
13
|
import { UiBeaker } from "../../icons/components/UiBeaker.js";
|
|
14
|
-
function counts(tests) {
|
|
15
|
-
return tests.reduce(
|
|
16
|
-
(acc, t) => {
|
|
17
|
-
const s = sumNonTaskTests(t);
|
|
18
|
-
Object.keys(acc).forEach((k) => acc[k] += s[k]);
|
|
19
|
-
return acc;
|
|
20
|
-
},
|
|
21
|
-
{ total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
14
|
function toRegistry(adapters) {
|
|
25
15
|
if (adapters instanceof TestNodeAdapterRegistry) return adapters;
|
|
26
16
|
return createTestRunnerRegistry(adapters ?? []);
|
|
@@ -47,6 +37,7 @@ function TestRunner({
|
|
|
47
37
|
title,
|
|
48
38
|
showSummary = true,
|
|
49
39
|
headerSlot,
|
|
40
|
+
hideHeader = false,
|
|
50
41
|
defaultSplit = 45,
|
|
51
42
|
startTime,
|
|
52
43
|
endTime,
|
|
@@ -94,10 +85,10 @@ function TestRunner({
|
|
|
94
85
|
]
|
|
95
86
|
);
|
|
96
87
|
const frameworks = useMemo(() => collectFrameworks(tests), [tests]);
|
|
97
|
-
const filterCounts = useMemo(() =>
|
|
88
|
+
const filterCounts = useMemo(() => aggregateStatusCounts(tests), [tests]);
|
|
98
89
|
const hasContent = tests.length > 0;
|
|
99
90
|
return /* @__PURE__ */ jsx(TestRunnerProvider, { value: ctx, children: /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col bg-background", className), children: [
|
|
100
|
-
/* @__PURE__ */ jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
|
|
91
|
+
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
|
|
101
92
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
102
93
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
103
94
|
title === void 0 ? /* @__PURE__ */ jsxs("h1", { className: "inline-flex items-center gap-1.5 text-lg font-bold", children: [
|
|
@@ -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 {
|
|
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(tests), [tests]);\n const filterCounts = useMemo(() => aggregateStatusCounts(tests), [tests]);\n const hasContent = tests.length > 0;\n\n return (\n <TestRunnerProvider value={ctx}>\n <div className={cn(\"flex h-full min-h-0 flex-col bg-background\", className)}>\n {!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={tests}\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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"}
|
|
@@ -27,6 +27,12 @@ function cycleFilterState(state, key) {
|
|
|
27
27
|
}
|
|
28
28
|
return next;
|
|
29
29
|
}
|
|
30
|
+
function filterStateToRecord(state) {
|
|
31
|
+
return Object.fromEntries(state);
|
|
32
|
+
}
|
|
33
|
+
function recordToFilterState(record) {
|
|
34
|
+
return new Map(Object.entries(record));
|
|
35
|
+
}
|
|
30
36
|
function matchesFilterState(value, state) {
|
|
31
37
|
if (state.size === 0) return true;
|
|
32
38
|
const include = /* @__PURE__ */ new Set();
|
|
@@ -43,5 +49,7 @@ exports.cycleFilterState = cycleFilterState;
|
|
|
43
49
|
exports.decodeFilterState = decodeFilterState;
|
|
44
50
|
exports.emptyTestFilters = emptyTestFilters;
|
|
45
51
|
exports.encodeFilterState = encodeFilterState;
|
|
52
|
+
exports.filterStateToRecord = filterStateToRecord;
|
|
46
53
|
exports.matchesFilterState = matchesFilterState;
|
|
54
|
+
exports.recordToFilterState = recordToFilterState;
|
|
47
55
|
//# sourceMappingURL=filterState.cjs.map
|