@vitest-agent/ui 2.2.13 → 2.3.1
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/dispatcher/classify.js +6 -1
- package/dispatcher/dispatch.js +26 -2
- package/dispatcher/helpers.js +29 -15
- package/index.d.ts +127 -66
- package/index.js +1 -1
- package/package.json +3 -3
- package/reducer.js +6 -2
- package/render-agent.js +18 -0
- package/render-ink/StreamApp.js +52 -29
- package/synthesize.js +15 -5
package/dispatcher/classify.js
CHANGED
|
@@ -36,7 +36,11 @@ const classifyRunShape = (state, projects) => {
|
|
|
36
36
|
* run whose only non-passing signal is one or more timed-out tests
|
|
37
37
|
* (`totals.timeoutCount > 0`, `totals.failCount === 0`) also
|
|
38
38
|
* classifies as `some-fail` — timeouts are not passes, and must not
|
|
39
|
-
* route to a passing cell (issue #224).
|
|
39
|
+
* route to a passing cell (issue #224). A run whose only non-passing
|
|
40
|
+
* signal is a process-level unhandled error (a non-empty
|
|
41
|
+
* `unhandledErrors`, all counts otherwise clean) also classifies as `some-fail` —
|
|
42
|
+
* a green-looking count must never hide an unhandled error behind an
|
|
43
|
+
* all-pass cell (issue #240).
|
|
40
44
|
*
|
|
41
45
|
* @param state - the fully-reduced render state
|
|
42
46
|
* @returns the outcome classification
|
|
@@ -44,6 +48,7 @@ const classifyRunShape = (state, projects) => {
|
|
|
44
48
|
*/
|
|
45
49
|
const classifyOutcome = (state) => {
|
|
46
50
|
if (state.totals.failCount > 0) return "some-fail";
|
|
51
|
+
if (state.unhandledErrors.length > 0) return "some-fail";
|
|
47
52
|
if (state.totals.timeoutCount > 0) return "some-fail";
|
|
48
53
|
if (state.coverage !== null && state.coverage.violations.length > 0) return "threshold-violation";
|
|
49
54
|
return "all-pass";
|
package/dispatcher/dispatch.js
CHANGED
|
@@ -10,6 +10,9 @@ import { renderSingleTestThreshold } from "./cells/single-test-threshold.js";
|
|
|
10
10
|
import { renderWorkspaceFail } from "./cells/workspace-fail.js";
|
|
11
11
|
import { renderWorkspacePass } from "./cells/workspace-pass.js";
|
|
12
12
|
import { renderWorkspaceThreshold } from "./cells/workspace-threshold.js";
|
|
13
|
+
import { formatScopedCoverageNote } from "@vitest-agent/sdk";
|
|
14
|
+
import { Box, Text } from "ink";
|
|
15
|
+
import { createElement } from "react";
|
|
13
16
|
|
|
14
17
|
//#region src/dispatcher/dispatch.ts
|
|
15
18
|
/**
|
|
@@ -51,7 +54,24 @@ const dispatcherTable = {
|
|
|
51
54
|
* @public
|
|
52
55
|
*/
|
|
53
56
|
const dispatch = (inputs, opts) => {
|
|
54
|
-
|
|
57
|
+
const body = dispatcherTable[inputs.shape][inputs.outcome].agent(inputs, opts);
|
|
58
|
+
const note = scopedCoverageNoteFor(inputs);
|
|
59
|
+
return note !== null ? `${body}\n${note}` : body;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Build the scoped-coverage note for this run, or `null` on a full
|
|
63
|
+
* (non-scoped) run. Shared by {@link dispatch} and {@link dispatchInk} so
|
|
64
|
+
* both render paths surface the same information (issue #160 gap 1) —
|
|
65
|
+
* Vitest's coverage thresholds are meaningless against a subset of the
|
|
66
|
+
* project's test files, so every cell's own threshold-flavored coverage
|
|
67
|
+
* text is followed by an explanation of why to disregard it.
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
const scopedCoverageNoteFor = (inputs) => {
|
|
72
|
+
const cov = inputs.state.coverage;
|
|
73
|
+
if (cov === null || cov.scoped !== true) return null;
|
|
74
|
+
return formatScopedCoverageNote(cov.scopedFiles ?? 0, cov.totalFiles);
|
|
55
75
|
};
|
|
56
76
|
/**
|
|
57
77
|
* Dispatch to the cell that matches `(inputs.shape, inputs.outcome)`
|
|
@@ -66,7 +86,11 @@ const dispatch = (inputs, opts) => {
|
|
|
66
86
|
*/
|
|
67
87
|
const dispatchInk = (inputs, opts) => {
|
|
68
88
|
const cell = dispatcherTable[inputs.shape][inputs.outcome];
|
|
69
|
-
|
|
89
|
+
if (cell.ink === void 0) return null;
|
|
90
|
+
const element = cell.ink(inputs, opts);
|
|
91
|
+
const note = scopedCoverageNoteFor(inputs);
|
|
92
|
+
if (note === null) return element;
|
|
93
|
+
return createElement(Box, { flexDirection: "column" }, element, createElement(Text, null, note));
|
|
70
94
|
};
|
|
71
95
|
|
|
72
96
|
//#endregion
|
package/dispatcher/helpers.js
CHANGED
|
@@ -102,9 +102,13 @@ const formatTrendLine = (trend) => {
|
|
|
102
102
|
* project carrying failures or violations.
|
|
103
103
|
*/
|
|
104
104
|
const formatProjectRow = (project, nameWidth) => {
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
const
|
|
105
|
+
const timeoutCount = project.timeoutCount ?? 0;
|
|
106
|
+
const total = project.passCount + project.failCount + project.skipCount + timeoutCount;
|
|
107
|
+
const glyph = project.failCount > 0 || timeoutCount > 0 ? "✗" : "✓";
|
|
108
|
+
const countParts = [`${project.passCount}/${total} passed`];
|
|
109
|
+
if (project.failCount > 0) countParts.push(`${project.failCount} failed`);
|
|
110
|
+
if (timeoutCount > 0) countParts.push(`${timeoutCount} timed out`);
|
|
111
|
+
const counts = project.failCount > 0 || timeoutCount > 0 ? countParts.join(", ") : `${project.passCount} passed`;
|
|
108
112
|
const tagSuffix = formatTagCountSuffix(project.tagCounts);
|
|
109
113
|
const base = ` ${glyph} ${project.name.padEnd(nameWidth)} ${counts} (${formatDisplayDuration(project.durationMs)})`;
|
|
110
114
|
return tagSuffix.length === 0 ? base : `${base} ${tagSuffix}`;
|
|
@@ -132,36 +136,46 @@ const formatWorkspaceTotal = (projects) => {
|
|
|
132
136
|
let pass = 0;
|
|
133
137
|
let fail = 0;
|
|
134
138
|
let skip = 0;
|
|
139
|
+
let timeout = 0;
|
|
135
140
|
let durationMs = 0;
|
|
136
141
|
for (const p of projects) {
|
|
137
142
|
pass += p.passCount;
|
|
138
143
|
fail += p.failCount;
|
|
139
144
|
skip += p.skipCount;
|
|
145
|
+
timeout += p.timeoutCount ?? 0;
|
|
140
146
|
durationMs += p.durationMs;
|
|
141
147
|
}
|
|
142
|
-
const total = pass + fail + skip;
|
|
148
|
+
const total = pass + fail + skip + timeout;
|
|
143
149
|
const parts = [`${pass}/${total} passed`];
|
|
144
150
|
if (fail > 0) parts.push(`${fail} failed`);
|
|
151
|
+
if (timeout > 0) parts.push(`${timeout} timed out`);
|
|
145
152
|
if (skip > 0) parts.push(`${skip} skipped`);
|
|
146
153
|
return `Total: ${parts.join(", ")} (${formatDisplayDuration(durationMs)})`;
|
|
147
154
|
};
|
|
148
|
-
const
|
|
155
|
+
const TABLE_COL_FILE_MIN = 60;
|
|
149
156
|
/**
|
|
150
157
|
* Format the `Files below aspirational target:` block — a pipe-delimited
|
|
151
158
|
* table truncated to the first `limit` files with a "+N more" suffix.
|
|
152
159
|
* Returns an empty array when `belowTarget` is empty.
|
|
160
|
+
*
|
|
161
|
+
* The file column is sized to the longest path among the printed rows
|
|
162
|
+
* (never narrower than {@link TABLE_COL_FILE_MIN}), so a printed path is
|
|
163
|
+
* never truncated (issue #237 follow-up) — only the omitted rows (past
|
|
164
|
+
* `limit`) are unaffected, since their width never entered the
|
|
165
|
+
* calculation.
|
|
153
166
|
*/
|
|
154
167
|
const formatBelowTargetTable = (belowTarget, limit) => {
|
|
155
168
|
if (belowTarget.length === 0) return [];
|
|
156
169
|
const top = belowTarget.slice(0, limit);
|
|
157
170
|
const omitted = belowTarget.length - top.length;
|
|
171
|
+
const fileColWidth = Math.max(TABLE_COL_FILE_MIN, ...top.map((f) => f.file.length + 2));
|
|
158
172
|
const header = [
|
|
159
173
|
"Files below aspirational target:",
|
|
160
|
-
buildTableSeparator(),
|
|
161
|
-
buildTableHeader(),
|
|
162
|
-
buildTableSeparator()
|
|
174
|
+
buildTableSeparator(fileColWidth),
|
|
175
|
+
buildTableHeader(fileColWidth),
|
|
176
|
+
buildTableSeparator(fileColWidth)
|
|
163
177
|
];
|
|
164
|
-
const rows = top.map((file) => buildTableRow(file));
|
|
178
|
+
const rows = top.map((file) => buildTableRow(file, fileColWidth));
|
|
165
179
|
const footer = [];
|
|
166
180
|
if (omitted > 0) footer.push(`… ${omitted} more (use the test_coverage MCP tool for the full list)`);
|
|
167
181
|
return [
|
|
@@ -170,14 +184,14 @@ const formatBelowTargetTable = (belowTarget, limit) => {
|
|
|
170
184
|
...footer
|
|
171
185
|
];
|
|
172
186
|
};
|
|
173
|
-
const buildTableSeparator = () => {
|
|
174
|
-
return `${"-".repeat(
|
|
187
|
+
const buildTableSeparator = (fileColWidth) => {
|
|
188
|
+
return `${"-".repeat(fileColWidth)}|---------|---------|---------|---------|-------------------`;
|
|
175
189
|
};
|
|
176
|
-
const buildTableHeader = () => {
|
|
177
|
-
return ` ${"File".padEnd(
|
|
190
|
+
const buildTableHeader = (fileColWidth) => {
|
|
191
|
+
return ` ${"File".padEnd(fileColWidth - 1)}| % Stmts | % Branch| % Funcs | % Lines | Uncovered Line #s`;
|
|
178
192
|
};
|
|
179
|
-
const buildTableRow = (file) => {
|
|
180
|
-
return `${` ${
|
|
193
|
+
const buildTableRow = (file, fileColWidth) => {
|
|
194
|
+
return `${` ${file.file.padEnd(fileColWidth - 1)}`}|${pctCell(file.summary.statements)}|${pctCell(file.summary.branches)}|${pctCell(file.summary.functions)}|${pctCell(file.summary.lines)}|${` ${file.uncoveredLines}`}`;
|
|
181
195
|
};
|
|
182
196
|
const pctCell = (n) => {
|
|
183
197
|
const text = `${Math.round(n)}`;
|
package/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ type FileCoverageReport = typeof FileCoverageReport.Type;
|
|
|
41
41
|
* Per-test record accumulated as `TestStarted` / `TestFinished` events arrive.
|
|
42
42
|
* @public
|
|
43
43
|
*/
|
|
44
|
-
declare const TestRecord: Schema.Struct<{
|
|
44
|
+
export declare const TestRecord: Schema.Struct<{
|
|
45
45
|
readonly testName: Schema.String;
|
|
46
46
|
readonly suitePath: Schema.$Array<Schema.String>;
|
|
47
47
|
readonly status: Schema.Union<readonly [Schema.Literals<readonly ["passed", "failed", "skipped", "pending"]>, Schema.Literals<readonly ["running", "timed-out"]>]>;
|
|
@@ -55,13 +55,13 @@ declare const TestRecord: Schema.Struct<{
|
|
|
55
55
|
}>>;
|
|
56
56
|
}>;
|
|
57
57
|
/** @public */
|
|
58
|
-
type TestRecord = typeof TestRecord.Type;
|
|
58
|
+
export type TestRecord = typeof TestRecord.Type;
|
|
59
59
|
/**
|
|
60
60
|
* Per-module aggregation. The reducer keeps modules keyed on
|
|
61
61
|
* `modulePath` so out-of-order events can update the right slot.
|
|
62
62
|
* @public
|
|
63
63
|
*/
|
|
64
|
-
declare const ModuleRecord: Schema.Struct<{
|
|
64
|
+
export declare const ModuleRecord: Schema.Struct<{
|
|
65
65
|
readonly modulePath: Schema.String;
|
|
66
66
|
readonly status: Schema.Literals<readonly ["queued", "running", "finished"]>;
|
|
67
67
|
readonly passCount: Schema.Number;
|
|
@@ -98,14 +98,14 @@ declare const ModuleRecord: Schema.Struct<{
|
|
|
98
98
|
readonly tagCounts: Schema.optional<Schema.$Record<Schema.String, Schema.Number>>;
|
|
99
99
|
}>;
|
|
100
100
|
/** @public */
|
|
101
|
-
type ModuleRecord = typeof ModuleRecord.Type;
|
|
101
|
+
export type ModuleRecord = typeof ModuleRecord.Type;
|
|
102
102
|
/**
|
|
103
103
|
* One row in the failure list. A failure can exist before its
|
|
104
104
|
* classification arrives (`FailureClassified` is emitted from a
|
|
105
105
|
* separate pipeline), so `classification` is nullable.
|
|
106
106
|
* @public
|
|
107
107
|
*/
|
|
108
|
-
declare const FailureRecord: Schema.Struct<{
|
|
108
|
+
export declare const FailureRecord: Schema.Struct<{
|
|
109
109
|
readonly modulePath: Schema.String;
|
|
110
110
|
readonly testName: Schema.String;
|
|
111
111
|
readonly suitePath: Schema.$Array<Schema.String>;
|
|
@@ -120,13 +120,13 @@ declare const FailureRecord: Schema.Struct<{
|
|
|
120
120
|
readonly classification: Schema.NullOr<Schema.Literals<readonly ["stable", "new-failure", "persistent", "flaky", "recovered"]>>;
|
|
121
121
|
}>;
|
|
122
122
|
/** @public */
|
|
123
|
-
type FailureRecord = typeof FailureRecord.Type;
|
|
123
|
+
export type FailureRecord = typeof FailureRecord.Type;
|
|
124
124
|
/**
|
|
125
125
|
* Coverage block, populated only after `CoverageReady`. Threshold
|
|
126
126
|
* violations are accumulated separately as they arrive.
|
|
127
127
|
* @public
|
|
128
128
|
*/
|
|
129
|
-
declare const CoverageRenderState: Schema.Struct<{
|
|
129
|
+
export declare const CoverageRenderState: Schema.Struct<{
|
|
130
130
|
readonly metrics: Schema.Struct<{
|
|
131
131
|
readonly statements: Schema.Number;
|
|
132
132
|
readonly branches: Schema.Number;
|
|
@@ -154,27 +154,30 @@ declare const CoverageRenderState: Schema.Struct<{
|
|
|
154
154
|
readonly expected: Schema.Number;
|
|
155
155
|
readonly actual: Schema.Number;
|
|
156
156
|
}>>;
|
|
157
|
+
readonly scoped: Schema.optional<Schema.Boolean>;
|
|
158
|
+
readonly scopedFiles: Schema.optional<Schema.Number>;
|
|
159
|
+
readonly totalFiles: Schema.optional<Schema.Number>;
|
|
157
160
|
}>;
|
|
158
161
|
/** @public */
|
|
159
|
-
type CoverageRenderState = typeof CoverageRenderState.Type;
|
|
162
|
+
export type CoverageRenderState = typeof CoverageRenderState.Type;
|
|
160
163
|
/**
|
|
161
164
|
* A `SuggestedAction` event, denormalized into the queue the renderer reads.
|
|
162
165
|
* @public
|
|
163
166
|
*/
|
|
164
|
-
declare const SuggestedActionRecord: Schema.Struct<{
|
|
167
|
+
export declare const SuggestedActionRecord: Schema.Struct<{
|
|
165
168
|
readonly severity: Schema.Literals<readonly ["info", "warn", "blocker"]>;
|
|
166
169
|
readonly title: Schema.String;
|
|
167
170
|
readonly detail: Schema.String;
|
|
168
171
|
readonly targetTool: Schema.optional<Schema.String>;
|
|
169
172
|
}>;
|
|
170
173
|
/** @public */
|
|
171
|
-
type SuggestedActionRecord = typeof SuggestedActionRecord.Type;
|
|
174
|
+
export type SuggestedActionRecord = typeof SuggestedActionRecord.Type;
|
|
172
175
|
/**
|
|
173
176
|
* Per-run totals, recomputed on every `ModuleFinished` and reconciled
|
|
174
177
|
* against `RunFinished` (which carries the runner's own count).
|
|
175
178
|
* @public
|
|
176
179
|
*/
|
|
177
|
-
declare const RenderTotals: Schema.Struct<{
|
|
180
|
+
export declare const RenderTotals: Schema.Struct<{
|
|
178
181
|
readonly passCount: Schema.Number;
|
|
179
182
|
readonly failCount: Schema.Number;
|
|
180
183
|
readonly skipCount: Schema.Number;
|
|
@@ -182,7 +185,7 @@ declare const RenderTotals: Schema.Struct<{
|
|
|
182
185
|
readonly durationMs: Schema.Number;
|
|
183
186
|
}>;
|
|
184
187
|
/** @public */
|
|
185
|
-
type RenderTotals = typeof RenderTotals.Type;
|
|
188
|
+
export type RenderTotals = typeof RenderTotals.Type;
|
|
186
189
|
/**
|
|
187
190
|
* The renderer state. Both the human (Ink) and agent (string)
|
|
188
191
|
* renderers read this shape; the reducer is the only producer.
|
|
@@ -194,7 +197,7 @@ type RenderTotals = typeof RenderTotals.Type;
|
|
|
194
197
|
* rather than the suite completing.
|
|
195
198
|
* @public
|
|
196
199
|
*/
|
|
197
|
-
declare const RenderState: Schema.Struct<{
|
|
200
|
+
export declare const RenderState: Schema.Struct<{
|
|
198
201
|
readonly phase: Schema.Literals<readonly ["idle", "running", "finished", "timed-out"]>;
|
|
199
202
|
readonly runId: Schema.NullOr<Schema.String>;
|
|
200
203
|
readonly configHash: Schema.NullOr<Schema.String>;
|
|
@@ -272,6 +275,9 @@ declare const RenderState: Schema.Struct<{
|
|
|
272
275
|
readonly expected: Schema.Number;
|
|
273
276
|
readonly actual: Schema.Number;
|
|
274
277
|
}>>;
|
|
278
|
+
readonly scoped: Schema.optional<Schema.Boolean>;
|
|
279
|
+
readonly scopedFiles: Schema.optional<Schema.Number>;
|
|
280
|
+
readonly totalFiles: Schema.optional<Schema.Number>;
|
|
275
281
|
}>>;
|
|
276
282
|
readonly trend: Schema.NullOr<Schema.Struct<{
|
|
277
283
|
readonly direction: Schema.Literals<readonly ["improving", "regressing", "stable"]>;
|
|
@@ -307,16 +313,29 @@ declare const RenderState: Schema.Struct<{
|
|
|
307
313
|
* fixtures) falls back to `moduleOrder.length`.
|
|
308
314
|
*/
|
|
309
315
|
readonly collectedModules: Schema.optional<Schema.Number>;
|
|
316
|
+
/**
|
|
317
|
+
* Process-level errors with no owning module, folded from
|
|
318
|
+
* `RunFinished.unhandledErrors`. A run that never carried the field
|
|
319
|
+
* (older replay data, hand-built fixtures) defaults to `[]`. See
|
|
320
|
+
* issue #240.
|
|
321
|
+
*/
|
|
322
|
+
readonly unhandledErrors: Schema.$Array<Schema.Struct<{
|
|
323
|
+
readonly message: Schema.String;
|
|
324
|
+
readonly stack: Schema.optional<Schema.String>;
|
|
325
|
+
readonly diff: Schema.optional<Schema.String>;
|
|
326
|
+
readonly expected: Schema.optional<Schema.String>;
|
|
327
|
+
readonly received: Schema.optional<Schema.String>;
|
|
328
|
+
}>>;
|
|
310
329
|
}>;
|
|
311
330
|
/** @public */
|
|
312
|
-
type RenderState = typeof RenderState.Type;
|
|
331
|
+
export type RenderState = typeof RenderState.Type;
|
|
313
332
|
/**
|
|
314
333
|
* Initial state — what the reducer returns when no events have been
|
|
315
334
|
* applied yet. Exported so tests and the renderers' bootstrapping
|
|
316
335
|
* code share one definition.
|
|
317
336
|
* @public
|
|
318
337
|
*/
|
|
319
|
-
declare const initialRenderState: RenderState;
|
|
338
|
+
export declare const initialRenderState: RenderState;
|
|
320
339
|
//#endregion
|
|
321
340
|
//#region src/contracts/dispatcher.d.ts
|
|
322
341
|
/**
|
|
@@ -356,6 +375,7 @@ interface ProjectSummary {
|
|
|
356
375
|
readonly failCount: number;
|
|
357
376
|
readonly skipCount: number;
|
|
358
377
|
readonly durationMs: number;
|
|
378
|
+
readonly timeoutCount?: number;
|
|
359
379
|
readonly tagCounts?: Record<string, number>;
|
|
360
380
|
readonly belowTarget?: number;
|
|
361
381
|
readonly violations?: number;
|
|
@@ -549,6 +569,7 @@ declare const AgentReport: Schema.Struct<{
|
|
|
549
569
|
}>>;
|
|
550
570
|
readonly scoped: Schema.withDecodingDefaultKey<Schema.Boolean, never>;
|
|
551
571
|
readonly scopedFiles: Schema.optional<Schema.$Array<Schema.String>>;
|
|
572
|
+
readonly totalFiles: Schema.optional<Schema.Number>;
|
|
552
573
|
readonly lowCoverage: Schema.$Array<Schema.Struct<{
|
|
553
574
|
readonly file: Schema.String;
|
|
554
575
|
readonly summary: Schema.Struct<{
|
|
@@ -587,6 +608,10 @@ declare const AgentReport: Schema.Struct<{
|
|
|
587
608
|
readonly sample: Schema.optional<Schema.String>;
|
|
588
609
|
}>>;
|
|
589
610
|
readonly truncated: Schema.optional<Schema.Boolean>;
|
|
611
|
+
readonly fromFailingTests: Schema.optional<Schema.Struct<{
|
|
612
|
+
readonly total: Schema.Number;
|
|
613
|
+
readonly files: Schema.Number;
|
|
614
|
+
}>>;
|
|
590
615
|
}>>;
|
|
591
616
|
}>;
|
|
592
617
|
/** @public */
|
|
@@ -604,22 +629,22 @@ type TestClassification = typeof TestClassification.Type;
|
|
|
604
629
|
* Coverage metric whose threshold can be violated independently.
|
|
605
630
|
* @public
|
|
606
631
|
*/
|
|
607
|
-
declare const CoverageMetric: Schema.Literals<readonly ["lines", "branches", "functions", "statements"]>;
|
|
632
|
+
export declare const CoverageMetric: Schema.Literals<readonly ["lines", "branches", "functions", "statements"]>;
|
|
608
633
|
/** @public */
|
|
609
|
-
type CoverageMetric = typeof CoverageMetric.Type;
|
|
634
|
+
export type CoverageMetric = typeof CoverageMetric.Type;
|
|
610
635
|
/**
|
|
611
636
|
* Severity tier for a suggested action emitted during a run.
|
|
612
637
|
* @public
|
|
613
638
|
*/
|
|
614
|
-
declare const ActionSeverity: Schema.Literals<readonly ["info", "warn", "blocker"]>;
|
|
639
|
+
export declare const ActionSeverity: Schema.Literals<readonly ["info", "warn", "blocker"]>;
|
|
615
640
|
/** @public */
|
|
616
|
-
type ActionSeverity = typeof ActionSeverity.Type;
|
|
641
|
+
export type ActionSeverity = typeof ActionSeverity.Type;
|
|
617
642
|
/**
|
|
618
643
|
* A coverage gap surfaced to the renderer — a per-file shortfall that
|
|
619
644
|
* a renderer may want to call out individually.
|
|
620
645
|
* @public
|
|
621
646
|
*/
|
|
622
|
-
declare const CoverageGap: Schema.Struct<{
|
|
647
|
+
export declare const CoverageGap: Schema.Struct<{
|
|
623
648
|
readonly file: Schema.String;
|
|
624
649
|
readonly missing: Schema.Struct<{
|
|
625
650
|
readonly statements: Schema.Number;
|
|
@@ -630,7 +655,7 @@ declare const CoverageGap: Schema.Struct<{
|
|
|
630
655
|
readonly uncoveredLines: Schema.optional<Schema.String>;
|
|
631
656
|
}>;
|
|
632
657
|
/** @public */
|
|
633
|
-
type CoverageGap = typeof CoverageGap.Type;
|
|
658
|
+
export type CoverageGap = typeof CoverageGap.Type;
|
|
634
659
|
/**
|
|
635
660
|
* The reducer projects this union into `RenderState`. Renderers
|
|
636
661
|
* never read the raw event stream — they read the projected state.
|
|
@@ -641,7 +666,7 @@ type CoverageGap = typeof CoverageGap.Type;
|
|
|
641
666
|
* been updated to consume the new state shape.
|
|
642
667
|
* @public
|
|
643
668
|
*/
|
|
644
|
-
declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted", {
|
|
669
|
+
export declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted", {
|
|
645
670
|
readonly runId: Schema.String;
|
|
646
671
|
readonly startedAt: Schema.String;
|
|
647
672
|
readonly configHash: Schema.String;
|
|
@@ -758,6 +783,9 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
|
|
|
758
783
|
}>;
|
|
759
784
|
readonly uncoveredLines: Schema.optional<Schema.String>;
|
|
760
785
|
}>>;
|
|
786
|
+
readonly scoped: Schema.optional<Schema.Boolean>;
|
|
787
|
+
readonly scopedFiles: Schema.optional<Schema.Number>;
|
|
788
|
+
readonly totalFiles: Schema.optional<Schema.Number>;
|
|
761
789
|
}>, Schema.TaggedStruct<"ThresholdViolation", {
|
|
762
790
|
readonly metric: Schema.Literals<readonly ["lines", "branches", "functions", "statements"]>;
|
|
763
791
|
readonly expected: Schema.Number;
|
|
@@ -787,9 +815,22 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
|
|
|
787
815
|
* tracks failing modules (report replay). See issue #204.
|
|
788
816
|
*/
|
|
789
817
|
readonly collectedModules: Schema.optional<Schema.Number>;
|
|
818
|
+
/**
|
|
819
|
+
* Process-level errors with no owning module (an unhandled
|
|
820
|
+
* rejection, a worker crash) — Vitest's `unhandledErrors`
|
|
821
|
+
* argument to `onTestRunEnd`. Optional so hand-built fixtures
|
|
822
|
+
* and older replay data keep decoding. See issue #240.
|
|
823
|
+
*/
|
|
824
|
+
readonly unhandledErrors: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
825
|
+
readonly message: Schema.String;
|
|
826
|
+
readonly stack: Schema.optional<Schema.String>;
|
|
827
|
+
readonly diff: Schema.optional<Schema.String>;
|
|
828
|
+
readonly expected: Schema.optional<Schema.String>;
|
|
829
|
+
readonly received: Schema.optional<Schema.String>;
|
|
830
|
+
}>>>;
|
|
790
831
|
}>]>;
|
|
791
832
|
/** @public */
|
|
792
|
-
type RunEvent = typeof RunEvent.Type;
|
|
833
|
+
export type RunEvent = typeof RunEvent.Type;
|
|
793
834
|
/**
|
|
794
835
|
* Convenience: discriminator-keyed map of the individual variants.
|
|
795
836
|
*
|
|
@@ -1003,7 +1044,7 @@ interface Cell {
|
|
|
1003
1044
|
* @returns the run shape classification
|
|
1004
1045
|
* @public
|
|
1005
1046
|
*/
|
|
1006
|
-
declare const classifyRunShape: (state: RenderState, projects: ReadonlyArray<ProjectSummary>) => RunShape;
|
|
1047
|
+
export declare const classifyRunShape: (state: RenderState, projects: ReadonlyArray<ProjectSummary>) => RunShape;
|
|
1007
1048
|
/**
|
|
1008
1049
|
* Compute the outcome class from the reduced state.
|
|
1009
1050
|
*
|
|
@@ -1014,13 +1055,17 @@ declare const classifyRunShape: (state: RenderState, projects: ReadonlyArray<Pro
|
|
|
1014
1055
|
* run whose only non-passing signal is one or more timed-out tests
|
|
1015
1056
|
* (`totals.timeoutCount > 0`, `totals.failCount === 0`) also
|
|
1016
1057
|
* classifies as `some-fail` — timeouts are not passes, and must not
|
|
1017
|
-
* route to a passing cell (issue #224).
|
|
1058
|
+
* route to a passing cell (issue #224). A run whose only non-passing
|
|
1059
|
+
* signal is a process-level unhandled error (a non-empty
|
|
1060
|
+
* `unhandledErrors`, all counts otherwise clean) also classifies as `some-fail` —
|
|
1061
|
+
* a green-looking count must never hide an unhandled error behind an
|
|
1062
|
+
* all-pass cell (issue #240).
|
|
1018
1063
|
*
|
|
1019
1064
|
* @param state - the fully-reduced render state
|
|
1020
1065
|
* @returns the outcome classification
|
|
1021
1066
|
* @public
|
|
1022
1067
|
*/
|
|
1023
|
-
declare const classifyOutcome: (state: RenderState) => RunOutcome;
|
|
1068
|
+
export declare const classifyOutcome: (state: RenderState) => RunOutcome;
|
|
1024
1069
|
//#endregion
|
|
1025
1070
|
//#region src/dispatcher/dispatch.d.ts
|
|
1026
1071
|
/**
|
|
@@ -1030,7 +1075,7 @@ declare const classifyOutcome: (state: RenderState) => RunOutcome;
|
|
|
1030
1075
|
*
|
|
1031
1076
|
* @public
|
|
1032
1077
|
*/
|
|
1033
|
-
declare const dispatcherTable: Readonly<Record<RunShape, Readonly<Record<RunOutcome, Cell>>>>;
|
|
1078
|
+
export declare const dispatcherTable: Readonly<Record<RunShape, Readonly<Record<RunOutcome, Cell>>>>;
|
|
1034
1079
|
/**
|
|
1035
1080
|
* Dispatch to the cell that matches `(inputs.shape, inputs.outcome)`
|
|
1036
1081
|
* and return its agent-half string output.
|
|
@@ -1040,7 +1085,7 @@ declare const dispatcherTable: Readonly<Record<RunShape, Readonly<Record<RunOutc
|
|
|
1040
1085
|
* @returns the agent-string output for the matched cell
|
|
1041
1086
|
* @public
|
|
1042
1087
|
*/
|
|
1043
|
-
declare const dispatch: (inputs: DispatchInputs, opts: CellOptions) => string;
|
|
1088
|
+
export declare const dispatch: (inputs: DispatchInputs, opts: CellOptions) => string;
|
|
1044
1089
|
/**
|
|
1045
1090
|
* Dispatch to the cell that matches `(inputs.shape, inputs.outcome)`
|
|
1046
1091
|
* and return its Ink-half React tree. Returns `null` when the matched
|
|
@@ -1052,7 +1097,7 @@ declare const dispatch: (inputs: DispatchInputs, opts: CellOptions) => string;
|
|
|
1052
1097
|
* @returns the Ink React element, or `null` when the cell has no Ink half
|
|
1053
1098
|
* @public
|
|
1054
1099
|
*/
|
|
1055
|
-
declare const dispatchInk: (inputs: DispatchInputs, opts: CellOptions) => ReactElement | null;
|
|
1100
|
+
export declare const dispatchInk: (inputs: DispatchInputs, opts: CellOptions) => ReactElement | null;
|
|
1056
1101
|
//#endregion
|
|
1057
1102
|
//#region src/dispatcher/footer.d.ts
|
|
1058
1103
|
/**
|
|
@@ -1066,7 +1111,7 @@ declare const dispatchInk: (inputs: DispatchInputs, opts: CellOptions) => ReactE
|
|
|
1066
1111
|
* @returns the most actionable classification, or `null` when none applies
|
|
1067
1112
|
* @public
|
|
1068
1113
|
*/
|
|
1069
|
-
declare const dominantClassification: (state: RenderState) => TestClassification | null;
|
|
1114
|
+
export declare const dominantClassification: (state: RenderState) => TestClassification | null;
|
|
1070
1115
|
/**
|
|
1071
1116
|
* Compose the trailing footer lines for a dispatcher cell. Returns an
|
|
1072
1117
|
* empty string when no pointer applies. The footer starts with a
|
|
@@ -1077,7 +1122,7 @@ declare const dominantClassification: (state: RenderState) => TestClassification
|
|
|
1077
1122
|
* @returns the footer string, or an empty string when no pointer applies
|
|
1078
1123
|
* @public
|
|
1079
1124
|
*/
|
|
1080
|
-
declare const buildFooter: (inputs: DispatchInputs) => string;
|
|
1125
|
+
export declare const buildFooter: (inputs: DispatchInputs) => string;
|
|
1081
1126
|
//#endregion
|
|
1082
1127
|
//#region src/format-duration.d.ts
|
|
1083
1128
|
/**
|
|
@@ -1105,7 +1150,7 @@ declare const buildFooter: (inputs: DispatchInputs) => string;
|
|
|
1105
1150
|
* @returns formatted duration string
|
|
1106
1151
|
* @public
|
|
1107
1152
|
*/
|
|
1108
|
-
declare const formatDisplayDuration: (ms: number) => string;
|
|
1153
|
+
export declare const formatDisplayDuration: (ms: number) => string;
|
|
1109
1154
|
//#endregion
|
|
1110
1155
|
//#region src/pubsub/Channel.d.ts
|
|
1111
1156
|
declare const RunEventChannel_base: Context.ServiceClass<RunEventChannel, "vitest-agent-ui/RunEventChannel", PubSub.PubSub<{
|
|
@@ -1247,6 +1292,9 @@ declare const RunEventChannel_base: Context.ServiceClass<RunEventChannel, "vites
|
|
|
1247
1292
|
};
|
|
1248
1293
|
readonly uncoveredLines?: string | undefined;
|
|
1249
1294
|
}[];
|
|
1295
|
+
readonly scoped?: boolean | undefined;
|
|
1296
|
+
readonly scopedFiles?: number | undefined;
|
|
1297
|
+
readonly totalFiles?: number | undefined;
|
|
1250
1298
|
} | {
|
|
1251
1299
|
readonly _tag: "ThresholdViolation";
|
|
1252
1300
|
readonly metric: "branches" | "functions" | "lines" | "statements";
|
|
@@ -1273,6 +1321,13 @@ declare const RunEventChannel_base: Context.ServiceClass<RunEventChannel, "vites
|
|
|
1273
1321
|
readonly durationMs: number;
|
|
1274
1322
|
readonly timeoutCount?: number | undefined;
|
|
1275
1323
|
readonly collectedModules?: number | undefined;
|
|
1324
|
+
readonly unhandledErrors?: readonly {
|
|
1325
|
+
readonly message: string;
|
|
1326
|
+
readonly stack?: string | undefined;
|
|
1327
|
+
readonly diff?: string | undefined;
|
|
1328
|
+
readonly expected?: string | undefined;
|
|
1329
|
+
readonly received?: string | undefined;
|
|
1330
|
+
}[] | undefined;
|
|
1276
1331
|
}>>;
|
|
1277
1332
|
/**
|
|
1278
1333
|
* Service tag for the run-event channel. Consumers depend on this tag;
|
|
@@ -1281,7 +1336,7 @@ declare const RunEventChannel_base: Context.ServiceClass<RunEventChannel, "vites
|
|
|
1281
1336
|
*
|
|
1282
1337
|
* @public
|
|
1283
1338
|
*/
|
|
1284
|
-
declare class RunEventChannel extends RunEventChannel_base {}
|
|
1339
|
+
export declare class RunEventChannel extends RunEventChannel_base {}
|
|
1285
1340
|
/**
|
|
1286
1341
|
* Default live layer providing an unbounded Effect `PubSub` for
|
|
1287
1342
|
* the run-event channel. The PubSub is scoped — when the surrounding
|
|
@@ -1297,7 +1352,7 @@ declare class RunEventChannel extends RunEventChannel_base {}
|
|
|
1297
1352
|
*
|
|
1298
1353
|
* @public
|
|
1299
1354
|
*/
|
|
1300
|
-
declare const RunEventChannelLive: Layer.Layer<RunEventChannel>;
|
|
1355
|
+
export declare const RunEventChannelLive: Layer.Layer<RunEventChannel>;
|
|
1301
1356
|
//#endregion
|
|
1302
1357
|
//#region src/pubsub/Publisher.d.ts
|
|
1303
1358
|
/**
|
|
@@ -1311,7 +1366,7 @@ declare const RunEventChannelLive: Layer.Layer<RunEventChannel>;
|
|
|
1311
1366
|
* @returns an Effect that resolves to the publish success boolean
|
|
1312
1367
|
* @public
|
|
1313
1368
|
*/
|
|
1314
|
-
declare const publish: (event: RunEvent) => Effect.Effect<boolean, never, RunEventChannel>;
|
|
1369
|
+
export declare const publish: (event: RunEvent) => Effect.Effect<boolean, never, RunEventChannel>;
|
|
1315
1370
|
/**
|
|
1316
1371
|
* Emit a batch of events onto the channel in order.
|
|
1317
1372
|
*
|
|
@@ -1323,7 +1378,7 @@ declare const publish: (event: RunEvent) => Effect.Effect<boolean, never, RunEve
|
|
|
1323
1378
|
* @returns an Effect that resolves when all events are enqueued
|
|
1324
1379
|
* @public
|
|
1325
1380
|
*/
|
|
1326
|
-
declare const publishAll: (events: Iterable<RunEvent>) => Effect.Effect<void, never, RunEventChannel>;
|
|
1381
|
+
export declare const publishAll: (events: Iterable<RunEvent>) => Effect.Effect<void, never, RunEventChannel>;
|
|
1327
1382
|
//#endregion
|
|
1328
1383
|
//#region src/pubsub/Subscriber.d.ts
|
|
1329
1384
|
/**
|
|
@@ -1337,7 +1392,7 @@ declare const publishAll: (events: Iterable<RunEvent>) => Effect.Effect<void, ne
|
|
|
1337
1392
|
* @returns an Effect that resolves to the terminal render state
|
|
1338
1393
|
* @public
|
|
1339
1394
|
*/
|
|
1340
|
-
declare const accumulateUntilFinished: (initial?: RenderState) => Effect.Effect<RenderState, never, RunEventChannel>;
|
|
1395
|
+
export declare const accumulateUntilFinished: (initial?: RenderState) => Effect.Effect<RenderState, never, RunEventChannel>;
|
|
1341
1396
|
/**
|
|
1342
1397
|
* Run a callback for every projected `RenderState` as events
|
|
1343
1398
|
* arrive. The stream terminates when the PubSub shuts down (typically
|
|
@@ -1353,7 +1408,7 @@ declare const accumulateUntilFinished: (initial?: RenderState) => Effect.Effect<
|
|
|
1353
1408
|
* @returns an Effect that completes when the PubSub shuts down
|
|
1354
1409
|
* @public
|
|
1355
1410
|
*/
|
|
1356
|
-
declare const forEachRenderState: <R, E>(onState: (state: RenderState) => Effect.Effect<void, E, R>, initial?: RenderState) => Effect.Effect<void, E, R | RunEventChannel>;
|
|
1411
|
+
export declare const forEachRenderState: <R, E>(onState: (state: RenderState) => Effect.Effect<void, E, R>, initial?: RenderState) => Effect.Effect<void, E, R | RunEventChannel>;
|
|
1357
1412
|
/**
|
|
1358
1413
|
* Build an Effect `Stream` of accumulated states over the channel.
|
|
1359
1414
|
*
|
|
@@ -1365,7 +1420,7 @@ declare const forEachRenderState: <R, E>(onState: (state: RenderState) => Effect
|
|
|
1365
1420
|
* @returns an Effect that resolves to a Stream of render states
|
|
1366
1421
|
* @public
|
|
1367
1422
|
*/
|
|
1368
|
-
declare const renderStateStream: (initial?: RenderState) => Effect.Effect<Stream.Stream<RenderState>, never, RunEventChannel>;
|
|
1423
|
+
export declare const renderStateStream: (initial?: RenderState) => Effect.Effect<Stream.Stream<RenderState>, never, RunEventChannel>;
|
|
1369
1424
|
/**
|
|
1370
1425
|
* Convenience: subscribe and produce a low-level dequeue of raw
|
|
1371
1426
|
* `RunEvent` values. Mostly for tests and ad-hoc consumers; most
|
|
@@ -1375,7 +1430,7 @@ declare const renderStateStream: (initial?: RenderState) => Effect.Effect<Stream
|
|
|
1375
1430
|
* @returns an Effect that resolves to a dequeue of raw run events
|
|
1376
1431
|
* @public
|
|
1377
1432
|
*/
|
|
1378
|
-
declare const subscribeRaw: () => Effect.Effect<PubSub.Subscription<RunEvent>, never, RunEventChannel | Scope.Scope>;
|
|
1433
|
+
export declare const subscribeRaw: () => Effect.Effect<PubSub.Subscription<RunEvent>, never, RunEventChannel | Scope.Scope>;
|
|
1379
1434
|
//#endregion
|
|
1380
1435
|
//#region src/reducer.d.ts
|
|
1381
1436
|
/**
|
|
@@ -1390,7 +1445,7 @@ declare const subscribeRaw: () => Effect.Effect<PubSub.Subscription<RunEvent>, n
|
|
|
1390
1445
|
* @returns the next render state
|
|
1391
1446
|
* @public
|
|
1392
1447
|
*/
|
|
1393
|
-
declare const reduceRenderState: (state: RenderState, event: RunEvent) => RenderState;
|
|
1448
|
+
export declare const reduceRenderState: (state: RenderState, event: RunEvent) => RenderState;
|
|
1394
1449
|
/**
|
|
1395
1450
|
* Fold a sequence of events into a final `RenderState`.
|
|
1396
1451
|
*
|
|
@@ -1402,7 +1457,7 @@ declare const reduceRenderState: (state: RenderState, event: RunEvent) => Render
|
|
|
1402
1457
|
* @returns the terminal render state after all events are applied
|
|
1403
1458
|
* @public
|
|
1404
1459
|
*/
|
|
1405
|
-
declare const reduceRenderStateAll: (events: ReadonlyArray<RunEvent>, seed?: RenderState) => RenderState;
|
|
1460
|
+
export declare const reduceRenderStateAll: (events: ReadonlyArray<RunEvent>, seed?: RenderState) => RenderState;
|
|
1406
1461
|
//#endregion
|
|
1407
1462
|
//#region src/render-agent.d.ts
|
|
1408
1463
|
/**
|
|
@@ -1449,7 +1504,7 @@ interface RenderAgentOptions {
|
|
|
1449
1504
|
* @returns the agent-format string
|
|
1450
1505
|
* @public
|
|
1451
1506
|
*/
|
|
1452
|
-
declare const renderAgent: (state: RenderState, options?: RenderAgentOptions) => string;
|
|
1507
|
+
export declare const renderAgent: (state: RenderState, options?: RenderAgentOptions) => string;
|
|
1453
1508
|
//#endregion
|
|
1454
1509
|
//#region src/render-ink/CountColumns.d.ts
|
|
1455
1510
|
/**
|
|
@@ -1474,7 +1529,7 @@ interface CountColumnsProps {
|
|
|
1474
1529
|
*
|
|
1475
1530
|
* @public
|
|
1476
1531
|
*/
|
|
1477
|
-
declare const DURATION_CELL_WIDTH = 7;
|
|
1532
|
+
export declare const DURATION_CELL_WIDTH = 7;
|
|
1478
1533
|
/**
|
|
1479
1534
|
* Renders four colored count columns (pass ✓, fail ✗, skip ↷, timeout ⧖)
|
|
1480
1535
|
* for an aggregate row. Counts render right-aligned in fixed 4-digit cells so
|
|
@@ -1483,7 +1538,7 @@ declare const DURATION_CELL_WIDTH = 7;
|
|
|
1483
1538
|
*
|
|
1484
1539
|
* @public
|
|
1485
1540
|
*/
|
|
1486
|
-
declare const CountColumns: FC<CountColumnsProps>;
|
|
1541
|
+
export declare const CountColumns: FC<CountColumnsProps>;
|
|
1487
1542
|
//#endregion
|
|
1488
1543
|
//#region src/render-ink/CoverageBlock.d.ts
|
|
1489
1544
|
/**
|
|
@@ -1503,7 +1558,7 @@ interface CoverageBlockProps {
|
|
|
1503
1558
|
*
|
|
1504
1559
|
* @public
|
|
1505
1560
|
*/
|
|
1506
|
-
declare const CoverageBlock: FC<CoverageBlockProps>;
|
|
1561
|
+
export declare const CoverageBlock: FC<CoverageBlockProps>;
|
|
1507
1562
|
//#endregion
|
|
1508
1563
|
//#region src/render-ink/FailureSection.d.ts
|
|
1509
1564
|
/**
|
|
@@ -1523,7 +1578,7 @@ interface FailureSectionProps {
|
|
|
1523
1578
|
*
|
|
1524
1579
|
* @public
|
|
1525
1580
|
*/
|
|
1526
|
-
declare const FailureSection: FC<FailureSectionProps>;
|
|
1581
|
+
export declare const FailureSection: FC<FailureSectionProps>;
|
|
1527
1582
|
//#endregion
|
|
1528
1583
|
//#region src/render-ink/FailuresSection.d.ts
|
|
1529
1584
|
/**
|
|
@@ -1543,7 +1598,7 @@ interface FailuresSectionProps {
|
|
|
1543
1598
|
*
|
|
1544
1599
|
* @public
|
|
1545
1600
|
*/
|
|
1546
|
-
declare const FailuresSection: FC<FailuresSectionProps>;
|
|
1601
|
+
export declare const FailuresSection: FC<FailuresSectionProps>;
|
|
1547
1602
|
//#endregion
|
|
1548
1603
|
//#region src/render-ink/ModuleHeader.d.ts
|
|
1549
1604
|
/**
|
|
@@ -1561,7 +1616,7 @@ interface ModuleHeaderProps {
|
|
|
1561
1616
|
*
|
|
1562
1617
|
* @public
|
|
1563
1618
|
*/
|
|
1564
|
-
declare const ModuleHeader: FC<ModuleHeaderProps>;
|
|
1619
|
+
export declare const ModuleHeader: FC<ModuleHeaderProps>;
|
|
1565
1620
|
//#endregion
|
|
1566
1621
|
//#region src/render-ink/ProjectRow.d.ts
|
|
1567
1622
|
/**
|
|
@@ -1607,7 +1662,7 @@ interface ProjectRowProps {
|
|
|
1607
1662
|
*
|
|
1608
1663
|
* @public
|
|
1609
1664
|
*/
|
|
1610
|
-
declare const ProjectRow: FC<ProjectRowProps>;
|
|
1665
|
+
export declare const ProjectRow: FC<ProjectRowProps>;
|
|
1611
1666
|
//#endregion
|
|
1612
1667
|
//#region src/render-ink/StatusIcon.d.ts
|
|
1613
1668
|
/**
|
|
@@ -1630,7 +1685,7 @@ interface StatusIconProps {
|
|
|
1630
1685
|
*
|
|
1631
1686
|
* @public
|
|
1632
1687
|
*/
|
|
1633
|
-
declare const StatusIcon: FC<StatusIconProps>;
|
|
1688
|
+
export declare const StatusIcon: FC<StatusIconProps>;
|
|
1634
1689
|
//#endregion
|
|
1635
1690
|
//#region src/render-ink/StreamApp.d.ts
|
|
1636
1691
|
/**
|
|
@@ -1652,7 +1707,7 @@ interface StreamAppProps {
|
|
|
1652
1707
|
*
|
|
1653
1708
|
* @public
|
|
1654
1709
|
*/
|
|
1655
|
-
declare const StreamApp: FC<StreamAppProps>;
|
|
1710
|
+
export declare const StreamApp: FC<StreamAppProps>;
|
|
1656
1711
|
//#endregion
|
|
1657
1712
|
//#region src/render-ink/SuggestedActions.d.ts
|
|
1658
1713
|
/**
|
|
@@ -1670,7 +1725,7 @@ interface SuggestedActionsProps {
|
|
|
1670
1725
|
*
|
|
1671
1726
|
* @public
|
|
1672
1727
|
*/
|
|
1673
|
-
declare const SuggestedActions: FC<SuggestedActionsProps>;
|
|
1728
|
+
export declare const SuggestedActions: FC<SuggestedActionsProps>;
|
|
1674
1729
|
//#endregion
|
|
1675
1730
|
//#region src/render-ink/spinner.d.ts
|
|
1676
1731
|
/**
|
|
@@ -1690,13 +1745,13 @@ declare const SuggestedActions: FC<SuggestedActionsProps>;
|
|
|
1690
1745
|
*
|
|
1691
1746
|
* @public
|
|
1692
1747
|
*/
|
|
1693
|
-
declare const SPINNER_FRAMES: readonly ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
1748
|
+
export declare const SPINNER_FRAMES: readonly ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
1694
1749
|
/**
|
|
1695
1750
|
* How long each spinner frame is held, in milliseconds.
|
|
1696
1751
|
*
|
|
1697
1752
|
* @public
|
|
1698
1753
|
*/
|
|
1699
|
-
declare const SPINNER_FRAME_MS = 80;
|
|
1754
|
+
export declare const SPINNER_FRAME_MS = 80;
|
|
1700
1755
|
/**
|
|
1701
1756
|
* Resolve the spinner glyph for a frame index. The index wraps modulo
|
|
1702
1757
|
* the frame count and tolerates negative values, so a wall-clock-derived
|
|
@@ -1706,7 +1761,7 @@ declare const SPINNER_FRAME_MS = 80;
|
|
|
1706
1761
|
* @returns the Braille glyph for the given frame
|
|
1707
1762
|
* @public
|
|
1708
1763
|
*/
|
|
1709
|
-
declare const spinnerFrame: (index: number) => string;
|
|
1764
|
+
export declare const spinnerFrame: (index: number) => string;
|
|
1710
1765
|
/**
|
|
1711
1766
|
* Derive the spinner frame index from a wall-clock timestamp. Using the
|
|
1712
1767
|
* clock — rather than a monotonic counter — keeps the animation correct
|
|
@@ -1716,7 +1771,7 @@ declare const spinnerFrame: (index: number) => string;
|
|
|
1716
1771
|
* @returns the frame index for the given timestamp
|
|
1717
1772
|
* @public
|
|
1718
1773
|
*/
|
|
1719
|
-
declare const spinnerFrameForTime: (nowMs: number) => number;
|
|
1774
|
+
export declare const spinnerFrameForTime: (nowMs: number) => number;
|
|
1720
1775
|
//#endregion
|
|
1721
1776
|
//#region src/render-ink/TagColumns.d.ts
|
|
1722
1777
|
/**
|
|
@@ -1727,7 +1782,7 @@ declare const spinnerFrameForTime: (nowMs: number) => number;
|
|
|
1727
1782
|
*
|
|
1728
1783
|
* @public
|
|
1729
1784
|
*/
|
|
1730
|
-
declare const tagUnion: (rows: ReadonlyArray<Record<string, number> | undefined>) => ReadonlyArray<string>;
|
|
1785
|
+
export declare const tagUnion: (rows: ReadonlyArray<Record<string, number> | undefined>) => ReadonlyArray<string>;
|
|
1731
1786
|
/**
|
|
1732
1787
|
* Props for the `TagColumns` component.
|
|
1733
1788
|
*
|
|
@@ -1745,7 +1800,7 @@ interface TagColumnsProps {
|
|
|
1745
1800
|
*
|
|
1746
1801
|
* @public
|
|
1747
1802
|
*/
|
|
1748
|
-
declare const TagColumns: FC<TagColumnsProps>;
|
|
1803
|
+
export declare const TagColumns: FC<TagColumnsProps>;
|
|
1749
1804
|
//#endregion
|
|
1750
1805
|
//#region src/render-ink/TestRow.d.ts
|
|
1751
1806
|
/**
|
|
@@ -1764,7 +1819,7 @@ interface TestRowProps {
|
|
|
1764
1819
|
*
|
|
1765
1820
|
* @public
|
|
1766
1821
|
*/
|
|
1767
|
-
declare const TestRow: FC<TestRowProps>;
|
|
1822
|
+
export declare const TestRow: FC<TestRowProps>;
|
|
1768
1823
|
//#endregion
|
|
1769
1824
|
//#region src/render-ink/TrendLine.d.ts
|
|
1770
1825
|
/**
|
|
@@ -1784,7 +1839,7 @@ interface TrendLineProps {
|
|
|
1784
1839
|
*
|
|
1785
1840
|
* @public
|
|
1786
1841
|
*/
|
|
1787
|
-
declare const TrendLine: FC<TrendLineProps>;
|
|
1842
|
+
export declare const TrendLine: FC<TrendLineProps>;
|
|
1788
1843
|
//#endregion
|
|
1789
1844
|
//#region src/synthesize.d.ts
|
|
1790
1845
|
/**
|
|
@@ -1795,7 +1850,7 @@ declare const TrendLine: FC<TrendLineProps>;
|
|
|
1795
1850
|
*
|
|
1796
1851
|
* @public
|
|
1797
1852
|
*/
|
|
1798
|
-
declare const SUITE_LOAD_FAILURE_LABEL = "test suite failed to load";
|
|
1853
|
+
export declare const SUITE_LOAD_FAILURE_LABEL = "test suite failed to load";
|
|
1799
1854
|
/**
|
|
1800
1855
|
* Optional metadata threaded through the synthesized event stream.
|
|
1801
1856
|
*
|
|
@@ -1833,6 +1888,12 @@ interface SynthesizedCoverage {
|
|
|
1833
1888
|
readonly expected: number;
|
|
1834
1889
|
readonly actual: number;
|
|
1835
1890
|
}>;
|
|
1891
|
+
/** Set when this run's coverage was scoped to a subset of test files (issue #160). */
|
|
1892
|
+
readonly scoped?: boolean;
|
|
1893
|
+
/** Number of test files actually run, when `scoped` is true. */
|
|
1894
|
+
readonly scopedFiles?: number;
|
|
1895
|
+
/** Total test-file count for the project, when known. */
|
|
1896
|
+
readonly totalFiles?: number;
|
|
1836
1897
|
}
|
|
1837
1898
|
/**
|
|
1838
1899
|
* Project a fully-completed Vitest run into the canonical
|
|
@@ -1844,7 +1905,7 @@ interface SynthesizedCoverage {
|
|
|
1844
1905
|
* @returns an ordered array of run events
|
|
1845
1906
|
* @public
|
|
1846
1907
|
*/
|
|
1847
|
-
declare const synthesizeRunEvents: (modules: ReadonlyArray<VitestTestModule>, options?: SynthesizeOptions) => RunEvent[];
|
|
1908
|
+
export declare const synthesizeRunEvents: (modules: ReadonlyArray<VitestTestModule>, options?: SynthesizeOptions) => RunEvent[];
|
|
1848
1909
|
/**
|
|
1849
1910
|
* Options for {@link synthesizeFromAgentReport}.
|
|
1850
1911
|
*
|
|
@@ -1877,7 +1938,7 @@ interface SynthesizeFromAgentReportOptions {
|
|
|
1877
1938
|
* @returns an ordered array of run events
|
|
1878
1939
|
* @public
|
|
1879
1940
|
*/
|
|
1880
|
-
declare const synthesizeFromAgentReport: (report: AgentReport, options?: SynthesizeFromAgentReportOptions) => RunEvent[];
|
|
1941
|
+
export declare const synthesizeFromAgentReport: (report: AgentReport, options?: SynthesizeFromAgentReportOptions) => RunEvent[];
|
|
1881
1942
|
//#endregion
|
|
1882
1943
|
//#region src/index.d.ts
|
|
1883
1944
|
/**
|
|
@@ -1887,7 +1948,7 @@ declare const synthesizeFromAgentReport: (report: AgentReport, options?: Synthes
|
|
|
1887
1948
|
*
|
|
1888
1949
|
* @public
|
|
1889
1950
|
*/
|
|
1890
|
-
declare const CURRENT_UI_VERSION: string;
|
|
1951
|
+
export declare const CURRENT_UI_VERSION: string;
|
|
1891
1952
|
//#endregion
|
|
1892
|
-
export {
|
|
1953
|
+
export { type AgentCellFn, type AgentReport, type Cell, type CellOptions, type CountColumnsProps, type CoverageBlockProps, FileCoverageReport as CoverageFile, type CoverageTotals, type DispatchInputs, type FailureSectionProps, type FailuresSectionProps, type InkCellFn, type MetricThresholds, type ModuleHeaderProps, type ProjectRowProps, type ProjectSummary, type RenderAgentOptions, type RunEventByTag, type RunOutcome, type RunShape, type StatusIconKind, type StatusIconProps, type StreamAppProps, type SuggestedActionsProps, type SynthesizeFromAgentReportOptions, type SynthesizeOptions, type SynthesizedCoverage, type TagColumnsProps, type TestClassification, type TestRowProps, type TrendLineProps, type TrendSummary, type VitestModuleDiagnostic, type VitestModuleError, type VitestParsedStack, type VitestTestCase, type VitestTestDiagnostic, type VitestTestError, type VitestTestModule, type VitestTestResult, type VitestTestSuite };
|
|
1893
1954
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -33,7 +33,7 @@ import { ActionSeverity, CoverageFile, CoverageGap, CoverageMetric, CoverageRend
|
|
|
33
33
|
*
|
|
34
34
|
* @public
|
|
35
35
|
*/
|
|
36
|
-
const CURRENT_UI_VERSION = "2.
|
|
36
|
+
const CURRENT_UI_VERSION = "2.3.1";
|
|
37
37
|
|
|
38
38
|
//#endregion
|
|
39
39
|
export { ActionSeverity, CURRENT_UI_VERSION, CountColumns, CoverageBlock, CoverageFile, CoverageGap, CoverageMetric, CoverageRenderState, DURATION_CELL_WIDTH, FailureRecord, FailureSection, FailuresSection, ModuleHeader, ModuleRecord, ProjectRow, RenderState, RenderTotals, RunEvent, RunEventChannel, RunEventChannelLive, SPINNER_FRAMES, SPINNER_FRAME_MS, SUITE_LOAD_FAILURE_LABEL, StatusIcon, StreamApp, SuggestedActionRecord, SuggestedActions, TagColumns, TestRecord, TestRow, TrendLine, accumulateUntilFinished, buildFooter, classifyOutcome, classifyRunShape, dispatch, dispatchInk, dispatcherTable, dominantClassification, forEachRenderState, formatDisplayDuration, initialRenderState, publish, publishAll, reduceRenderState, reduceRenderStateAll, renderAgent, renderStateStream, spinnerFrame, spinnerFrameForTime, subscribeRaw, synthesizeFromAgentReport, synthesizeRunEvents, tagUnion };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared event-sourced renderer for vitest-agent. React Ink view for humans, plain-string view for agents, both fed by the same reducer over a RunEvent stream.",
|
|
6
6
|
"keywords": [
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@vitest-agent/sdk": "2.
|
|
43
|
-
"effect": "4.0.0-rc.
|
|
42
|
+
"@vitest-agent/sdk": "2.5.1",
|
|
43
|
+
"effect": "4.0.0-rc.112"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"ink": "^7.1.0",
|
package/reducer.js
CHANGED
|
@@ -175,7 +175,10 @@ const reduceRenderState = (state, event) => {
|
|
|
175
175
|
metrics: e.metrics,
|
|
176
176
|
thresholds: e.thresholds,
|
|
177
177
|
gaps: e.gaps,
|
|
178
|
-
violations: state.coverage?.violations ?? []
|
|
178
|
+
violations: state.coverage?.violations ?? [],
|
|
179
|
+
...e.scoped !== void 0 ? { scoped: e.scoped } : {},
|
|
180
|
+
...e.scopedFiles !== void 0 ? { scopedFiles: e.scopedFiles } : {},
|
|
181
|
+
...e.totalFiles !== void 0 ? { totalFiles: e.totalFiles } : {}
|
|
179
182
|
}
|
|
180
183
|
}),
|
|
181
184
|
ThresholdViolation: (e) => {
|
|
@@ -219,7 +222,8 @@ const reduceRenderState = (state, event) => {
|
|
|
219
222
|
timeoutCount: e.timeoutCount ?? state.totals.timeoutCount,
|
|
220
223
|
durationMs: e.durationMs
|
|
221
224
|
},
|
|
222
|
-
...e.collectedModules !== void 0 && { collectedModules: e.collectedModules }
|
|
225
|
+
...e.collectedModules !== void 0 && { collectedModules: e.collectedModules },
|
|
226
|
+
...e.unhandledErrors !== void 0 && { unhandledErrors: e.unhandledErrors }
|
|
223
227
|
}),
|
|
224
228
|
RunTimedOut: () => ({
|
|
225
229
|
...state,
|
package/render-agent.js
CHANGED
|
@@ -14,6 +14,10 @@ const formatHeader = (state) => {
|
|
|
14
14
|
if (failCount > 0) parts.push(`${failCount} failed`);
|
|
15
15
|
if (timeoutCount > 0) parts.push(`${timeoutCount} timed out`);
|
|
16
16
|
if (skipCount > 0) parts.push(`${skipCount} skipped`);
|
|
17
|
+
if (state.unhandledErrors.length > 0) {
|
|
18
|
+
const noun = state.unhandledErrors.length === 1 ? "unhandled error" : "unhandled errors";
|
|
19
|
+
parts.push(`${state.unhandledErrors.length} ${noun}`);
|
|
20
|
+
}
|
|
17
21
|
return `Tests: ${parts.join(", ")} (${formatDisplayDuration(durationMs)})`;
|
|
18
22
|
};
|
|
19
23
|
const formatModulesSection = (state) => {
|
|
@@ -61,6 +65,18 @@ const formatFailuresSection = (state, width, includeStack) => {
|
|
|
61
65
|
for (const f of state.failures) lines.push(formatFailure(f, width, includeStack));
|
|
62
66
|
return lines.join("\n");
|
|
63
67
|
};
|
|
68
|
+
const formatUnhandledError = (err, width, includeStack) => {
|
|
69
|
+
const firstLine = err.message.split("\n", 1)[0] ?? "";
|
|
70
|
+
const lines = [`- ${truncate(firstLine, Math.max(20, width - 2))}`];
|
|
71
|
+
if (includeStack && err.stack !== void 0) for (const stackLine of err.stack.split("\n")) lines.push(` ${truncate(stackLine, Math.max(20, width - 2))}`);
|
|
72
|
+
return lines.join("\n");
|
|
73
|
+
};
|
|
74
|
+
const formatUnhandledErrorsSection = (state, width, includeStack) => {
|
|
75
|
+
if (state.unhandledErrors.length === 0) return null;
|
|
76
|
+
const lines = ["Unhandled errors:"];
|
|
77
|
+
for (const err of state.unhandledErrors) lines.push(formatUnhandledError(err, width, includeStack));
|
|
78
|
+
return lines.join("\n");
|
|
79
|
+
};
|
|
64
80
|
const formatPercent = (n) => {
|
|
65
81
|
const rounded = Math.round(n * 10) / 10;
|
|
66
82
|
return Number.isInteger(rounded) ? `${rounded}%` : `${rounded}%`;
|
|
@@ -128,6 +144,8 @@ const renderAgent = (state, options = {}) => {
|
|
|
128
144
|
const sections = [formatHeader(state)];
|
|
129
145
|
const failures = formatFailuresSection(state, width, includeStack);
|
|
130
146
|
if (failures !== null) sections.push(failures);
|
|
147
|
+
const unhandledErrors = formatUnhandledErrorsSection(state, width, includeStack);
|
|
148
|
+
if (unhandledErrors !== null) sections.push(unhandledErrors);
|
|
131
149
|
const modules = formatModulesSection(state);
|
|
132
150
|
if (modules !== null) sections.push(modules);
|
|
133
151
|
const coverage = formatCoverageSection(state, maxGaps);
|
package/render-ink/StreamApp.js
CHANGED
|
@@ -58,6 +58,21 @@ const sumCounts = (modules) => {
|
|
|
58
58
|
timeoutCount
|
|
59
59
|
};
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Build a `ProjectSummary` from a project's name and its rolled-up
|
|
63
|
+
* counts. Includes `timeoutCount` only when nonzero, matching
|
|
64
|
+
* `summarizeProject`'s convention in `@vitest-agent/reporter` (issue #242).
|
|
65
|
+
*
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
68
|
+
const buildProjectSummary = (name, counts) => ({
|
|
69
|
+
name,
|
|
70
|
+
passCount: counts.passCount,
|
|
71
|
+
failCount: counts.failCount,
|
|
72
|
+
skipCount: counts.skipCount,
|
|
73
|
+
durationMs: 0,
|
|
74
|
+
...counts.timeoutCount > 0 ? { timeoutCount: counts.timeoutCount } : {}
|
|
75
|
+
});
|
|
61
76
|
const mergeTagCounts = (modules) => {
|
|
62
77
|
const out = {};
|
|
63
78
|
for (const m of modules) for (const [tag, count] of Object.entries(m.tagCounts ?? {})) out[tag] = (out[tag] ?? 0) + count;
|
|
@@ -166,6 +181,21 @@ const InlineError = ({ failure }) => {
|
|
|
166
181
|
});
|
|
167
182
|
};
|
|
168
183
|
const failureKey = (f) => `failure:${f.modulePath}::${f.suitePath.join("/")}::${f.testName}`;
|
|
184
|
+
/** A process-level unhandled error, rendered in the aggregate Unhandled errors block. */
|
|
185
|
+
const UnhandledErrorItem = ({ error }) => /* @__PURE__ */ jsxs(Box, {
|
|
186
|
+
flexDirection: "column",
|
|
187
|
+
children: [/* @__PURE__ */ jsxs(Text, { children: [
|
|
188
|
+
" ",
|
|
189
|
+
/* @__PURE__ */ jsx(Text, {
|
|
190
|
+
color: "red",
|
|
191
|
+
children: "✗"
|
|
192
|
+
}),
|
|
193
|
+
" unhandled error"
|
|
194
|
+
] }), /* @__PURE__ */ jsxs(Text, {
|
|
195
|
+
dimColor: true,
|
|
196
|
+
children: [" ", error.message.split("\n", 1)[0] ?? ""]
|
|
197
|
+
})]
|
|
198
|
+
});
|
|
169
199
|
const testKey = (modulePath, t) => `test:${modulePath}::${t.suitePath.join("/")}::${t.testName}`;
|
|
170
200
|
const failurePath = (f) => [
|
|
171
201
|
f.modulePath,
|
|
@@ -248,6 +278,13 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
248
278
|
const timedOut = state.phase === "timed-out";
|
|
249
279
|
const finished = state.phase === "finished" || state.phase === "timed-out";
|
|
250
280
|
const ordered = state.moduleOrder.map((p) => state.modules[p]).filter((m) => m !== void 0);
|
|
281
|
+
const unhandledErrorsSection = state.unhandledErrors.length > 0 ? /* @__PURE__ */ jsxs(Box, {
|
|
282
|
+
flexDirection: "column",
|
|
283
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
284
|
+
bold: true,
|
|
285
|
+
children: "Unhandled errors:"
|
|
286
|
+
}), state.unhandledErrors.map((err, i) => /* @__PURE__ */ jsx(UnhandledErrorItem, { error: err }, `unhandled:${i}:${err.message}`))]
|
|
287
|
+
}) : null;
|
|
251
288
|
if (shape === "single-test") {
|
|
252
289
|
const sole = ordered[0];
|
|
253
290
|
if (sole === void 0 || sole.tests.length === 0) return /* @__PURE__ */ jsx(Text, {
|
|
@@ -260,10 +297,14 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
260
297
|
children: "discovering tests…"
|
|
261
298
|
});
|
|
262
299
|
const failure = only.status === "failed" || only.status === "timed-out" ? state.failures.find((f) => f.testName === only.testName && f.modulePath === sole.modulePath && samePath(f.suitePath, only.suitePath)) : void 0;
|
|
263
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
300
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
301
|
+
/* @__PURE__ */ jsx(TestRow, {
|
|
302
|
+
test: only,
|
|
303
|
+
indent: 0
|
|
304
|
+
}),
|
|
305
|
+
failure !== void 0 ? /* @__PURE__ */ jsx(InlineError, { failure }) : null,
|
|
306
|
+
unhandledErrorsSection
|
|
307
|
+
] });
|
|
267
308
|
}
|
|
268
309
|
if (groups.length === 0) return /* @__PURE__ */ jsx(Text, {
|
|
269
310
|
dimColor: true,
|
|
@@ -297,13 +338,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
297
338
|
if (runningVisible < MAX_LIVE_RUNNING_ROWS) {
|
|
298
339
|
runningVisible++;
|
|
299
340
|
const counts = sumCounts(g.modules);
|
|
300
|
-
const summary =
|
|
301
|
-
name: g.name,
|
|
302
|
-
passCount: counts.passCount,
|
|
303
|
-
failCount: counts.failCount,
|
|
304
|
-
skipCount: counts.skipCount,
|
|
305
|
-
durationMs: 0
|
|
306
|
-
};
|
|
341
|
+
const summary = buildProjectSummary(g.name, counts);
|
|
307
342
|
rows.push(/* @__PURE__ */ jsx(ProjectRow, {
|
|
308
343
|
project: summary,
|
|
309
344
|
counts,
|
|
@@ -318,13 +353,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
318
353
|
} else runningOverflow++;
|
|
319
354
|
} else {
|
|
320
355
|
const counts = sumCounts(g.modules);
|
|
321
|
-
const summary =
|
|
322
|
-
name: g.name,
|
|
323
|
-
passCount: counts.passCount,
|
|
324
|
-
failCount: counts.failCount,
|
|
325
|
-
skipCount: counts.skipCount,
|
|
326
|
-
durationMs: 0
|
|
327
|
-
};
|
|
356
|
+
const summary = buildProjectSummary(g.name, counts);
|
|
328
357
|
rows.push(/* @__PURE__ */ jsx(ProjectRow, {
|
|
329
358
|
project: summary,
|
|
330
359
|
counts,
|
|
@@ -356,6 +385,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
356
385
|
]
|
|
357
386
|
}) : null,
|
|
358
387
|
failuresSection,
|
|
388
|
+
unhandledErrorsSection,
|
|
359
389
|
coverageItem,
|
|
360
390
|
trendItem,
|
|
361
391
|
totalsItem(nameWidth + 4)
|
|
@@ -406,6 +436,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
406
436
|
]
|
|
407
437
|
}) : null,
|
|
408
438
|
failuresSection,
|
|
439
|
+
unhandledErrorsSection,
|
|
409
440
|
coverageItem,
|
|
410
441
|
trendItem,
|
|
411
442
|
totalsItem()
|
|
@@ -438,6 +469,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
438
469
|
}), failure !== void 0 ? /* @__PURE__ */ jsx(InlineError, { failure }) : null]
|
|
439
470
|
}, testKey(sole.modulePath, t));
|
|
440
471
|
}),
|
|
472
|
+
unhandledErrorsSection,
|
|
441
473
|
coverageItem,
|
|
442
474
|
trendItem,
|
|
443
475
|
totalsItem()
|
|
@@ -452,16 +484,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
452
484
|
const StreamApp = ({ state, frameIndex, nowMs }) => {
|
|
453
485
|
const now = nowMs ?? Date.now();
|
|
454
486
|
const frame = spinnerFrame(frameIndex);
|
|
455
|
-
const projects = groupByProject(state).map((g) =>
|
|
456
|
-
const c = sumCounts(g.modules);
|
|
457
|
-
return {
|
|
458
|
-
name: g.name,
|
|
459
|
-
passCount: c.passCount,
|
|
460
|
-
failCount: c.failCount,
|
|
461
|
-
skipCount: c.skipCount,
|
|
462
|
-
durationMs: 0
|
|
463
|
-
};
|
|
464
|
-
});
|
|
487
|
+
const projects = groupByProject(state).map((g) => buildProjectSummary(g.name, sumCounts(g.modules)));
|
|
465
488
|
const shape = classifyRunShape(state, projects);
|
|
466
489
|
const liveContent = liveRegion(state, shape, now, frame);
|
|
467
490
|
return /* @__PURE__ */ jsx(Box, {
|
|
@@ -471,4 +494,4 @@ const StreamApp = ({ state, frameIndex, nowMs }) => {
|
|
|
471
494
|
};
|
|
472
495
|
|
|
473
496
|
//#endregion
|
|
474
|
-
export { StreamApp };
|
|
497
|
+
export { StreamApp, buildProjectSummary };
|
package/synthesize.js
CHANGED
|
@@ -147,7 +147,10 @@ const synthesizeRunEvents = (modules, options = {}) => {
|
|
|
147
147
|
_tag: "CoverageReady",
|
|
148
148
|
metrics: cov.metrics,
|
|
149
149
|
thresholds: cov.thresholds,
|
|
150
|
-
gaps: cov.gaps
|
|
150
|
+
gaps: cov.gaps,
|
|
151
|
+
...cov.scoped !== void 0 ? { scoped: cov.scoped } : {},
|
|
152
|
+
...cov.scopedFiles !== void 0 ? { scopedFiles: cov.scopedFiles } : {},
|
|
153
|
+
...cov.totalFiles !== void 0 ? { totalFiles: cov.totalFiles } : {}
|
|
151
154
|
});
|
|
152
155
|
if (cov.violations !== void 0) for (const v of cov.violations) events.push({
|
|
153
156
|
_tag: "ThresholdViolation",
|
|
@@ -189,7 +192,7 @@ const coverageReportToBlock = (report) => {
|
|
|
189
192
|
const cov = report.coverage;
|
|
190
193
|
if (cov === void 0) return void 0;
|
|
191
194
|
const violations = [];
|
|
192
|
-
for (const metric of [
|
|
195
|
+
if (!cov.scoped) for (const metric of [
|
|
193
196
|
"lines",
|
|
194
197
|
"branches",
|
|
195
198
|
"functions",
|
|
@@ -212,7 +215,10 @@ const coverageReportToBlock = (report) => {
|
|
|
212
215
|
metrics: cov.totals,
|
|
213
216
|
thresholds: cov.thresholds.global,
|
|
214
217
|
gaps,
|
|
215
|
-
violations
|
|
218
|
+
violations,
|
|
219
|
+
...cov.scoped ? { scoped: cov.scoped } : {},
|
|
220
|
+
...cov.scopedFiles !== void 0 ? { scopedFiles: cov.scopedFiles.length } : {},
|
|
221
|
+
...cov.totalFiles !== void 0 ? { totalFiles: cov.totalFiles } : {}
|
|
216
222
|
};
|
|
217
223
|
};
|
|
218
224
|
/**
|
|
@@ -331,7 +337,10 @@ const synthesizeFromAgentReport = (report, options = {}) => {
|
|
|
331
337
|
_tag: "CoverageReady",
|
|
332
338
|
metrics: coverage.metrics,
|
|
333
339
|
thresholds: coverage.thresholds,
|
|
334
|
-
gaps: coverage.gaps
|
|
340
|
+
gaps: coverage.gaps,
|
|
341
|
+
...coverage.scoped !== void 0 ? { scoped: coverage.scoped } : {},
|
|
342
|
+
...coverage.scopedFiles !== void 0 ? { scopedFiles: coverage.scopedFiles } : {},
|
|
343
|
+
...coverage.totalFiles !== void 0 ? { totalFiles: coverage.totalFiles } : {}
|
|
335
344
|
});
|
|
336
345
|
if (coverage.violations !== void 0) for (const v of coverage.violations) events.push({
|
|
337
346
|
_tag: "ThresholdViolation",
|
|
@@ -362,7 +371,8 @@ const synthesizeFromAgentReport = (report, options = {}) => {
|
|
|
362
371
|
skipCount: report.summary.skipped,
|
|
363
372
|
durationMs: report.summary.duration,
|
|
364
373
|
...totalTimeoutCount > 0 && { timeoutCount: totalTimeoutCount },
|
|
365
|
-
...report.summary.modules !== void 0 && { collectedModules: report.summary.modules }
|
|
374
|
+
...report.summary.modules !== void 0 && { collectedModules: report.summary.modules },
|
|
375
|
+
...report.unhandledErrors.length > 0 && { unhandledErrors: report.unhandledErrors }
|
|
366
376
|
});
|
|
367
377
|
return events;
|
|
368
378
|
};
|