@flanksource/clicky-ui 0.2.12 → 0.2.14
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/components/Combobox.cjs +47 -33
- package/dist/components/Combobox.cjs.map +1 -1
- package/dist/components/Combobox.d.ts +12 -0
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/Combobox.js +48 -34
- package/dist/components/Combobox.js.map +1 -1
- package/dist/components/IconButton.cjs +26 -0
- package/dist/components/IconButton.cjs.map +1 -0
- package/dist/components/IconButton.d.ts +25 -0
- package/dist/components/IconButton.d.ts.map +1 -0
- package/dist/components/IconButton.js +26 -0
- package/dist/components/IconButton.js.map +1 -0
- package/dist/components/SecretKeySelector.cjs +151 -0
- package/dist/components/SecretKeySelector.cjs.map +1 -0
- package/dist/components/SecretKeySelector.d.ts +51 -0
- package/dist/components/SecretKeySelector.d.ts.map +1 -0
- package/dist/components/SecretKeySelector.js +151 -0
- package/dist/components/SecretKeySelector.js.map +1 -0
- package/dist/components/WorkloadPicker.cjs +144 -0
- package/dist/components/WorkloadPicker.cjs.map +1 -0
- package/dist/components/WorkloadPicker.d.ts +52 -0
- package/dist/components/WorkloadPicker.d.ts.map +1 -0
- package/dist/components/WorkloadPicker.js +144 -0
- package/dist/components/WorkloadPicker.js.map +1 -0
- package/dist/components/icon-menu-picker.cjs +83 -112
- package/dist/components/icon-menu-picker.cjs.map +1 -1
- package/dist/components/icon-menu-picker.d.ts.map +1 -1
- package/dist/components/icon-menu-picker.js +85 -114
- package/dist/components/icon-menu-picker.js.map +1 -1
- package/dist/components.cjs +10 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +11 -1
- package/dist/components.js.map +1 -1
- package/dist/data/Clicky.cjs +1 -1
- package/dist/data/Clicky.cjs.map +1 -1
- package/dist/data/Clicky.js +1 -1
- package/dist/data/Clicky.js.map +1 -1
- package/dist/data/TimeseriesCoreBars.cjs +8 -1
- package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
- package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
- package/dist/data/TimeseriesCoreBars.js +8 -1
- package/dist/data/TimeseriesCoreBars.js.map +1 -1
- package/dist/data/Version.cjs +42 -0
- package/dist/data/Version.cjs.map +1 -0
- package/dist/data/Version.d.ts +36 -0
- package/dist/data/Version.d.ts.map +1 -0
- package/dist/data/Version.js +42 -0
- package/dist/data/Version.js.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.cjs +165 -0
- package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.d.ts +7 -0
- package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.js +165 -0
- package/dist/data/test-runner/TestDetailPanel.js.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.cjs +78 -0
- package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.d.ts +14 -0
- package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -0
- package/dist/data/test-runner/TestFailureDetail.js +78 -0
- package/dist/data/test-runner/TestFailureDetail.js.map +1 -0
- package/dist/data/test-runner/TestFilterBar.cjs +108 -0
- package/dist/data/test-runner/TestFilterBar.cjs.map +1 -0
- package/dist/data/test-runner/TestFilterBar.d.ts +15 -0
- package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -0
- package/dist/data/test-runner/TestFilterBar.js +108 -0
- package/dist/data/test-runner/TestFilterBar.js.map +1 -0
- package/dist/data/test-runner/TestRunSummary.cjs +81 -0
- package/dist/data/test-runner/TestRunSummary.cjs.map +1 -0
- package/dist/data/test-runner/TestRunSummary.d.ts +19 -0
- package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunSummary.js +81 -0
- package/dist/data/test-runner/TestRunSummary.js.map +1 -0
- package/dist/data/test-runner/TestRunner.cjs +143 -0
- package/dist/data/test-runner/TestRunner.cjs.map +1 -0
- package/dist/data/test-runner/TestRunner.d.ts +42 -0
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunner.fixtures.d.ts +26 -0
- package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -0
- package/dist/data/test-runner/TestRunner.js +143 -0
- package/dist/data/test-runner/TestRunner.js.map +1 -0
- package/dist/data/test-runner/TestTree.cjs +42 -0
- package/dist/data/test-runner/TestTree.cjs.map +1 -0
- package/dist/data/test-runner/TestTree.d.ts +9 -0
- package/dist/data/test-runner/TestTree.d.ts.map +1 -0
- package/dist/data/test-runner/TestTree.js +42 -0
- package/dist/data/test-runner/TestTree.js.map +1 -0
- package/dist/data/test-runner/TestTreeNode.cjs +99 -0
- package/dist/data/test-runner/TestTreeNode.cjs.map +1 -0
- package/dist/data/test-runner/TestTreeNode.d.ts +12 -0
- package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -0
- package/dist/data/test-runner/TestTreeNode.js +99 -0
- package/dist/data/test-runner/TestTreeNode.js.map +1 -0
- package/dist/data/test-runner/adapter.cjs +57 -0
- package/dist/data/test-runner/adapter.cjs.map +1 -0
- package/dist/data/test-runner/adapter.d.ts +60 -0
- package/dist/data/test-runner/adapter.d.ts.map +1 -0
- package/dist/data/test-runner/adapter.js +57 -0
- package/dist/data/test-runner/adapter.js.map +1 -0
- package/dist/data/test-runner/context.cjs +15 -0
- package/dist/data/test-runner/context.cjs.map +1 -0
- package/dist/data/test-runner/context.d.ts +40 -0
- package/dist/data/test-runner/context.d.ts.map +1 -0
- package/dist/data/test-runner/context.js +15 -0
- package/dist/data/test-runner/context.js.map +1 -0
- package/dist/data/test-runner/filterState.cjs +47 -0
- package/dist/data/test-runner/filterState.cjs.map +1 -0
- package/dist/data/test-runner/filterState.d.ts +14 -0
- package/dist/data/test-runner/filterState.d.ts.map +1 -0
- package/dist/data/test-runner/filterState.js +47 -0
- package/dist/data/test-runner/filterState.js.map +1 -0
- package/dist/data/test-runner/frameworkIcon.cjs +27 -0
- package/dist/data/test-runner/frameworkIcon.cjs.map +1 -0
- package/dist/data/test-runner/frameworkIcon.d.ts +3 -0
- package/dist/data/test-runner/frameworkIcon.d.ts.map +1 -0
- package/dist/data/test-runner/frameworkIcon.js +27 -0
- package/dist/data/test-runner/frameworkIcon.js.map +1 -0
- package/dist/data/test-runner/index.d.ts +14 -0
- package/dist/data/test-runner/index.d.ts.map +1 -0
- package/dist/data/test-runner/status.cjs +234 -0
- package/dist/data/test-runner/status.cjs.map +1 -0
- package/dist/data/test-runner/status.d.ts +52 -0
- package/dist/data/test-runner/status.d.ts.map +1 -0
- package/dist/data/test-runner/status.js +234 -0
- package/dist/data/test-runner/status.js.map +1 -0
- package/dist/data/test-runner/types.d.ts +148 -0
- package/dist/data/test-runner/types.d.ts.map +1 -0
- package/dist/data/version-info.cjs +26 -0
- package/dist/data/version-info.cjs.map +1 -0
- package/dist/data/version-info.d.ts +11 -0
- package/dist/data/version-info.d.ts.map +1 -0
- package/dist/data/version-info.js +26 -0
- package/dist/data/version-info.js.map +1 -0
- package/dist/data.cjs +49 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +3 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +50 -1
- package/dist/data.js.map +1 -1
- package/dist/index.cjs +59 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +60 -1
- package/dist/index.js.map +1 -1
- package/dist/overlay/DropdownMenu.cjs +57 -30
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.d.ts.map +1 -1
- package/dist/overlay/DropdownMenu.js +57 -30
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/overlay/Modal.cjs +62 -6
- package/dist/overlay/Modal.cjs.map +1 -1
- package/dist/overlay/Modal.d.ts +19 -1
- package/dist/overlay/Modal.d.ts.map +1 -1
- package/dist/overlay/Modal.js +62 -6
- package/dist/overlay/Modal.js.map +1 -1
- package/dist/rpc/CommandForm.cjs +2 -2
- package/dist/rpc/CommandForm.cjs.map +1 -1
- package/dist/rpc/CommandForm.d.ts.map +1 -1
- package/dist/rpc/CommandForm.js +3 -3
- package/dist/rpc/CommandForm.js.map +1 -1
- package/dist/rpc/formMetadata.cjs +5 -4
- package/dist/rpc/formMetadata.cjs.map +1 -1
- package/dist/rpc/formMetadata.d.ts.map +1 -1
- package/dist/rpc/formMetadata.js +6 -5
- package/dist/rpc/formMetadata.js.map +1 -1
- package/dist/rpc/types.cjs +4 -0
- package/dist/rpc/types.cjs.map +1 -1
- package/dist/rpc/types.d.ts +10 -0
- package/dist/rpc/types.d.ts.map +1 -1
- package/dist/rpc/types.js +5 -1
- package/dist/rpc/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const filterState = require("./filterState.cjs");
|
|
4
|
+
const UiHourglass = require("../../icons/components/UiHourglass.cjs");
|
|
5
|
+
const UiLoader = require("../../icons/components/UiLoader.cjs");
|
|
6
|
+
const UiPause = require("../../icons/components/UiPause.cjs");
|
|
7
|
+
const UiError = require("../../icons/components/UiError.cjs");
|
|
8
|
+
const UiWarningTriangle = require("../../icons/components/UiWarningTriangle.cjs");
|
|
9
|
+
const UiPass = require("../../icons/components/UiPass.cjs");
|
|
10
|
+
const UiClass = require("../../icons/components/UiClass.cjs");
|
|
11
|
+
const emptyCounts = () => ({
|
|
12
|
+
total: 0,
|
|
13
|
+
passed: 0,
|
|
14
|
+
failed: 0,
|
|
15
|
+
warned: 0,
|
|
16
|
+
skipped: 0,
|
|
17
|
+
pending: 0,
|
|
18
|
+
running: 0,
|
|
19
|
+
timedout: 0
|
|
20
|
+
});
|
|
21
|
+
const addCounts = (r, s) => {
|
|
22
|
+
r.total += s.total;
|
|
23
|
+
r.passed += s.passed;
|
|
24
|
+
r.failed += s.failed;
|
|
25
|
+
r.warned += s.warned;
|
|
26
|
+
r.skipped += s.skipped;
|
|
27
|
+
r.pending += s.pending;
|
|
28
|
+
r.running += s.running;
|
|
29
|
+
r.timedout += s.timedout;
|
|
30
|
+
};
|
|
31
|
+
const countsFromSummary = (summary) => ({
|
|
32
|
+
total: summary.Total,
|
|
33
|
+
passed: summary.Passed,
|
|
34
|
+
failed: summary.Failed,
|
|
35
|
+
warned: summary.Warned || 0,
|
|
36
|
+
skipped: summary.Skipped,
|
|
37
|
+
pending: summary.Pending || 0,
|
|
38
|
+
running: summary.Running || 0,
|
|
39
|
+
timedout: 0
|
|
40
|
+
});
|
|
41
|
+
const countsFromLeaf = (t) => {
|
|
42
|
+
const isTimedOut = !!t.timed_out;
|
|
43
|
+
const counted = isTimedOut || t.passed || t.failed || t.warned || t.skipped || t.pending || t.running;
|
|
44
|
+
return {
|
|
45
|
+
total: counted ? 1 : 0,
|
|
46
|
+
passed: !isTimedOut && t.passed ? 1 : 0,
|
|
47
|
+
failed: !isTimedOut && t.failed ? 1 : 0,
|
|
48
|
+
warned: !isTimedOut && t.warned ? 1 : 0,
|
|
49
|
+
skipped: !isTimedOut && t.skipped ? 1 : 0,
|
|
50
|
+
pending: !isTimedOut && t.pending ? 1 : 0,
|
|
51
|
+
running: !isTimedOut && t.running ? 1 : 0,
|
|
52
|
+
timedout: isTimedOut ? 1 : 0
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
function sum(t) {
|
|
56
|
+
if (t.summary) return countsFromSummary(t.summary);
|
|
57
|
+
if (!t.children || t.children.length === 0) return countsFromLeaf(t);
|
|
58
|
+
const r = emptyCounts();
|
|
59
|
+
for (const c of t.children) addCounts(r, sum(c));
|
|
60
|
+
return r;
|
|
61
|
+
}
|
|
62
|
+
function sumNonTaskTests(t) {
|
|
63
|
+
if (t.framework === "task") {
|
|
64
|
+
const r2 = emptyCounts();
|
|
65
|
+
for (const c of t.children || []) addCounts(r2, sumNonTaskTests(c));
|
|
66
|
+
return r2;
|
|
67
|
+
}
|
|
68
|
+
if (t.summary) return countsFromSummary(t.summary);
|
|
69
|
+
if (!t.children || t.children.length === 0) return countsFromLeaf(t);
|
|
70
|
+
const r = emptyCounts();
|
|
71
|
+
for (const c of t.children) addCounts(r, sumNonTaskTests(c));
|
|
72
|
+
return r;
|
|
73
|
+
}
|
|
74
|
+
function hasFailed(t) {
|
|
75
|
+
if (t.failed) return true;
|
|
76
|
+
return (t.children || []).some(hasFailed);
|
|
77
|
+
}
|
|
78
|
+
function hasPending(t) {
|
|
79
|
+
if (t.pending) return true;
|
|
80
|
+
return (t.children || []).some(hasPending);
|
|
81
|
+
}
|
|
82
|
+
function hasTimedOutDescendant(t) {
|
|
83
|
+
if (t.timed_out) return true;
|
|
84
|
+
return (t.children || []).some(hasTimedOutDescendant);
|
|
85
|
+
}
|
|
86
|
+
function totalDuration(t) {
|
|
87
|
+
if (t.duration && t.duration > 0) return t.duration;
|
|
88
|
+
let d = 0;
|
|
89
|
+
for (const c of t.children || []) d += totalDuration(c);
|
|
90
|
+
return d;
|
|
91
|
+
}
|
|
92
|
+
function testStatus(t) {
|
|
93
|
+
if (t.timed_out) return "timedout";
|
|
94
|
+
if (t.running) return "running";
|
|
95
|
+
if (t.pending) return "pending";
|
|
96
|
+
if (t.failed) return "failed";
|
|
97
|
+
if (t.warned) return "warned";
|
|
98
|
+
if (t.skipped) return "skipped";
|
|
99
|
+
if (t.passed) return "passed";
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
function statusIconFor(t) {
|
|
103
|
+
if (t.timed_out) return UiHourglass.UiHourglass;
|
|
104
|
+
if (t.running) return UiLoader.UiLoader;
|
|
105
|
+
if (t.pending) return UiPause.UiPause;
|
|
106
|
+
if (t.failed) return UiError.UiError;
|
|
107
|
+
if (t.warned) return UiWarningTriangle.UiWarningTriangle;
|
|
108
|
+
if (t.skipped) return UiPause.UiPause;
|
|
109
|
+
if (t.passed) return UiPass.UiPass;
|
|
110
|
+
if (t.children && t.children.length > 0) {
|
|
111
|
+
const s = sum(t);
|
|
112
|
+
if (hasTimedOutDescendant(t)) return UiHourglass.UiHourglass;
|
|
113
|
+
if (s.failed > 0) return UiError.UiError;
|
|
114
|
+
if (s.warned > 0) return UiWarningTriangle.UiWarningTriangle;
|
|
115
|
+
if (s.running > 0) return UiLoader.UiLoader;
|
|
116
|
+
if (s.pending > 0) return UiPause.UiPause;
|
|
117
|
+
return UiPass.UiPass;
|
|
118
|
+
}
|
|
119
|
+
return UiClass.UiClass;
|
|
120
|
+
}
|
|
121
|
+
function statusToneFor(t) {
|
|
122
|
+
if (t.timed_out) return "warning";
|
|
123
|
+
if (t.running) return "info";
|
|
124
|
+
if (t.pending) return "neutral";
|
|
125
|
+
if (t.failed) return "danger";
|
|
126
|
+
if (t.warned) return "warning";
|
|
127
|
+
if (t.skipped) return "warning";
|
|
128
|
+
if (t.passed) return "success";
|
|
129
|
+
if (t.children && t.children.length > 0) {
|
|
130
|
+
const s = sum(t);
|
|
131
|
+
if (hasTimedOutDescendant(t)) return "warning";
|
|
132
|
+
if (s.failed > 0) return "danger";
|
|
133
|
+
if (s.warned > 0) return "warning";
|
|
134
|
+
if (s.running > 0) return "info";
|
|
135
|
+
if (s.pending > 0) return "neutral";
|
|
136
|
+
return "success";
|
|
137
|
+
}
|
|
138
|
+
return "neutral";
|
|
139
|
+
}
|
|
140
|
+
function formatTestDuration(ns) {
|
|
141
|
+
if (ns <= 0) return "";
|
|
142
|
+
const ms = ns / 1e6;
|
|
143
|
+
if (ms < 1e3) return `${ms.toFixed(0)}ms`;
|
|
144
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
145
|
+
}
|
|
146
|
+
function compactWithUnit(value, unit) {
|
|
147
|
+
const rounded = Math.abs(value) >= 10 ? Math.round(value).toString() : value.toFixed(1).replace(/\.0$/, "");
|
|
148
|
+
return `${rounded}${unit}`;
|
|
149
|
+
}
|
|
150
|
+
function formatCount(n) {
|
|
151
|
+
const abs = Math.abs(n);
|
|
152
|
+
if (abs < 1e3) return String(n);
|
|
153
|
+
if (abs < 1e6) return compactWithUnit(n / 1e3, "k");
|
|
154
|
+
return compactWithUnit(n / 1e6, "M");
|
|
155
|
+
}
|
|
156
|
+
function humanizeName(name, framework) {
|
|
157
|
+
if (!name) return "";
|
|
158
|
+
if (framework !== "go test") return name;
|
|
159
|
+
if (name.endsWith("/")) return name;
|
|
160
|
+
return name.split("/").map((p, i) => {
|
|
161
|
+
if (i === 0) {
|
|
162
|
+
return p.replace(/^Test/, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
163
|
+
}
|
|
164
|
+
return p.replace(/_/g, " ");
|
|
165
|
+
}).join(" / ");
|
|
166
|
+
}
|
|
167
|
+
function collectFrameworks(tests) {
|
|
168
|
+
const set = /* @__PURE__ */ new Set();
|
|
169
|
+
const walk = (t) => {
|
|
170
|
+
if (t.framework && t.framework !== "task") set.add(t.framework);
|
|
171
|
+
(t.children || []).forEach(walk);
|
|
172
|
+
};
|
|
173
|
+
tests.forEach(walk);
|
|
174
|
+
return Array.from(set).sort();
|
|
175
|
+
}
|
|
176
|
+
function joinChainNames(parent, child) {
|
|
177
|
+
const p = (parent ?? "").trim();
|
|
178
|
+
const c = (child ?? "").trim();
|
|
179
|
+
if (!p) return c;
|
|
180
|
+
if (!c) return p;
|
|
181
|
+
return `${p} > ${c}`;
|
|
182
|
+
}
|
|
183
|
+
function isCollapsibleContainer(t) {
|
|
184
|
+
return !(t.passed || t.failed || t.skipped || t.pending);
|
|
185
|
+
}
|
|
186
|
+
function withoutSummary(t) {
|
|
187
|
+
const { summary: _summary, ...rest } = t;
|
|
188
|
+
return rest;
|
|
189
|
+
}
|
|
190
|
+
function collapseNode(t) {
|
|
191
|
+
const children = t.children ?? [];
|
|
192
|
+
if (children.length === 0) return t;
|
|
193
|
+
const collapsedChildren = children.map(collapseNode);
|
|
194
|
+
const onlyChild = collapsedChildren.length === 1 ? collapsedChildren[0] : void 0;
|
|
195
|
+
if (onlyChild && isCollapsibleContainer(t)) {
|
|
196
|
+
return { ...withoutSummary(onlyChild), name: joinChainNames(t.name, onlyChild.name) };
|
|
197
|
+
}
|
|
198
|
+
return { ...t, children: collapsedChildren };
|
|
199
|
+
}
|
|
200
|
+
function collapseSingleChildChains(tests) {
|
|
201
|
+
return tests.map(collapseNode);
|
|
202
|
+
}
|
|
203
|
+
function matchesLeaf(t, statusFilter, frameworkFilter) {
|
|
204
|
+
const s = testStatus(t);
|
|
205
|
+
if (s === null) return false;
|
|
206
|
+
return filterState.matchesFilterState(s, statusFilter) && filterState.matchesFilterState(t.framework, frameworkFilter);
|
|
207
|
+
}
|
|
208
|
+
function filterNode(t, statusFilter, frameworkFilter) {
|
|
209
|
+
if (t.children && t.children.length > 0) {
|
|
210
|
+
const filtered = t.children.map((c) => filterNode(c, statusFilter, frameworkFilter)).filter(Boolean);
|
|
211
|
+
if (filtered.length === 0) return null;
|
|
212
|
+
return { ...withoutSummary(t), children: filtered };
|
|
213
|
+
}
|
|
214
|
+
return matchesLeaf(t, statusFilter, frameworkFilter) ? t : null;
|
|
215
|
+
}
|
|
216
|
+
function filterTests(tests, statusFilter, frameworkFilter) {
|
|
217
|
+
if (statusFilter.size === 0 && frameworkFilter.size === 0) return tests;
|
|
218
|
+
return tests.map((t) => filterNode(t, statusFilter, frameworkFilter)).filter(Boolean);
|
|
219
|
+
}
|
|
220
|
+
exports.collapseSingleChildChains = collapseSingleChildChains;
|
|
221
|
+
exports.collectFrameworks = collectFrameworks;
|
|
222
|
+
exports.filterTests = filterTests;
|
|
223
|
+
exports.formatCount = formatCount;
|
|
224
|
+
exports.formatTestDuration = formatTestDuration;
|
|
225
|
+
exports.hasFailed = hasFailed;
|
|
226
|
+
exports.hasPending = hasPending;
|
|
227
|
+
exports.humanizeName = humanizeName;
|
|
228
|
+
exports.statusIconFor = statusIconFor;
|
|
229
|
+
exports.statusToneFor = statusToneFor;
|
|
230
|
+
exports.sum = sum;
|
|
231
|
+
exports.sumNonTaskTests = sumNonTaskTests;
|
|
232
|
+
exports.testStatus = testStatus;
|
|
233
|
+
exports.totalDuration = totalDuration;
|
|
234
|
+
//# sourceMappingURL=status.cjs.map
|
|
@@ -0,0 +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/** 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":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { StaticIconComponent } from '../Icon';
|
|
2
|
+
import { BadgeTone } from '../Badge';
|
|
3
|
+
import { Test } from './types';
|
|
4
|
+
import { FilterState } from './filterState';
|
|
5
|
+
/**
|
|
6
|
+
* Tallied verdict breakdown a `Test` subtree rolls up to. `warned` is amber and
|
|
7
|
+
* orthogonal to a real failure — a warned-only leaf counts in total + warned,
|
|
8
|
+
* never in failed.
|
|
9
|
+
*/
|
|
10
|
+
export type StatusCounts = {
|
|
11
|
+
total: number;
|
|
12
|
+
passed: number;
|
|
13
|
+
failed: number;
|
|
14
|
+
warned: number;
|
|
15
|
+
skipped: number;
|
|
16
|
+
pending: number;
|
|
17
|
+
running: number;
|
|
18
|
+
timedout: number;
|
|
19
|
+
};
|
|
20
|
+
export declare function sum(t: Test): StatusCounts;
|
|
21
|
+
/** Like {@link sum} but excludes `task`-framework pseudo-nodes from the tally. */
|
|
22
|
+
export declare function sumNonTaskTests(t: Test): StatusCounts;
|
|
23
|
+
export declare function hasFailed(t: Test): boolean;
|
|
24
|
+
export declare function hasPending(t: Test): boolean;
|
|
25
|
+
export declare function totalDuration(t: Test): number;
|
|
26
|
+
export type TestStatus = "passed" | "failed" | "warned" | "skipped" | "pending" | "running" | "timedout";
|
|
27
|
+
/** Resolve a leaf node's single status, or null for a status-less container. */
|
|
28
|
+
export declare function testStatus(t: Test): TestStatus | null;
|
|
29
|
+
/** Pick the clicky icon for a node, rolling up container state. */
|
|
30
|
+
export declare function statusIconFor(t: Test): StaticIconComponent;
|
|
31
|
+
/** Map a node's status to a semantic Badge tone, rolling up container state. */
|
|
32
|
+
export declare function statusToneFor(t: Test): BadgeTone;
|
|
33
|
+
/**
|
|
34
|
+
* Format a nanosecond duration as "123ms" or "1.2s". Empty for non-positive.
|
|
35
|
+
* Named distinctly from clicky's ms/s `formatDuration` (lib/format) since the
|
|
36
|
+
* test-runner wire format is nanoseconds.
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatTestDuration(ns: number): string;
|
|
39
|
+
/** Compact integer counts: <1000 raw, then "k", then "M". */
|
|
40
|
+
export declare function formatCount(n: number): string;
|
|
41
|
+
/** Strip Go's `Test` prefix and split CamelCase / subtest names into words. */
|
|
42
|
+
export declare function humanizeName(name: string, framework?: string): string;
|
|
43
|
+
/** Unique non-task frameworks present in the forest, sorted. */
|
|
44
|
+
export declare function collectFrameworks(tests: Test[]): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Compress any status-less container with exactly one child into that child,
|
|
47
|
+
* joining names with " > ". Recursive, so chains collapse into a single row.
|
|
48
|
+
*/
|
|
49
|
+
export declare function collapseSingleChildChains(tests: Test[]): Test[];
|
|
50
|
+
/** Prune leaves not matching the status/framework filters; drop empty branches. */
|
|
51
|
+
export declare function filterTests(tests: Test[], statusFilter: FilterState<string>, frameworkFilter: FilterState<string>): Test[];
|
|
52
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/status.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAU1C,OAAO,KAAK,EAAE,IAAI,EAAe,MAAM,SAAS,CAAC;AACjD,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAIrE;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAmDF,wBAAgB,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,YAAY,CAMzC;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,CAAC,EAAE,IAAI,GAAG,YAAY,CAWrD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAG1C;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAG3C;AAOD,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAK7C;AAID,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,SAAS,GACT,SAAS,GACT,UAAU,CAAC;AAEf,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,GAAG,UAAU,GAAG,IAAI,CASrD;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,mBAAmB,CAkB1D;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,CAkBhD;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAKrD;AAQD,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAarE;AAID,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAQzD;AAmCD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAE/D;AA2BD,mFAAmF;AACnF,wBAAgB,WAAW,CACzB,KAAK,EAAE,IAAI,EAAE,EACb,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,EACjC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,GACnC,IAAI,EAAE,CAKR"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { matchesFilterState } from "./filterState.js";
|
|
2
|
+
import { UiHourglass } from "../../icons/components/UiHourglass.js";
|
|
3
|
+
import { UiLoader } from "../../icons/components/UiLoader.js";
|
|
4
|
+
import { UiPause } from "../../icons/components/UiPause.js";
|
|
5
|
+
import { UiError } from "../../icons/components/UiError.js";
|
|
6
|
+
import { UiWarningTriangle } from "../../icons/components/UiWarningTriangle.js";
|
|
7
|
+
import { UiPass } from "../../icons/components/UiPass.js";
|
|
8
|
+
import { UiClass } from "../../icons/components/UiClass.js";
|
|
9
|
+
const emptyCounts = () => ({
|
|
10
|
+
total: 0,
|
|
11
|
+
passed: 0,
|
|
12
|
+
failed: 0,
|
|
13
|
+
warned: 0,
|
|
14
|
+
skipped: 0,
|
|
15
|
+
pending: 0,
|
|
16
|
+
running: 0,
|
|
17
|
+
timedout: 0
|
|
18
|
+
});
|
|
19
|
+
const addCounts = (r, s) => {
|
|
20
|
+
r.total += s.total;
|
|
21
|
+
r.passed += s.passed;
|
|
22
|
+
r.failed += s.failed;
|
|
23
|
+
r.warned += s.warned;
|
|
24
|
+
r.skipped += s.skipped;
|
|
25
|
+
r.pending += s.pending;
|
|
26
|
+
r.running += s.running;
|
|
27
|
+
r.timedout += s.timedout;
|
|
28
|
+
};
|
|
29
|
+
const countsFromSummary = (summary) => ({
|
|
30
|
+
total: summary.Total,
|
|
31
|
+
passed: summary.Passed,
|
|
32
|
+
failed: summary.Failed,
|
|
33
|
+
warned: summary.Warned || 0,
|
|
34
|
+
skipped: summary.Skipped,
|
|
35
|
+
pending: summary.Pending || 0,
|
|
36
|
+
running: summary.Running || 0,
|
|
37
|
+
timedout: 0
|
|
38
|
+
});
|
|
39
|
+
const countsFromLeaf = (t) => {
|
|
40
|
+
const isTimedOut = !!t.timed_out;
|
|
41
|
+
const counted = isTimedOut || t.passed || t.failed || t.warned || t.skipped || t.pending || t.running;
|
|
42
|
+
return {
|
|
43
|
+
total: counted ? 1 : 0,
|
|
44
|
+
passed: !isTimedOut && t.passed ? 1 : 0,
|
|
45
|
+
failed: !isTimedOut && t.failed ? 1 : 0,
|
|
46
|
+
warned: !isTimedOut && t.warned ? 1 : 0,
|
|
47
|
+
skipped: !isTimedOut && t.skipped ? 1 : 0,
|
|
48
|
+
pending: !isTimedOut && t.pending ? 1 : 0,
|
|
49
|
+
running: !isTimedOut && t.running ? 1 : 0,
|
|
50
|
+
timedout: isTimedOut ? 1 : 0
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
function sum(t) {
|
|
54
|
+
if (t.summary) return countsFromSummary(t.summary);
|
|
55
|
+
if (!t.children || t.children.length === 0) return countsFromLeaf(t);
|
|
56
|
+
const r = emptyCounts();
|
|
57
|
+
for (const c of t.children) addCounts(r, sum(c));
|
|
58
|
+
return r;
|
|
59
|
+
}
|
|
60
|
+
function sumNonTaskTests(t) {
|
|
61
|
+
if (t.framework === "task") {
|
|
62
|
+
const r2 = emptyCounts();
|
|
63
|
+
for (const c of t.children || []) addCounts(r2, sumNonTaskTests(c));
|
|
64
|
+
return r2;
|
|
65
|
+
}
|
|
66
|
+
if (t.summary) return countsFromSummary(t.summary);
|
|
67
|
+
if (!t.children || t.children.length === 0) return countsFromLeaf(t);
|
|
68
|
+
const r = emptyCounts();
|
|
69
|
+
for (const c of t.children) addCounts(r, sumNonTaskTests(c));
|
|
70
|
+
return r;
|
|
71
|
+
}
|
|
72
|
+
function hasFailed(t) {
|
|
73
|
+
if (t.failed) return true;
|
|
74
|
+
return (t.children || []).some(hasFailed);
|
|
75
|
+
}
|
|
76
|
+
function hasPending(t) {
|
|
77
|
+
if (t.pending) return true;
|
|
78
|
+
return (t.children || []).some(hasPending);
|
|
79
|
+
}
|
|
80
|
+
function hasTimedOutDescendant(t) {
|
|
81
|
+
if (t.timed_out) return true;
|
|
82
|
+
return (t.children || []).some(hasTimedOutDescendant);
|
|
83
|
+
}
|
|
84
|
+
function totalDuration(t) {
|
|
85
|
+
if (t.duration && t.duration > 0) return t.duration;
|
|
86
|
+
let d = 0;
|
|
87
|
+
for (const c of t.children || []) d += totalDuration(c);
|
|
88
|
+
return d;
|
|
89
|
+
}
|
|
90
|
+
function testStatus(t) {
|
|
91
|
+
if (t.timed_out) return "timedout";
|
|
92
|
+
if (t.running) return "running";
|
|
93
|
+
if (t.pending) return "pending";
|
|
94
|
+
if (t.failed) return "failed";
|
|
95
|
+
if (t.warned) return "warned";
|
|
96
|
+
if (t.skipped) return "skipped";
|
|
97
|
+
if (t.passed) return "passed";
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function statusIconFor(t) {
|
|
101
|
+
if (t.timed_out) return UiHourglass;
|
|
102
|
+
if (t.running) return UiLoader;
|
|
103
|
+
if (t.pending) return UiPause;
|
|
104
|
+
if (t.failed) return UiError;
|
|
105
|
+
if (t.warned) return UiWarningTriangle;
|
|
106
|
+
if (t.skipped) return UiPause;
|
|
107
|
+
if (t.passed) return UiPass;
|
|
108
|
+
if (t.children && t.children.length > 0) {
|
|
109
|
+
const s = sum(t);
|
|
110
|
+
if (hasTimedOutDescendant(t)) return UiHourglass;
|
|
111
|
+
if (s.failed > 0) return UiError;
|
|
112
|
+
if (s.warned > 0) return UiWarningTriangle;
|
|
113
|
+
if (s.running > 0) return UiLoader;
|
|
114
|
+
if (s.pending > 0) return UiPause;
|
|
115
|
+
return UiPass;
|
|
116
|
+
}
|
|
117
|
+
return UiClass;
|
|
118
|
+
}
|
|
119
|
+
function statusToneFor(t) {
|
|
120
|
+
if (t.timed_out) return "warning";
|
|
121
|
+
if (t.running) return "info";
|
|
122
|
+
if (t.pending) return "neutral";
|
|
123
|
+
if (t.failed) return "danger";
|
|
124
|
+
if (t.warned) return "warning";
|
|
125
|
+
if (t.skipped) return "warning";
|
|
126
|
+
if (t.passed) return "success";
|
|
127
|
+
if (t.children && t.children.length > 0) {
|
|
128
|
+
const s = sum(t);
|
|
129
|
+
if (hasTimedOutDescendant(t)) return "warning";
|
|
130
|
+
if (s.failed > 0) return "danger";
|
|
131
|
+
if (s.warned > 0) return "warning";
|
|
132
|
+
if (s.running > 0) return "info";
|
|
133
|
+
if (s.pending > 0) return "neutral";
|
|
134
|
+
return "success";
|
|
135
|
+
}
|
|
136
|
+
return "neutral";
|
|
137
|
+
}
|
|
138
|
+
function formatTestDuration(ns) {
|
|
139
|
+
if (ns <= 0) return "";
|
|
140
|
+
const ms = ns / 1e6;
|
|
141
|
+
if (ms < 1e3) return `${ms.toFixed(0)}ms`;
|
|
142
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
143
|
+
}
|
|
144
|
+
function compactWithUnit(value, unit) {
|
|
145
|
+
const rounded = Math.abs(value) >= 10 ? Math.round(value).toString() : value.toFixed(1).replace(/\.0$/, "");
|
|
146
|
+
return `${rounded}${unit}`;
|
|
147
|
+
}
|
|
148
|
+
function formatCount(n) {
|
|
149
|
+
const abs = Math.abs(n);
|
|
150
|
+
if (abs < 1e3) return String(n);
|
|
151
|
+
if (abs < 1e6) return compactWithUnit(n / 1e3, "k");
|
|
152
|
+
return compactWithUnit(n / 1e6, "M");
|
|
153
|
+
}
|
|
154
|
+
function humanizeName(name, framework) {
|
|
155
|
+
if (!name) return "";
|
|
156
|
+
if (framework !== "go test") return name;
|
|
157
|
+
if (name.endsWith("/")) return name;
|
|
158
|
+
return name.split("/").map((p, i) => {
|
|
159
|
+
if (i === 0) {
|
|
160
|
+
return p.replace(/^Test/, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
161
|
+
}
|
|
162
|
+
return p.replace(/_/g, " ");
|
|
163
|
+
}).join(" / ");
|
|
164
|
+
}
|
|
165
|
+
function collectFrameworks(tests) {
|
|
166
|
+
const set = /* @__PURE__ */ new Set();
|
|
167
|
+
const walk = (t) => {
|
|
168
|
+
if (t.framework && t.framework !== "task") set.add(t.framework);
|
|
169
|
+
(t.children || []).forEach(walk);
|
|
170
|
+
};
|
|
171
|
+
tests.forEach(walk);
|
|
172
|
+
return Array.from(set).sort();
|
|
173
|
+
}
|
|
174
|
+
function joinChainNames(parent, child) {
|
|
175
|
+
const p = (parent ?? "").trim();
|
|
176
|
+
const c = (child ?? "").trim();
|
|
177
|
+
if (!p) return c;
|
|
178
|
+
if (!c) return p;
|
|
179
|
+
return `${p} > ${c}`;
|
|
180
|
+
}
|
|
181
|
+
function isCollapsibleContainer(t) {
|
|
182
|
+
return !(t.passed || t.failed || t.skipped || t.pending);
|
|
183
|
+
}
|
|
184
|
+
function withoutSummary(t) {
|
|
185
|
+
const { summary: _summary, ...rest } = t;
|
|
186
|
+
return rest;
|
|
187
|
+
}
|
|
188
|
+
function collapseNode(t) {
|
|
189
|
+
const children = t.children ?? [];
|
|
190
|
+
if (children.length === 0) return t;
|
|
191
|
+
const collapsedChildren = children.map(collapseNode);
|
|
192
|
+
const onlyChild = collapsedChildren.length === 1 ? collapsedChildren[0] : void 0;
|
|
193
|
+
if (onlyChild && isCollapsibleContainer(t)) {
|
|
194
|
+
return { ...withoutSummary(onlyChild), name: joinChainNames(t.name, onlyChild.name) };
|
|
195
|
+
}
|
|
196
|
+
return { ...t, children: collapsedChildren };
|
|
197
|
+
}
|
|
198
|
+
function collapseSingleChildChains(tests) {
|
|
199
|
+
return tests.map(collapseNode);
|
|
200
|
+
}
|
|
201
|
+
function matchesLeaf(t, statusFilter, frameworkFilter) {
|
|
202
|
+
const s = testStatus(t);
|
|
203
|
+
if (s === null) return false;
|
|
204
|
+
return matchesFilterState(s, statusFilter) && matchesFilterState(t.framework, frameworkFilter);
|
|
205
|
+
}
|
|
206
|
+
function filterNode(t, statusFilter, frameworkFilter) {
|
|
207
|
+
if (t.children && t.children.length > 0) {
|
|
208
|
+
const filtered = t.children.map((c) => filterNode(c, statusFilter, frameworkFilter)).filter(Boolean);
|
|
209
|
+
if (filtered.length === 0) return null;
|
|
210
|
+
return { ...withoutSummary(t), children: filtered };
|
|
211
|
+
}
|
|
212
|
+
return matchesLeaf(t, statusFilter, frameworkFilter) ? t : null;
|
|
213
|
+
}
|
|
214
|
+
function filterTests(tests, statusFilter, frameworkFilter) {
|
|
215
|
+
if (statusFilter.size === 0 && frameworkFilter.size === 0) return tests;
|
|
216
|
+
return tests.map((t) => filterNode(t, statusFilter, frameworkFilter)).filter(Boolean);
|
|
217
|
+
}
|
|
218
|
+
export {
|
|
219
|
+
collapseSingleChildChains,
|
|
220
|
+
collectFrameworks,
|
|
221
|
+
filterTests,
|
|
222
|
+
formatCount,
|
|
223
|
+
formatTestDuration,
|
|
224
|
+
hasFailed,
|
|
225
|
+
hasPending,
|
|
226
|
+
humanizeName,
|
|
227
|
+
statusIconFor,
|
|
228
|
+
statusToneFor,
|
|
229
|
+
sum,
|
|
230
|
+
sumNonTaskTests,
|
|
231
|
+
testStatus,
|
|
232
|
+
totalDuration
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +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/** 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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"}
|