@vitest-agent/reporter 5.0.9 → 6.0.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/README.md +1 -1
- package/defaultReporter.js +61 -67
- package/fileLinks.js +43 -0
- package/githubLog.js +35 -11
- package/humanReport.js +14 -9
- package/index.d.ts +256 -178
- package/index.js +3 -3
- package/liveView.js +26 -14
- package/package.json +5 -5
- package/projectSummary.js +41 -0
- package/streamView.js +9 -4
- package/tsdoc-metadata.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The default reporter package and the reference for custom-reporter authors. Ship
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
13
|
- **`DefaultVitestAgentReporter`** — the production default `VitestAgentReporterFactory` the plugin wires automatically; classifies runs into one of 12 shape × outcome cells, owns the Ink live-mount lifecycle, and emits a GitHub Actions Step Summary in CI
|
|
14
|
-
- **Dispatch helpers** — `buildDispatchInputs`, `
|
|
14
|
+
- **Dispatch helpers** — `buildDispatchInputs`, `renderAgentStringForReport`, `renderHumanStringForReport`
|
|
15
15
|
- **Contract re-exports** — `VitestAgentReporterFactory`, `ReporterKit`, `ReporterRenderInput`, `RenderedOutput`, `VitestAgentReporter`, `ResolvedReporterConfig` all re-exported from `@vitest-agent/sdk` so you only need one import
|
|
16
16
|
- **Worked example** — `DefaultVitestAgentReporter` source in this package is the canonical reference for the `VitestAgentReporterFactory` contract
|
|
17
17
|
|
package/defaultReporter.js
CHANGED
|
@@ -1,45 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveFileLink } from "./fileLinks.js";
|
|
2
|
+
import { summarizeProject } from "./projectSummary.js";
|
|
3
|
+
import { renderGithubLog, strayOutputOf, toDisplayPath } from "./githubLog.js";
|
|
2
4
|
import { LiveViewEnv, startLiveView } from "./liveView.js";
|
|
3
|
-
import { Doc, Render, Status } from "@effected/cli";
|
|
4
|
-
import { RUN_REPORT_FILE_SCHEMA_URL,
|
|
5
|
+
import { Doc, Fmt, Render, Status } from "@effected/cli";
|
|
6
|
+
import { RUN_REPORT_FILE_SCHEMA_URL, formatStrayOutputNote } from "@vitest-agent/sdk";
|
|
5
7
|
import { classifyOutcome, classifyRunShape, dispatch, reduceRenderStateAll, synthesizeFromAgentReport } from "@vitest-agent/ui";
|
|
8
|
+
import { Effect } from "effect";
|
|
6
9
|
|
|
7
10
|
// src/defaultReporter.ts
|
|
8
|
-
const countTimeouts = (report) => {
|
|
9
|
-
let timeoutCount = 0;
|
|
10
|
-
for (const mod of report.failed) for (const test of mod.tests) {
|
|
11
|
-
const firstError = test.errors?.[0];
|
|
12
|
-
if (test.state === "failed" && firstError !== void 0 && isTimeoutError({ message: firstError.message })) timeoutCount++;
|
|
13
|
-
}
|
|
14
|
-
return timeoutCount;
|
|
15
|
-
};
|
|
16
|
-
const summarizeProject = (report) => {
|
|
17
|
-
const collapsedTagCounts = report.tagCounts !== void 0 ? collapseTagCounts(report.tagCounts) : void 0;
|
|
18
|
-
const tagCountsHasEntries = collapsedTagCounts !== void 0 && Object.keys(collapsedTagCounts).length > 0;
|
|
19
|
-
const belowTargetCount = report.coverage?.belowTargetFiles?.length;
|
|
20
|
-
const violationsCount = report.coverage !== void 0 && report.coverage.lowCoverage.length > 0 ? report.coverage.lowCoverage.length : void 0;
|
|
21
|
-
const timeoutCount = countTimeouts(report);
|
|
22
|
-
return {
|
|
23
|
-
name: report.project ?? "default",
|
|
24
|
-
passCount: report.summary.passed,
|
|
25
|
-
failCount: report.summary.failed + countSuiteFailures(report) - timeoutCount,
|
|
26
|
-
skipCount: report.summary.skipped,
|
|
27
|
-
durationMs: report.summary.duration,
|
|
28
|
-
...timeoutCount > 0 ? { timeoutCount } : {},
|
|
29
|
-
...tagCountsHasEntries && collapsedTagCounts !== void 0 ? { tagCounts: collapsedTagCounts } : {},
|
|
30
|
-
...belowTargetCount !== void 0 ? { belowTarget: belowTargetCount } : {},
|
|
31
|
-
...violationsCount !== void 0 ? { violations: violationsCount } : {}
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
const collapseTagCounts = (entries) => {
|
|
35
|
-
const out = {};
|
|
36
|
-
if (entries === void 0) return out;
|
|
37
|
-
for (const [tag, entry] of Object.entries(entries)) {
|
|
38
|
-
const total = (entry.passed ?? 0) + (entry.failed ?? 0) + (entry.skipped ?? 0);
|
|
39
|
-
if (total > 0) out[tag] = total;
|
|
40
|
-
}
|
|
41
|
-
return out;
|
|
42
|
-
};
|
|
43
11
|
const collectBelowTarget = (reports) => {
|
|
44
12
|
const out = [];
|
|
45
13
|
for (const r of reports) {
|
|
@@ -78,28 +46,20 @@ const buildDispatchInputs = (state, input, overrides = {}) => {
|
|
|
78
46
|
runCommand: overrides.runCommand ?? null
|
|
79
47
|
};
|
|
80
48
|
};
|
|
81
|
-
/**
|
|
82
|
-
* Build `CellOptions` from a {@link ReporterKit}. Picks the kit's
|
|
83
|
-
* resolved `noColor` value and the pre-bound OSC-8 hyperlink helper.
|
|
84
|
-
*
|
|
85
|
-
* @public
|
|
86
|
-
*/
|
|
87
|
-
const resolveCellOptions = (kit) => ({
|
|
88
|
-
noColor: kit.config.noColor,
|
|
89
|
-
osc8: kit.stdOsc8
|
|
90
|
-
});
|
|
91
49
|
const shouldRenderForMode = (mode) => mode === "agent";
|
|
50
|
+
const synthesizeOptionsFrom = (options) => ({ ...options.suggestedActions !== void 0 ? { suggestedActions: options.suggestedActions } : {} });
|
|
92
51
|
/**
|
|
93
52
|
* Convenience helper for one-shot consumers (e.g. a CLI command
|
|
94
53
|
* replaying a stored run). Synthesizes a minimal `ReporterRenderInput`
|
|
95
54
|
* from a single `AgentReport`, classifies the shape and outcome, and
|
|
96
55
|
* returns the dispatched agent-string for the matching cell. Equivalent
|
|
97
|
-
* to the pre-2.0 `renderRun(events, "agent")` shortcut.
|
|
56
|
+
* to the pre-2.0 `renderRun(events, "agent")` shortcut. Pass
|
|
57
|
+
* `options.suggestedActions` to append the run's `Actions:` list.
|
|
98
58
|
*
|
|
99
59
|
* @public
|
|
100
60
|
*/
|
|
101
|
-
const renderAgentStringForReport = (report) => {
|
|
102
|
-
const events = synthesizeFromAgentReport(report);
|
|
61
|
+
const renderAgentStringForReport = (report, options = {}) => {
|
|
62
|
+
const events = synthesizeFromAgentReport(report, synthesizeOptionsFrom(options));
|
|
103
63
|
const state = reduceRenderStateAll(events);
|
|
104
64
|
const projects = [summarizeProject(report)];
|
|
105
65
|
const inputs = {
|
|
@@ -111,10 +71,7 @@ const renderAgentStringForReport = (report) => {
|
|
|
111
71
|
belowTarget: collectBelowTarget([report]),
|
|
112
72
|
runCommand: null
|
|
113
73
|
};
|
|
114
|
-
return dispatch(inputs, {
|
|
115
|
-
noColor: true,
|
|
116
|
-
osc8: (_url, label) => label
|
|
117
|
-
});
|
|
74
|
+
return dispatch(inputs, {});
|
|
118
75
|
};
|
|
119
76
|
/**
|
|
120
77
|
* Same as {@link renderAgentStringForReport} but returns the Ink-half
|
|
@@ -140,7 +97,7 @@ const renderHumanStringForReport = (report, options = {}) => renderHumanString(r
|
|
|
140
97
|
* @internal
|
|
141
98
|
*/
|
|
142
99
|
const renderHumanString = async (report, options, env) => {
|
|
143
|
-
const events = synthesizeFromAgentReport(report);
|
|
100
|
+
const events = synthesizeFromAgentReport(report, synthesizeOptionsFrom(options));
|
|
144
101
|
const state = reduceRenderStateAll(events);
|
|
145
102
|
const projects = [summarizeProject(report)];
|
|
146
103
|
const inputs = {
|
|
@@ -152,12 +109,11 @@ const renderHumanString = async (report, options, env) => {
|
|
|
152
109
|
belowTarget: collectBelowTarget([report]),
|
|
153
110
|
runCommand: null
|
|
154
111
|
};
|
|
155
|
-
const opts = {
|
|
156
|
-
noColor: false,
|
|
157
|
-
osc8: (_url, label) => label
|
|
158
|
-
};
|
|
159
112
|
const { renderInkReport } = await import("./humanReport.js");
|
|
160
|
-
|
|
113
|
+
const rendered = await renderInkReport(inputs, env, options.width);
|
|
114
|
+
if (rendered !== null) return rendered;
|
|
115
|
+
const link = await Effect.runPromise(resolveFileLink.pipe(Effect.provide(env)));
|
|
116
|
+
return dispatch(inputs, { link });
|
|
161
117
|
};
|
|
162
118
|
const NON_STABLE_SUMMARY_CLASSIFICATIONS = [
|
|
163
119
|
"new-failure",
|
|
@@ -166,6 +122,8 @@ const NON_STABLE_SUMMARY_CLASSIFICATIONS = [
|
|
|
166
122
|
"recovered"
|
|
167
123
|
];
|
|
168
124
|
const MAX_SUMMARY_COVERAGE_ROWS = 10;
|
|
125
|
+
/** Sample lines shown in the step summary's stray-output section; the same cap the human note uses. */
|
|
126
|
+
const MAX_SUMMARY_STRAY_SAMPLES = 3;
|
|
169
127
|
/**
|
|
170
128
|
* The step summary and `summary.md` are files, not log lines, so their
|
|
171
129
|
* markdown context opts out of the `ci` default that neutralizes workflow
|
|
@@ -212,6 +170,42 @@ const renderTrendSection = (trendSummary) => {
|
|
|
212
170
|
return Render.markdown([Doc.heading(3, "Trend"), Doc.lines(lines)], summaryRenderContext);
|
|
213
171
|
};
|
|
214
172
|
/**
|
|
173
|
+
* Split a plain-text note on its backtick spans into inlines, so a span the
|
|
174
|
+
* shared formatter wrote as `` `x` `` renders as a code span in markdown
|
|
175
|
+
* rather than as the sanitizer's escaped literal backticks. An unmatched
|
|
176
|
+
* trailing backtick is kept as text.
|
|
177
|
+
*/
|
|
178
|
+
const inlineWithCodeSpans = (text) => {
|
|
179
|
+
const parts = text.split("`");
|
|
180
|
+
const inlines = [];
|
|
181
|
+
parts.forEach((part, index) => {
|
|
182
|
+
if (part.length === 0) return;
|
|
183
|
+
const insideSpan = index % 2 === 1 && index < parts.length - 1;
|
|
184
|
+
inlines.push(insideSpan ? Doc.code(part) : Doc.text(part));
|
|
185
|
+
});
|
|
186
|
+
return inlines;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* What the run wrote straight to the terminal, past Vitest's console capture
|
|
190
|
+
* (issue #578): the same headline the human and agent notes print, the byte
|
|
191
|
+
* count, then the sample lines as code spans (a sample is untrusted text, so
|
|
192
|
+
* it is never read as markdown). `null` when the run wrote nothing stray.
|
|
193
|
+
*/
|
|
194
|
+
const renderStrayOutputSection = (reports) => {
|
|
195
|
+
const stray = strayOutputOf(reports);
|
|
196
|
+
if (stray === void 0) return null;
|
|
197
|
+
const [headline = ""] = formatStrayOutputNote(stray);
|
|
198
|
+
const shown = stray.samples.slice(0, MAX_SUMMARY_STRAY_SAMPLES);
|
|
199
|
+
const lines = shown.map((sample) => [Doc.text(`${sample.stream}: `), Doc.code(sample.text)]);
|
|
200
|
+
const more = stray.total - shown.length;
|
|
201
|
+
if (more > 0 && shown.length > 0) lines.push([Doc.text(`(+${more} more)`)]);
|
|
202
|
+
return Render.markdown([
|
|
203
|
+
Doc.heading(3, "Stray output"),
|
|
204
|
+
Doc.paragraph(...inlineWithCodeSpans(`${headline} ${Fmt.plural(stray.bytes, "byte")} recorded.`)),
|
|
205
|
+
...lines.length > 0 ? [Doc.lines(lines)] : []
|
|
206
|
+
], summaryRenderContext);
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
215
209
|
* Per-project pass/fail/timeout/skip/duration table — the unconditional
|
|
216
210
|
* half of the summary body.
|
|
217
211
|
*
|
|
@@ -265,7 +259,7 @@ const renderTotalsSection = (reports) => {
|
|
|
265
259
|
* Assemble the vitest-agent markdown body shared by the GitHub step
|
|
266
260
|
* summary and the `summary.md` report file: a `## vitest-agent` heading,
|
|
267
261
|
* the always-present per-project totals table, then whichever of the
|
|
268
|
-
* classification, coverage, and
|
|
262
|
+
* classification, coverage, trend, and stray-output sections have content.
|
|
269
263
|
*
|
|
270
264
|
* Always returns markdown. An all-green run still gets a body — with
|
|
271
265
|
* Vitest's own job summary disabled, returning `null` here left the step
|
|
@@ -278,7 +272,8 @@ const buildSummaryMarkdown = (input) => {
|
|
|
278
272
|
const sections = [
|
|
279
273
|
renderClassificationsSection(input.classifications),
|
|
280
274
|
renderCoverageSection(input.reports),
|
|
281
|
-
renderTrendSection(input.trendSummary)
|
|
275
|
+
renderTrendSection(input.trendSummary),
|
|
276
|
+
renderStrayOutputSection(input.reports)
|
|
282
277
|
].filter((section) => section !== null);
|
|
283
278
|
return [
|
|
284
279
|
"## vitest-agent",
|
|
@@ -341,11 +336,10 @@ const DefaultVitestAgentReporter = (kit) => {
|
|
|
341
336
|
render(input, renderKit) {
|
|
342
337
|
const out = [];
|
|
343
338
|
if (shouldRenderForMode(renderKit.config.consoleMode)) {
|
|
344
|
-
const events = input.reports.flatMap((r) => synthesizeFromAgentReport(r));
|
|
339
|
+
const events = input.reports.flatMap((r) => synthesizeFromAgentReport(r, { ...input.suggestedActions !== void 0 ? { suggestedActions: input.suggestedActions } : {} }));
|
|
345
340
|
const state = reduceRenderStateAll(events);
|
|
346
341
|
const inputs = buildDispatchInputs(state, input, { runCommand: renderKit.config.runCommand ?? null });
|
|
347
|
-
const
|
|
348
|
-
const content = dispatch(inputs, opts);
|
|
342
|
+
const content = dispatch(inputs, {});
|
|
349
343
|
if (content.length > 0) out.push({
|
|
350
344
|
target: "stdout",
|
|
351
345
|
content,
|
|
@@ -378,4 +372,4 @@ const DefaultVitestAgentReporter = (kit) => {
|
|
|
378
372
|
};
|
|
379
373
|
};
|
|
380
374
|
|
|
381
|
-
export { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanString, renderHumanStringForReport
|
|
375
|
+
export { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanString, renderHumanStringForReport };
|
package/fileLinks.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { CliLinks } from "@effected/cli";
|
|
2
|
+
import { Effect, Option } from "effect";
|
|
3
|
+
import { Audience, TerminalEnv } from "@effected/env";
|
|
4
|
+
|
|
5
|
+
// src/fileLinks.ts
|
|
6
|
+
/**
|
|
7
|
+
* The file-link writer the reporter's human renders draw paths through
|
|
8
|
+
* (issue #541): `@effected/cli`'s `CliLinks.linker` over the services the
|
|
9
|
+
* reporter's environment carries.
|
|
10
|
+
*
|
|
11
|
+
* The kit decides everything about a link. `CliLinks` picks where a file
|
|
12
|
+
* opens (`vscode://file/<path>:<line>:<col>` when `TERM_PROGRAM` is VS Code
|
|
13
|
+
* or a `.vscode/` directory sits at the project root, `file://` otherwise,
|
|
14
|
+
* or nothing under `editorLinks: "off"`); `TerminalEnv` says whether stdout
|
|
15
|
+
* renders OSC 8 at all (a pipe, tmux, GNU screen, `NO_COLOR` and
|
|
16
|
+
* `NO_HYPERLINK` all say no); and the `Audience` rule keeps every escape
|
|
17
|
+
* away from an agent. The writer is pure, so it is resolved once and handed
|
|
18
|
+
* to the cells (`CellOptions.link`) and to the Ink tree (`FileLinkProvider`).
|
|
19
|
+
*
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
/** The writer an agent, a pipe or an environment without the kit's services gets: the label alone. */
|
|
23
|
+
const plainFileLink = (_target, label) => label;
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the writer from the environment, when it carries the kit's
|
|
26
|
+
* `CliLinks`, `TerminalEnv` and `Audience`; the label alone when any is
|
|
27
|
+
* missing (a test environment fixed to a theme only, say). It never fails
|
|
28
|
+
* and never requires: a reporter rendering without `CliEnv` keeps working,
|
|
29
|
+
* it just writes no link.
|
|
30
|
+
*/
|
|
31
|
+
const resolveFileLink = Effect.gen(function* () {
|
|
32
|
+
const links = yield* Effect.serviceOption(CliLinks);
|
|
33
|
+
const terminal = yield* Effect.serviceOption(TerminalEnv);
|
|
34
|
+
const audience = yield* Effect.serviceOption(Audience);
|
|
35
|
+
if (Option.isNone(links) || Option.isNone(terminal) || Option.isNone(audience)) return plainFileLink;
|
|
36
|
+
return CliLinks.linker({
|
|
37
|
+
links: links.value,
|
|
38
|
+
hyperlinks: terminal.value.stdout.hyperlinks,
|
|
39
|
+
audience: audience.value.kind
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export { plainFileLink, resolveFileLink };
|
package/githubLog.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { summarizeProject } from "./projectSummary.js";
|
|
2
|
+
import { Doc, Fmt, Render, Status } from "@effected/cli";
|
|
3
|
+
import { formatStrayOutputNote } from "@vitest-agent/sdk";
|
|
2
4
|
import { relative } from "node:path";
|
|
3
5
|
|
|
4
6
|
// src/githubLog.ts
|
|
@@ -17,14 +19,21 @@ import { relative } from "node:path";
|
|
|
17
19
|
* `::group::`) and rendered with `Render.githubLog`, which also neutralizes
|
|
18
20
|
* any workflow command a project name or path could otherwise inject.
|
|
19
21
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
22
|
+
* The per-project counts come from `summarizeProject`, the same projection
|
|
23
|
+
* the step-summary totals table renders, so a timed-out test reads
|
|
24
|
+
* `timed out` here as it does there instead of folding into `failed`.
|
|
23
25
|
*
|
|
24
26
|
* @internal
|
|
25
27
|
*/
|
|
26
28
|
const MAX_NAMED_FILES = 3;
|
|
27
29
|
/**
|
|
30
|
+
* The run's stray output. It is a run-level signal (the bytes arrive on one
|
|
31
|
+
* stream shared by every project), so every project report of a run carries
|
|
32
|
+
* the same value and the first one found is the run's; `undefined` when the
|
|
33
|
+
* run wrote nothing past Vitest's console capture.
|
|
34
|
+
*/
|
|
35
|
+
const strayOutputOf = (reports) => reports.find((report) => report.strayOutput !== void 0)?.strayOutput;
|
|
36
|
+
/**
|
|
28
37
|
* Renders a coverage-target file path relative to `process.cwd()` for
|
|
29
38
|
* display. Falls back to the original string unchanged when the relative
|
|
30
39
|
* path is empty or escapes the root (starts with `..`) rather than
|
|
@@ -41,30 +50,39 @@ const NON_STABLE_CLASSIFICATIONS = [
|
|
|
41
50
|
"flaky",
|
|
42
51
|
"recovered"
|
|
43
52
|
];
|
|
44
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* `<project>: <passed>/<total> passed, <failed> failed, <timed out> timed out, <skipped> skipped` — every counter
|
|
55
|
+
* shown, zeros included, read from the same `ProjectSummary` the totals table renders.
|
|
56
|
+
*/
|
|
45
57
|
const projectCounts = (report) => {
|
|
46
|
-
const {
|
|
58
|
+
const { passCount, failCount, skipCount, timeoutCount = 0 } = summarizeProject(report);
|
|
47
59
|
return Doc.counts({
|
|
48
60
|
label: report.project ?? "default",
|
|
49
61
|
layout: "inline",
|
|
50
|
-
total: () => total,
|
|
62
|
+
total: () => report.summary.total,
|
|
51
63
|
counters: [
|
|
52
64
|
Doc.counter(Status.core, "success", {
|
|
53
65
|
key: "passed",
|
|
54
66
|
label: "passed",
|
|
55
|
-
n:
|
|
67
|
+
n: passCount,
|
|
56
68
|
showZero: true
|
|
57
69
|
}),
|
|
58
70
|
Doc.counter(Status.core, "failure", {
|
|
59
71
|
key: "failed",
|
|
60
72
|
label: "failed",
|
|
61
|
-
n:
|
|
73
|
+
n: failCount,
|
|
74
|
+
showZero: true
|
|
75
|
+
}),
|
|
76
|
+
Doc.counter(Status.core, "warning", {
|
|
77
|
+
key: "timedOut",
|
|
78
|
+
label: "timed out",
|
|
79
|
+
n: timeoutCount,
|
|
62
80
|
showZero: true
|
|
63
81
|
}),
|
|
64
82
|
Doc.counter(Status.core, "skip", {
|
|
65
83
|
key: "skipped",
|
|
66
84
|
label: "skipped",
|
|
67
|
-
n:
|
|
85
|
+
n: skipCount,
|
|
68
86
|
showZero: true
|
|
69
87
|
})
|
|
70
88
|
]
|
|
@@ -82,6 +100,12 @@ function renderGithubLog(input, kit) {
|
|
|
82
100
|
for (const classification of input.classifications.values()) classificationCounts.set(classification, (classificationCounts.get(classification) ?? 0) + 1);
|
|
83
101
|
const classificationParts = NON_STABLE_CLASSIFICATIONS.filter((kind) => (classificationCounts.get(kind) ?? 0) > 0).map((kind) => `${kind}: ${classificationCounts.get(kind)}`);
|
|
84
102
|
if (classificationParts.length > 0) body.push(Doc.paragraph(`classifications: ${classificationParts.join(", ")}`));
|
|
103
|
+
const stray = strayOutputOf(input.reports);
|
|
104
|
+
if (stray !== void 0) {
|
|
105
|
+
const [headline, ...samples] = formatStrayOutputNote(stray);
|
|
106
|
+
body.push(Doc.paragraph(`${headline} ${Fmt.plural(stray.bytes, "byte")} recorded.`));
|
|
107
|
+
if (samples.length > 0) body.push(Doc.verbatim(samples.join("\n")));
|
|
108
|
+
}
|
|
85
109
|
if (kit.config.dbPath !== void 0) body.push(Doc.paragraph(`db: ${kit.config.dbPath}`));
|
|
86
110
|
const ctx = Render.contextOf({
|
|
87
111
|
audience: "ci",
|
|
@@ -94,4 +118,4 @@ function renderGithubLog(input, kit) {
|
|
|
94
118
|
};
|
|
95
119
|
}
|
|
96
120
|
|
|
97
|
-
export { renderGithubLog, toDisplayPath };
|
|
121
|
+
export { renderGithubLog, strayOutputOf, toDisplayPath };
|
package/humanReport.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveFileLink } from "./fileLinks.js";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
import { TerminalEnv } from "@effected/env";
|
|
4
|
-
import {
|
|
4
|
+
import { CliUi, UiProvider } from "@effected/cli/ui";
|
|
5
|
+
import { FileLinkProvider, dispatchInk } from "@vitest-agent/ui/ink";
|
|
5
6
|
import { renderToString } from "ink";
|
|
6
7
|
import { createElement } from "react";
|
|
7
8
|
|
|
@@ -14,22 +15,26 @@ const FALLBACK_COLUMNS = 80;
|
|
|
14
15
|
*
|
|
15
16
|
* The layout width is `width` when given, else the terminal's
|
|
16
17
|
* (`TerminalEnv.width`: stdout's columns, else `COLUMNS`, else 80 — so a
|
|
17
|
-
* non-TTY run lays out at 80).
|
|
18
|
+
* non-TTY run lays out at 80). File paths are drawn through the link writer
|
|
19
|
+
* the environment decides (`resolveFileLink`): the cell's own string gets it
|
|
20
|
+
* as `CellOptions.link`, and the tree through `FileLinkProvider`.
|
|
18
21
|
*
|
|
19
22
|
* @internal
|
|
20
23
|
*/
|
|
21
|
-
const renderInkReport = async (inputs,
|
|
22
|
-
const
|
|
23
|
-
if (element === null) return null;
|
|
24
|
-
const { context, columns } = await Effect.runPromise(Effect.gen(function* () {
|
|
24
|
+
const renderInkReport = async (inputs, env, width) => {
|
|
25
|
+
const { context, columns, link } = await Effect.runPromise(Effect.gen(function* () {
|
|
25
26
|
const context = yield* CliUi.context;
|
|
26
27
|
const terminal = yield* TerminalEnv;
|
|
28
|
+
const link = yield* resolveFileLink;
|
|
27
29
|
return {
|
|
28
30
|
context,
|
|
29
|
-
columns: width ?? terminal.width(FALLBACK_COLUMNS)
|
|
31
|
+
columns: width ?? terminal.width(FALLBACK_COLUMNS),
|
|
32
|
+
link
|
|
30
33
|
};
|
|
31
34
|
}).pipe(Effect.provide(env)));
|
|
32
|
-
|
|
35
|
+
const element = dispatchInk(inputs, { link });
|
|
36
|
+
if (element === null) return null;
|
|
37
|
+
return renderToString(createElement(UiProvider, { value: context }, createElement(FileLinkProvider, { link }, element)), { columns });
|
|
33
38
|
};
|
|
34
39
|
|
|
35
40
|
export { renderInkReport };
|
package/index.d.ts
CHANGED
|
@@ -8,15 +8,27 @@ import "@effected/env";
|
|
|
8
8
|
declare const FileCoverageReport: Schema.Struct<{
|
|
9
9
|
readonly file: Schema.String;
|
|
10
10
|
readonly summary: Schema.Struct<{
|
|
11
|
-
readonly statements: Schema.
|
|
12
|
-
readonly branches: Schema.
|
|
13
|
-
readonly functions: Schema.
|
|
14
|
-
readonly lines: Schema.
|
|
11
|
+
readonly statements: Schema.Finite;
|
|
12
|
+
readonly branches: Schema.Finite;
|
|
13
|
+
readonly functions: Schema.Finite;
|
|
14
|
+
readonly lines: Schema.Finite;
|
|
15
15
|
}>;
|
|
16
16
|
readonly uncoveredLines: Schema.String;
|
|
17
17
|
}>;
|
|
18
18
|
/** @public */
|
|
19
19
|
type FileCoverageReport = typeof FileCoverageReport.Type;
|
|
20
|
+
/**
|
|
21
|
+
* A `SuggestedAction` event, denormalized into the queue the renderer reads.
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
declare const SuggestedActionRecord: Schema.Struct<{
|
|
25
|
+
readonly severity: Schema.Literals<readonly ["info", "warn", "blocker"]>;
|
|
26
|
+
readonly title: Schema.String;
|
|
27
|
+
readonly detail: Schema.String;
|
|
28
|
+
readonly targetTool: Schema.optional<Schema.String>;
|
|
29
|
+
}>;
|
|
30
|
+
/** @public */
|
|
31
|
+
type SuggestedActionRecord = typeof SuggestedActionRecord.Type;
|
|
20
32
|
/**
|
|
21
33
|
* The renderer state. Both the human (Ink) and agent (string)
|
|
22
34
|
* renderers read this shape; the reducer is the only producer.
|
|
@@ -80,24 +92,24 @@ declare const RenderState: Schema.Struct<{
|
|
|
80
92
|
}>;
|
|
81
93
|
readonly coverage: Schema.NullOr<Schema.Struct<{
|
|
82
94
|
readonly metrics: Schema.Struct<{
|
|
83
|
-
readonly statements: Schema.
|
|
84
|
-
readonly branches: Schema.
|
|
85
|
-
readonly functions: Schema.
|
|
86
|
-
readonly lines: Schema.
|
|
95
|
+
readonly statements: Schema.Finite;
|
|
96
|
+
readonly branches: Schema.Finite;
|
|
97
|
+
readonly functions: Schema.Finite;
|
|
98
|
+
readonly lines: Schema.Finite;
|
|
87
99
|
}>;
|
|
88
100
|
readonly thresholds: Schema.Struct<{
|
|
89
|
-
readonly lines: Schema.optional<Schema.
|
|
90
|
-
readonly functions: Schema.optional<Schema.
|
|
91
|
-
readonly branches: Schema.optional<Schema.
|
|
92
|
-
readonly statements: Schema.optional<Schema.
|
|
101
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
102
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
103
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
104
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
93
105
|
}>;
|
|
94
106
|
readonly gaps: Schema.$Array<Schema.Struct<{
|
|
95
107
|
readonly file: Schema.String;
|
|
96
108
|
readonly missing: Schema.Struct<{
|
|
97
|
-
readonly statements: Schema.
|
|
98
|
-
readonly branches: Schema.
|
|
99
|
-
readonly functions: Schema.
|
|
100
|
-
readonly lines: Schema.
|
|
109
|
+
readonly statements: Schema.Finite;
|
|
110
|
+
readonly branches: Schema.Finite;
|
|
111
|
+
readonly functions: Schema.Finite;
|
|
112
|
+
readonly lines: Schema.Finite;
|
|
101
113
|
}>;
|
|
102
114
|
readonly uncoveredLines: Schema.optional<Schema.String>;
|
|
103
115
|
}>>;
|
|
@@ -112,16 +124,16 @@ declare const RenderState: Schema.Struct<{
|
|
|
112
124
|
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
113
125
|
readonly pattern: Schema.String;
|
|
114
126
|
readonly summary: Schema.Struct<{
|
|
115
|
-
readonly statements: Schema.
|
|
116
|
-
readonly branches: Schema.
|
|
117
|
-
readonly functions: Schema.
|
|
118
|
-
readonly lines: Schema.
|
|
127
|
+
readonly statements: Schema.Finite;
|
|
128
|
+
readonly branches: Schema.Finite;
|
|
129
|
+
readonly functions: Schema.Finite;
|
|
130
|
+
readonly lines: Schema.Finite;
|
|
119
131
|
}>;
|
|
120
132
|
readonly thresholds: Schema.Struct<{
|
|
121
|
-
readonly lines: Schema.optional<Schema.
|
|
122
|
-
readonly functions: Schema.optional<Schema.
|
|
123
|
-
readonly branches: Schema.optional<Schema.
|
|
124
|
-
readonly statements: Schema.optional<Schema.
|
|
133
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
134
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
135
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
136
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
125
137
|
}>;
|
|
126
138
|
}>>>;
|
|
127
139
|
}>>;
|
|
@@ -178,15 +190,35 @@ declare const RenderState: Schema.Struct<{
|
|
|
178
190
|
* wrote nothing stray.
|
|
179
191
|
*/
|
|
180
192
|
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
181
|
-
readonly total: Schema.
|
|
182
|
-
readonly stdout: Schema.
|
|
183
|
-
readonly stderr: Schema.
|
|
184
|
-
readonly bytes: Schema.
|
|
193
|
+
readonly total: Schema.Finite;
|
|
194
|
+
readonly stdout: Schema.Finite;
|
|
195
|
+
readonly stderr: Schema.Finite;
|
|
196
|
+
readonly bytes: Schema.Finite;
|
|
185
197
|
readonly samples: Schema.$Array<Schema.Struct<{
|
|
186
198
|
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
187
199
|
readonly text: Schema.String;
|
|
188
200
|
}>>;
|
|
189
201
|
}>>;
|
|
202
|
+
/**
|
|
203
|
+
* `console.*` output Vitest captured from tests that did not fail, folded
|
|
204
|
+
* from `RunFinished.consoleLeaks`. Absent when the run logged nothing
|
|
205
|
+
* (issue #544).
|
|
206
|
+
*/
|
|
207
|
+
readonly consoleLeaks: Schema.optional<Schema.Struct<{
|
|
208
|
+
readonly total: Schema.Finite;
|
|
209
|
+
readonly byFile: Schema.$Array<Schema.Struct<{
|
|
210
|
+
readonly file: Schema.String;
|
|
211
|
+
readonly stdout: Schema.Finite;
|
|
212
|
+
readonly stderr: Schema.Finite;
|
|
213
|
+
readonly tests: Schema.optional<Schema.$Array<Schema.String>>;
|
|
214
|
+
readonly sample: Schema.optional<Schema.String>;
|
|
215
|
+
}>>;
|
|
216
|
+
readonly truncated: Schema.optional<Schema.Boolean>;
|
|
217
|
+
readonly fromFailingTests: Schema.optional<Schema.Struct<{
|
|
218
|
+
readonly total: Schema.Finite;
|
|
219
|
+
readonly files: Schema.Finite;
|
|
220
|
+
}>>;
|
|
221
|
+
}>>;
|
|
190
222
|
}>;
|
|
191
223
|
/** @public */
|
|
192
224
|
type RenderState = typeof RenderState.Type;
|
|
@@ -289,27 +321,41 @@ interface DispatchInputs {
|
|
|
289
321
|
readonly runCommand: string | null;
|
|
290
322
|
}
|
|
291
323
|
/**
|
|
292
|
-
*
|
|
293
|
-
* `ReporterKit`. These are concerns the cell needs to format its
|
|
294
|
-
* output but that do not belong on the state-derived
|
|
295
|
-
* {@link DispatchInputs} struct.
|
|
324
|
+
* Where a file link points: a path, with an optional line and column.
|
|
296
325
|
*
|
|
297
|
-
* The
|
|
326
|
+
* The file half of `@effected/cli`'s `LinkTarget`, spelled structurally so
|
|
327
|
+
* this platform-free contract names no kit type. A relative path resolves
|
|
328
|
+
* against the working directory the kit's `CliLinks` layer was built with.
|
|
329
|
+
* @public
|
|
330
|
+
*/
|
|
331
|
+
interface FileLinkTarget {
|
|
332
|
+
readonly file: string;
|
|
333
|
+
readonly line?: number;
|
|
334
|
+
readonly col?: number;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Render-time options threaded into each cell. These are concerns the
|
|
338
|
+
* cell needs to format its output but that do not belong on the
|
|
339
|
+
* state-derived {@link DispatchInputs} struct.
|
|
340
|
+
*
|
|
341
|
+
* The caller builds this once per render and passes it into
|
|
298
342
|
* `dispatch(inputs, opts)`. Cells destructure only what they consume.
|
|
299
343
|
* @public
|
|
300
344
|
*/
|
|
301
345
|
interface CellOptions {
|
|
302
346
|
/**
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
347
|
+
* Writes a file path as a link for the audience and terminal the output
|
|
348
|
+
* is for: the label wrapped in an OSC 8 hyperlink when the reader can
|
|
349
|
+
* follow it, the label unchanged otherwise. The kit decides both halves
|
|
350
|
+
* (`@effected/cli`'s `CliLinks.linker`, `Render.context("stdout").link`):
|
|
351
|
+
* `vscode://file/<path>:<line>:<col>` under VS Code, `file://` otherwise,
|
|
352
|
+
* never for an agent audience, and nothing on a terminal that cannot
|
|
353
|
+
* render hyperlinks (a pipe, tmux, GNU screen, `NO_COLOR`).
|
|
354
|
+
*
|
|
355
|
+
* Omitted, every path is its label alone, which is what an agent reads;
|
|
356
|
+
* a cell therefore never writes an escape sequence of its own.
|
|
311
357
|
*/
|
|
312
|
-
readonly
|
|
358
|
+
readonly link?: (target: FileLinkTarget, label: string) => string;
|
|
313
359
|
}
|
|
314
360
|
/**
|
|
315
361
|
* One unit of rendered reporter output, tagged by where it goes.
|
|
@@ -343,11 +389,11 @@ declare const AgentReport: Schema.Struct<{
|
|
|
343
389
|
readonly project: Schema.optional<Schema.String>;
|
|
344
390
|
readonly reason: Schema.Literals<readonly ["passed", "failed", "interrupted"]>;
|
|
345
391
|
readonly summary: Schema.Struct<{
|
|
346
|
-
readonly total: Schema.
|
|
347
|
-
readonly passed: Schema.
|
|
348
|
-
readonly failed: Schema.
|
|
349
|
-
readonly skipped: Schema.
|
|
350
|
-
readonly duration: Schema.
|
|
392
|
+
readonly total: Schema.Finite;
|
|
393
|
+
readonly passed: Schema.Finite;
|
|
394
|
+
readonly failed: Schema.Finite;
|
|
395
|
+
readonly skipped: Schema.Finite;
|
|
396
|
+
readonly duration: Schema.Finite;
|
|
351
397
|
/**
|
|
352
398
|
* Count of every collected module, passing modules included.
|
|
353
399
|
* `AgentReport.failed` only carries failing modules, so a green run
|
|
@@ -355,12 +401,12 @@ declare const AgentReport: Schema.Struct<{
|
|
|
355
401
|
* this a fully-passing replay reads as "0 modules all-passed"
|
|
356
402
|
* (issue #204). Optional to keep existing fixtures/reports valid.
|
|
357
403
|
*/
|
|
358
|
-
readonly modules: Schema.optional<Schema.
|
|
404
|
+
readonly modules: Schema.optional<Schema.Finite>;
|
|
359
405
|
}>;
|
|
360
406
|
readonly failed: Schema.$Array<Schema.Struct<{
|
|
361
407
|
readonly file: Schema.String;
|
|
362
408
|
readonly state: Schema.Literals<readonly ["passed", "failed", "skipped", "pending"]>;
|
|
363
|
-
readonly duration: Schema.optional<Schema.
|
|
409
|
+
readonly duration: Schema.optional<Schema.Finite>;
|
|
364
410
|
readonly errors: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
365
411
|
readonly message: Schema.String;
|
|
366
412
|
readonly stack: Schema.optional<Schema.String>;
|
|
@@ -372,7 +418,7 @@ declare const AgentReport: Schema.Struct<{
|
|
|
372
418
|
readonly name: Schema.String;
|
|
373
419
|
readonly fullName: Schema.String;
|
|
374
420
|
readonly state: Schema.Literals<readonly ["passed", "failed", "skipped", "pending"]>;
|
|
375
|
-
readonly duration: Schema.optional<Schema.
|
|
421
|
+
readonly duration: Schema.optional<Schema.Finite>;
|
|
376
422
|
readonly flaky: Schema.optional<Schema.Boolean>;
|
|
377
423
|
readonly slow: Schema.optional<Schema.Boolean>;
|
|
378
424
|
readonly errors: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
@@ -395,81 +441,81 @@ declare const AgentReport: Schema.Struct<{
|
|
|
395
441
|
readonly failedFiles: Schema.$Array<Schema.String>;
|
|
396
442
|
readonly coverage: Schema.optional<Schema.Struct<{
|
|
397
443
|
readonly totals: Schema.Struct<{
|
|
398
|
-
readonly statements: Schema.
|
|
399
|
-
readonly branches: Schema.
|
|
400
|
-
readonly functions: Schema.
|
|
401
|
-
readonly lines: Schema.
|
|
444
|
+
readonly statements: Schema.Finite;
|
|
445
|
+
readonly branches: Schema.Finite;
|
|
446
|
+
readonly functions: Schema.Finite;
|
|
447
|
+
readonly lines: Schema.Finite;
|
|
402
448
|
}>;
|
|
403
449
|
readonly thresholds: Schema.Struct<{
|
|
404
450
|
readonly global: Schema.Struct<{
|
|
405
|
-
readonly lines: Schema.optional<Schema.
|
|
406
|
-
readonly functions: Schema.optional<Schema.
|
|
407
|
-
readonly branches: Schema.optional<Schema.
|
|
408
|
-
readonly statements: Schema.optional<Schema.
|
|
451
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
452
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
453
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
454
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
409
455
|
}>;
|
|
410
456
|
readonly patterns: Schema.withDecodingDefaultKey<Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.Struct<{
|
|
411
|
-
readonly lines: Schema.optional<Schema.
|
|
412
|
-
readonly functions: Schema.optional<Schema.
|
|
413
|
-
readonly branches: Schema.optional<Schema.
|
|
414
|
-
readonly statements: Schema.optional<Schema.
|
|
457
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
458
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
459
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
460
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
415
461
|
readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
|
|
416
|
-
readonly lines: Schema.optional<Schema.
|
|
417
|
-
readonly functions: Schema.optional<Schema.
|
|
418
|
-
readonly branches: Schema.optional<Schema.
|
|
419
|
-
readonly statements: Schema.optional<Schema.
|
|
462
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
463
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
464
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
465
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
420
466
|
}>]>>;
|
|
421
467
|
}>]>>, never>;
|
|
422
468
|
}>;
|
|
423
469
|
readonly targets: Schema.optional<Schema.Struct<{
|
|
424
470
|
readonly global: Schema.Struct<{
|
|
425
|
-
readonly lines: Schema.optional<Schema.
|
|
426
|
-
readonly functions: Schema.optional<Schema.
|
|
427
|
-
readonly branches: Schema.optional<Schema.
|
|
428
|
-
readonly statements: Schema.optional<Schema.
|
|
471
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
472
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
473
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
474
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
429
475
|
}>;
|
|
430
476
|
readonly patterns: Schema.withDecodingDefaultKey<Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.Struct<{
|
|
431
|
-
readonly lines: Schema.optional<Schema.
|
|
432
|
-
readonly functions: Schema.optional<Schema.
|
|
433
|
-
readonly branches: Schema.optional<Schema.
|
|
434
|
-
readonly statements: Schema.optional<Schema.
|
|
477
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
478
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
479
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
480
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
435
481
|
readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
|
|
436
|
-
readonly lines: Schema.optional<Schema.
|
|
437
|
-
readonly functions: Schema.optional<Schema.
|
|
438
|
-
readonly branches: Schema.optional<Schema.
|
|
439
|
-
readonly statements: Schema.optional<Schema.
|
|
482
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
483
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
484
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
485
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
440
486
|
}>]>>;
|
|
441
487
|
}>]>>, never>;
|
|
442
488
|
}>>;
|
|
443
489
|
readonly baselines: Schema.optional<Schema.Struct<{
|
|
444
490
|
readonly global: Schema.Struct<{
|
|
445
|
-
readonly lines: Schema.optional<Schema.
|
|
446
|
-
readonly functions: Schema.optional<Schema.
|
|
447
|
-
readonly branches: Schema.optional<Schema.
|
|
448
|
-
readonly statements: Schema.optional<Schema.
|
|
491
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
492
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
493
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
494
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
449
495
|
}>;
|
|
450
496
|
readonly patterns: Schema.withDecodingDefaultKey<Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.Struct<{
|
|
451
|
-
readonly lines: Schema.optional<Schema.
|
|
452
|
-
readonly functions: Schema.optional<Schema.
|
|
453
|
-
readonly branches: Schema.optional<Schema.
|
|
454
|
-
readonly statements: Schema.optional<Schema.
|
|
497
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
498
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
499
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
500
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
455
501
|
readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
|
|
456
|
-
readonly lines: Schema.optional<Schema.
|
|
457
|
-
readonly functions: Schema.optional<Schema.
|
|
458
|
-
readonly branches: Schema.optional<Schema.
|
|
459
|
-
readonly statements: Schema.optional<Schema.
|
|
502
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
503
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
504
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
505
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
460
506
|
}>]>>;
|
|
461
507
|
}>]>>, never>;
|
|
462
508
|
}>>;
|
|
463
509
|
readonly scoped: Schema.withDecodingDefaultKey<Schema.Boolean, never>;
|
|
464
510
|
readonly scopedFiles: Schema.optional<Schema.$Array<Schema.String>>;
|
|
465
|
-
readonly totalFiles: Schema.optional<Schema.
|
|
511
|
+
readonly totalFiles: Schema.optional<Schema.Finite>;
|
|
466
512
|
readonly lowCoverage: Schema.$Array<Schema.Struct<{
|
|
467
513
|
readonly file: Schema.String;
|
|
468
514
|
readonly summary: Schema.Struct<{
|
|
469
|
-
readonly statements: Schema.
|
|
470
|
-
readonly branches: Schema.
|
|
471
|
-
readonly functions: Schema.
|
|
472
|
-
readonly lines: Schema.
|
|
515
|
+
readonly statements: Schema.Finite;
|
|
516
|
+
readonly branches: Schema.Finite;
|
|
517
|
+
readonly functions: Schema.Finite;
|
|
518
|
+
readonly lines: Schema.Finite;
|
|
473
519
|
}>;
|
|
474
520
|
readonly uncoveredLines: Schema.String;
|
|
475
521
|
}>>;
|
|
@@ -477,10 +523,10 @@ declare const AgentReport: Schema.Struct<{
|
|
|
477
523
|
readonly belowTarget: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
478
524
|
readonly file: Schema.String;
|
|
479
525
|
readonly summary: Schema.Struct<{
|
|
480
|
-
readonly statements: Schema.
|
|
481
|
-
readonly branches: Schema.
|
|
482
|
-
readonly functions: Schema.
|
|
483
|
-
readonly lines: Schema.
|
|
526
|
+
readonly statements: Schema.Finite;
|
|
527
|
+
readonly branches: Schema.Finite;
|
|
528
|
+
readonly functions: Schema.Finite;
|
|
529
|
+
readonly lines: Schema.Finite;
|
|
484
530
|
}>;
|
|
485
531
|
readonly uncoveredLines: Schema.String;
|
|
486
532
|
}>>>;
|
|
@@ -488,44 +534,44 @@ declare const AgentReport: Schema.Struct<{
|
|
|
488
534
|
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
489
535
|
readonly pattern: Schema.String;
|
|
490
536
|
readonly summary: Schema.Struct<{
|
|
491
|
-
readonly statements: Schema.
|
|
492
|
-
readonly branches: Schema.
|
|
493
|
-
readonly functions: Schema.
|
|
494
|
-
readonly lines: Schema.
|
|
537
|
+
readonly statements: Schema.Finite;
|
|
538
|
+
readonly branches: Schema.Finite;
|
|
539
|
+
readonly functions: Schema.Finite;
|
|
540
|
+
readonly lines: Schema.Finite;
|
|
495
541
|
}>;
|
|
496
542
|
readonly thresholds: Schema.Struct<{
|
|
497
|
-
readonly lines: Schema.optional<Schema.
|
|
498
|
-
readonly functions: Schema.optional<Schema.
|
|
499
|
-
readonly branches: Schema.optional<Schema.
|
|
500
|
-
readonly statements: Schema.optional<Schema.
|
|
543
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
544
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
545
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
546
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
501
547
|
}>;
|
|
502
548
|
}>>>;
|
|
503
549
|
}>>;
|
|
504
550
|
readonly tagCounts: Schema.optional<Schema.$Record<Schema.String, Schema.Struct<{
|
|
505
|
-
readonly passed: Schema.optional<Schema.
|
|
506
|
-
readonly failed: Schema.optional<Schema.
|
|
507
|
-
readonly skipped: Schema.optional<Schema.
|
|
551
|
+
readonly passed: Schema.optional<Schema.Finite>;
|
|
552
|
+
readonly failed: Schema.optional<Schema.Finite>;
|
|
553
|
+
readonly skipped: Schema.optional<Schema.Finite>;
|
|
508
554
|
}>>>;
|
|
509
555
|
readonly consoleLeaks: Schema.optional<Schema.Struct<{
|
|
510
|
-
readonly total: Schema.
|
|
556
|
+
readonly total: Schema.Finite;
|
|
511
557
|
readonly byFile: Schema.$Array<Schema.Struct<{
|
|
512
558
|
readonly file: Schema.String;
|
|
513
|
-
readonly stdout: Schema.
|
|
514
|
-
readonly stderr: Schema.
|
|
559
|
+
readonly stdout: Schema.Finite;
|
|
560
|
+
readonly stderr: Schema.Finite;
|
|
515
561
|
readonly tests: Schema.optional<Schema.$Array<Schema.String>>;
|
|
516
562
|
readonly sample: Schema.optional<Schema.String>;
|
|
517
563
|
}>>;
|
|
518
564
|
readonly truncated: Schema.optional<Schema.Boolean>;
|
|
519
565
|
readonly fromFailingTests: Schema.optional<Schema.Struct<{
|
|
520
|
-
readonly total: Schema.
|
|
521
|
-
readonly files: Schema.
|
|
566
|
+
readonly total: Schema.Finite;
|
|
567
|
+
readonly files: Schema.Finite;
|
|
522
568
|
}>>;
|
|
523
569
|
}>>;
|
|
524
570
|
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
525
|
-
readonly total: Schema.
|
|
526
|
-
readonly stdout: Schema.
|
|
527
|
-
readonly stderr: Schema.
|
|
528
|
-
readonly bytes: Schema.
|
|
571
|
+
readonly total: Schema.Finite;
|
|
572
|
+
readonly stdout: Schema.Finite;
|
|
573
|
+
readonly stderr: Schema.Finite;
|
|
574
|
+
readonly bytes: Schema.Finite;
|
|
529
575
|
readonly samples: Schema.$Array<Schema.Struct<{
|
|
530
576
|
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
531
577
|
readonly text: Schema.String;
|
|
@@ -701,24 +747,24 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
|
|
|
701
747
|
readonly runCount: Schema.Number;
|
|
702
748
|
}>, Schema.TaggedStruct<"CoverageReady", {
|
|
703
749
|
readonly metrics: Schema.Struct<{
|
|
704
|
-
readonly statements: Schema.
|
|
705
|
-
readonly branches: Schema.
|
|
706
|
-
readonly functions: Schema.
|
|
707
|
-
readonly lines: Schema.
|
|
750
|
+
readonly statements: Schema.Finite;
|
|
751
|
+
readonly branches: Schema.Finite;
|
|
752
|
+
readonly functions: Schema.Finite;
|
|
753
|
+
readonly lines: Schema.Finite;
|
|
708
754
|
}>;
|
|
709
755
|
readonly thresholds: Schema.Struct<{
|
|
710
|
-
readonly lines: Schema.optional<Schema.
|
|
711
|
-
readonly functions: Schema.optional<Schema.
|
|
712
|
-
readonly branches: Schema.optional<Schema.
|
|
713
|
-
readonly statements: Schema.optional<Schema.
|
|
756
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
757
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
758
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
759
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
714
760
|
}>;
|
|
715
761
|
readonly gaps: Schema.$Array<Schema.Struct<{
|
|
716
762
|
readonly file: Schema.String;
|
|
717
763
|
readonly missing: Schema.Struct<{
|
|
718
|
-
readonly statements: Schema.
|
|
719
|
-
readonly branches: Schema.
|
|
720
|
-
readonly functions: Schema.
|
|
721
|
-
readonly lines: Schema.
|
|
764
|
+
readonly statements: Schema.Finite;
|
|
765
|
+
readonly branches: Schema.Finite;
|
|
766
|
+
readonly functions: Schema.Finite;
|
|
767
|
+
readonly lines: Schema.Finite;
|
|
722
768
|
}>;
|
|
723
769
|
readonly uncoveredLines: Schema.optional<Schema.String>;
|
|
724
770
|
}>>;
|
|
@@ -728,16 +774,16 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
|
|
|
728
774
|
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
729
775
|
readonly pattern: Schema.String;
|
|
730
776
|
readonly summary: Schema.Struct<{
|
|
731
|
-
readonly statements: Schema.
|
|
732
|
-
readonly branches: Schema.
|
|
733
|
-
readonly functions: Schema.
|
|
734
|
-
readonly lines: Schema.
|
|
777
|
+
readonly statements: Schema.Finite;
|
|
778
|
+
readonly branches: Schema.Finite;
|
|
779
|
+
readonly functions: Schema.Finite;
|
|
780
|
+
readonly lines: Schema.Finite;
|
|
735
781
|
}>;
|
|
736
782
|
readonly thresholds: Schema.Struct<{
|
|
737
|
-
readonly lines: Schema.optional<Schema.
|
|
738
|
-
readonly functions: Schema.optional<Schema.
|
|
739
|
-
readonly branches: Schema.optional<Schema.
|
|
740
|
-
readonly statements: Schema.optional<Schema.
|
|
783
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
784
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
785
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
786
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
741
787
|
}>;
|
|
742
788
|
}>>>;
|
|
743
789
|
}>, Schema.TaggedStruct<"ThresholdViolation", {
|
|
@@ -788,15 +834,35 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
|
|
|
788
834
|
* wrote nothing stray, and in older replay data.
|
|
789
835
|
*/
|
|
790
836
|
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
791
|
-
readonly total: Schema.
|
|
792
|
-
readonly stdout: Schema.
|
|
793
|
-
readonly stderr: Schema.
|
|
794
|
-
readonly bytes: Schema.
|
|
837
|
+
readonly total: Schema.Finite;
|
|
838
|
+
readonly stdout: Schema.Finite;
|
|
839
|
+
readonly stderr: Schema.Finite;
|
|
840
|
+
readonly bytes: Schema.Finite;
|
|
795
841
|
readonly samples: Schema.$Array<Schema.Struct<{
|
|
796
842
|
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
797
843
|
readonly text: Schema.String;
|
|
798
844
|
}>>;
|
|
799
845
|
}>>;
|
|
846
|
+
/**
|
|
847
|
+
* `console.*` output Vitest captured from tests that did not fail
|
|
848
|
+
* (`AgentReport.consoleLeaks`). Optional: absent when the run logged
|
|
849
|
+
* nothing, and in older replay data (issue #544).
|
|
850
|
+
*/
|
|
851
|
+
readonly consoleLeaks: Schema.optional<Schema.Struct<{
|
|
852
|
+
readonly total: Schema.Finite;
|
|
853
|
+
readonly byFile: Schema.$Array<Schema.Struct<{
|
|
854
|
+
readonly file: Schema.String;
|
|
855
|
+
readonly stdout: Schema.Finite;
|
|
856
|
+
readonly stderr: Schema.Finite;
|
|
857
|
+
readonly tests: Schema.optional<Schema.$Array<Schema.String>>;
|
|
858
|
+
readonly sample: Schema.optional<Schema.String>;
|
|
859
|
+
}>>;
|
|
860
|
+
readonly truncated: Schema.optional<Schema.Boolean>;
|
|
861
|
+
readonly fromFailingTests: Schema.optional<Schema.Struct<{
|
|
862
|
+
readonly total: Schema.Finite;
|
|
863
|
+
readonly files: Schema.Finite;
|
|
864
|
+
}>>;
|
|
865
|
+
}>>;
|
|
800
866
|
}>]>;
|
|
801
867
|
/** @public */
|
|
802
868
|
type RunEvent = typeof RunEvent.Type;
|
|
@@ -806,27 +872,27 @@ type RunEvent = typeof RunEvent.Type;
|
|
|
806
872
|
*/
|
|
807
873
|
declare const ResolvedThresholds: Schema.Struct<{
|
|
808
874
|
readonly global: Schema.Struct<{
|
|
809
|
-
readonly lines: Schema.optional<Schema.
|
|
810
|
-
readonly functions: Schema.optional<Schema.
|
|
811
|
-
readonly branches: Schema.optional<Schema.
|
|
812
|
-
readonly statements: Schema.optional<Schema.
|
|
875
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
876
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
877
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
878
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
813
879
|
}>;
|
|
814
880
|
readonly perFile: Schema.withDecodingDefaultKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
|
|
815
|
-
readonly lines: Schema.optional<Schema.
|
|
816
|
-
readonly functions: Schema.optional<Schema.
|
|
817
|
-
readonly branches: Schema.optional<Schema.
|
|
818
|
-
readonly statements: Schema.optional<Schema.
|
|
881
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
882
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
883
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
884
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
819
885
|
}>]>, never>;
|
|
820
886
|
readonly patterns: Schema.withDecodingDefaultKey<Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.Struct<{
|
|
821
|
-
readonly lines: Schema.optional<Schema.
|
|
822
|
-
readonly functions: Schema.optional<Schema.
|
|
823
|
-
readonly branches: Schema.optional<Schema.
|
|
824
|
-
readonly statements: Schema.optional<Schema.
|
|
887
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
888
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
889
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
890
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
825
891
|
readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
|
|
826
|
-
readonly lines: Schema.optional<Schema.
|
|
827
|
-
readonly functions: Schema.optional<Schema.
|
|
828
|
-
readonly branches: Schema.optional<Schema.
|
|
829
|
-
readonly statements: Schema.optional<Schema.
|
|
892
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
893
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
894
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
895
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
830
896
|
}>]>>;
|
|
831
897
|
}>]>>, never>;
|
|
832
898
|
}>;
|
|
@@ -857,8 +923,8 @@ type Transport = typeof Transport.Type;
|
|
|
857
923
|
*
|
|
858
924
|
* `detail` is pre-resolved by the plugin (via `DetailResolver`) but
|
|
859
925
|
* reporters that want to override can ignore it. `noColor` is the resolved
|
|
860
|
-
* value of the `NO_COLOR` env var; reporters use it to gate ANSI escapes
|
|
861
|
-
*
|
|
926
|
+
* value of the `NO_COLOR` env var; reporters use it to gate ANSI escapes
|
|
927
|
+
* (hyperlinks are the kit's decision: `@effected/cli`'s `CliLinks`).
|
|
862
928
|
* @public
|
|
863
929
|
*/
|
|
864
930
|
interface ResolvedReporterConfig {
|
|
@@ -943,8 +1009,9 @@ interface ResolvedReporterConfig {
|
|
|
943
1009
|
* Plugin-provided primitives passed to the reporter factory at construction
|
|
944
1010
|
* time. The `std*` prefix marks these as "the plugin gives you these — do
|
|
945
1011
|
* not import equivalents yourself"; they are pre-resolved with full context
|
|
946
|
-
* (environment, executor, NO_COLOR
|
|
947
|
-
*
|
|
1012
|
+
* (environment, executor, NO_COLOR) so the reporter doesn't have to
|
|
1013
|
+
* re-derive that state. File hyperlinks are not among them: the reporter
|
|
1014
|
+
* reads `@effected/cli`'s `CliLinks` from its own environment.
|
|
948
1015
|
*
|
|
949
1016
|
* The shape is open to additions: future fields (e.g. `stdLogger`,
|
|
950
1017
|
* `stdRuntime`) won't break existing reporters because the parameter is a
|
|
@@ -954,12 +1021,6 @@ interface ResolvedReporterConfig {
|
|
|
954
1021
|
interface ReporterKit {
|
|
955
1022
|
readonly config: ResolvedReporterConfig;
|
|
956
1023
|
readonly stdEnv: Environment;
|
|
957
|
-
/**
|
|
958
|
-
* Pre-bound OSC-8 hyperlink helper. The plugin has already decided
|
|
959
|
-
* whether OSC-8 should be enabled (target=stdout, !noColor) so the
|
|
960
|
-
* reporter can call this directly without consulting environment.
|
|
961
|
-
*/
|
|
962
|
-
readonly stdOsc8: (url: string, label: string) => string;
|
|
963
1024
|
/**
|
|
964
1025
|
* Live run-event channel. The plugin publishes one `RunEvent`
|
|
965
1026
|
* per Vitest streaming callback (`onTestRunStart`, `onTestModuleStart`,
|
|
@@ -990,6 +1051,13 @@ interface ReporterKit {
|
|
|
990
1051
|
interface ReporterRenderInput {
|
|
991
1052
|
readonly reports: ReadonlyArray<AgentReport>;
|
|
992
1053
|
readonly classifications: ReadonlyMap<string, TestClassification>;
|
|
1054
|
+
/**
|
|
1055
|
+
* Run-level suggested actions for the agent (a `SuggestedAction` event
|
|
1056
|
+
* each), rendered as a compact `Actions:` list after the cell output. The
|
|
1057
|
+
* default reporter feeds them to the synthesizer for every report; absent
|
|
1058
|
+
* when the plugin has nothing to suggest (issue #544).
|
|
1059
|
+
*/
|
|
1060
|
+
readonly suggestedActions?: ReadonlyArray<SuggestedActionRecord>;
|
|
993
1061
|
readonly trendSummary?: {
|
|
994
1062
|
readonly direction: "improving" | "regressing" | "stable";
|
|
995
1063
|
readonly runCount: number;
|
|
@@ -1087,22 +1155,34 @@ export declare const buildDispatchInputs: (state: RenderState, input: ReporterRe
|
|
|
1087
1155
|
readonly runCommand?: string | null;
|
|
1088
1156
|
}) => DispatchInputs;
|
|
1089
1157
|
/**
|
|
1090
|
-
*
|
|
1091
|
-
* resolved `noColor` value and the pre-bound OSC-8 hyperlink helper.
|
|
1158
|
+
* Options for the one-shot report renderers.
|
|
1092
1159
|
*
|
|
1093
1160
|
* @public
|
|
1094
1161
|
*/
|
|
1095
|
-
|
|
1162
|
+
interface ReportRenderOptions {
|
|
1163
|
+
/** Run-level suggested actions to append as the `Actions:` list (issue #544). */
|
|
1164
|
+
readonly suggestedActions?: ReadonlyArray<SuggestedActionRecord>;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Options for the Ink one-shot renderer: the agent options plus a layout width.
|
|
1168
|
+
*
|
|
1169
|
+
* @public
|
|
1170
|
+
*/
|
|
1171
|
+
interface HumanReportRenderOptions extends ReportRenderOptions {
|
|
1172
|
+
/** The column width to lay out at; the terminal's (else 80) when omitted. */
|
|
1173
|
+
readonly width?: number;
|
|
1174
|
+
}
|
|
1096
1175
|
/**
|
|
1097
1176
|
* Convenience helper for one-shot consumers (e.g. a CLI command
|
|
1098
1177
|
* replaying a stored run). Synthesizes a minimal `ReporterRenderInput`
|
|
1099
1178
|
* from a single `AgentReport`, classifies the shape and outcome, and
|
|
1100
1179
|
* returns the dispatched agent-string for the matching cell. Equivalent
|
|
1101
|
-
* to the pre-2.0 `renderRun(events, "agent")` shortcut.
|
|
1180
|
+
* to the pre-2.0 `renderRun(events, "agent")` shortcut. Pass
|
|
1181
|
+
* `options.suggestedActions` to append the run's `Actions:` list.
|
|
1102
1182
|
*
|
|
1103
1183
|
* @public
|
|
1104
1184
|
*/
|
|
1105
|
-
export declare const renderAgentStringForReport: (report: AgentReport) => string;
|
|
1185
|
+
export declare const renderAgentStringForReport: (report: AgentReport, options?: ReportRenderOptions) => string;
|
|
1106
1186
|
/**
|
|
1107
1187
|
* Same as {@link renderAgentStringForReport} but returns the Ink-half
|
|
1108
1188
|
* rendered to a string via Ink's `renderToString`, inside the kit's
|
|
@@ -1119,9 +1199,7 @@ export declare const renderAgentStringForReport: (report: AgentReport) => string
|
|
|
1119
1199
|
*
|
|
1120
1200
|
* @public
|
|
1121
1201
|
*/
|
|
1122
|
-
export declare const renderHumanStringForReport: (report: AgentReport, options?:
|
|
1123
|
-
readonly width?: number;
|
|
1124
|
-
}) => Promise<string>;
|
|
1202
|
+
export declare const renderHumanStringForReport: (report: AgentReport, options?: HumanReportRenderOptions) => Promise<string>;
|
|
1125
1203
|
/**
|
|
1126
1204
|
* The default reporter factory.
|
|
1127
1205
|
*
|
|
@@ -1155,5 +1233,5 @@ export declare const DefaultVitestAgentReporter: VitestAgentReporterFactory;
|
|
|
1155
1233
|
* @public
|
|
1156
1234
|
*/
|
|
1157
1235
|
export declare const CURRENT_REPORTER_VERSION: string;
|
|
1158
|
-
export type { AgentReport, CellOptions, ConsoleMode, DetailLevel, DispatchInputs, Environment, Executor, FileCoverageReport, ProjectSummary, RenderState, RenderedOutput, ReporterKit, ReporterRenderInput, ResolvedReporterConfig, ResolvedThresholds, RunEvent, RunOutcome, RunShape, TestClassification, Transport, TrendSummary, VitestAgentReporter, VitestAgentReporterFactory };
|
|
1236
|
+
export type { AgentReport, CellOptions, ConsoleMode, DetailLevel, DispatchInputs, Environment, Executor, FileCoverageReport, FileLinkTarget, HumanReportRenderOptions, ProjectSummary, RenderState, RenderedOutput, ReportRenderOptions, ReporterKit, ReporterRenderInput, ResolvedReporterConfig, ResolvedThresholds, RunEvent, RunOutcome, RunShape, SuggestedActionRecord, TestClassification, Transport, TrendSummary, VitestAgentReporter, VitestAgentReporterFactory };
|
|
1159
1237
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanStringForReport
|
|
1
|
+
import { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanStringForReport } from "./defaultReporter.js";
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
/**
|
|
@@ -8,6 +8,6 @@ import { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForRe
|
|
|
8
8
|
*
|
|
9
9
|
* @public
|
|
10
10
|
*/
|
|
11
|
-
const CURRENT_REPORTER_VERSION = "
|
|
11
|
+
const CURRENT_REPORTER_VERSION = "6.0.1";
|
|
12
12
|
|
|
13
|
-
export { CURRENT_REPORTER_VERSION, DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanStringForReport
|
|
13
|
+
export { CURRENT_REPORTER_VERSION, DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanStringForReport };
|
package/liveView.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { plainFileLink, resolveFileLink } from "./fileLinks.js";
|
|
1
2
|
import { CliEnv, Doc } from "@effected/cli";
|
|
2
3
|
import { initialRenderState } from "@vitest-agent/sdk";
|
|
3
4
|
import { SPINNER_FRAME_MS, reduceRenderState, renderAgent } from "@vitest-agent/ui";
|
|
5
|
+
import { Cause, Deferred, Effect, Exit, Layer, PubSub, Scope } from "effect";
|
|
4
6
|
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
5
7
|
import { CliUi } from "@effected/cli/ui";
|
|
6
|
-
import { Cause, Deferred, Effect, Exit, Layer, PubSub, Scope } from "effect";
|
|
7
8
|
|
|
8
9
|
// src/liveView.ts
|
|
9
10
|
/**
|
|
@@ -41,29 +42,40 @@ import { Cause, Deferred, Effect, Exit, Layer, PubSub, Scope } from "effect";
|
|
|
41
42
|
* watch rerun.
|
|
42
43
|
*/
|
|
43
44
|
/**
|
|
44
|
-
* The live view's options, everything but `events
|
|
45
|
-
* `
|
|
46
|
-
*
|
|
45
|
+
* The live view's options, everything but `events`, for a view whose paths
|
|
46
|
+
* are drawn through `link`: what the reporter hands `CliUi.live` once it has
|
|
47
|
+
* resolved the writer from its environment (`resolveFileLink`).
|
|
47
48
|
*
|
|
48
49
|
* @internal
|
|
49
50
|
*/
|
|
50
|
-
const
|
|
51
|
+
const liveViewOptionsWith = (link) => ({
|
|
51
52
|
initial: initialRenderState,
|
|
52
53
|
reduce: reduceRenderState,
|
|
53
|
-
render: CliUi.lazyView(() => import("./streamView.js")),
|
|
54
|
+
render: CliUi.lazyView(() => import("./streamView.js").then((view) => view.makeStreamView(link))),
|
|
54
55
|
isStart: (event) => event._tag === "RunStarted",
|
|
55
56
|
isTerminal: (event) => event._tag === "RunFinished" || event._tag === "RunTimedOut",
|
|
56
57
|
begins: (event, before, after) => event._tag === "RunStarted" || before.phase === "idle" && after.phase !== "idle",
|
|
57
58
|
mode: "owned",
|
|
58
59
|
tickMillis: SPINNER_FRAME_MS,
|
|
59
60
|
final: (state) => renderAgent(state).trimEnd().split("\n").map((line) => Doc.line(line, { wrap: false }))
|
|
60
|
-
};
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* The live view's options with no link writer (every path its label alone):
|
|
64
|
+
* what the tests hand `CliUiTest.live`, so they run the same fold, drawing
|
|
65
|
+
* and run predicates `startLiveView` does.
|
|
66
|
+
*
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
const liveViewOptions = liveViewOptionsWith(plainFileLink);
|
|
61
70
|
/**
|
|
62
71
|
* The environment a Vitest-hosted live view reads: the kit's `CliEnv` over
|
|
63
72
|
* Node's stdio and terminal. It builds `CliTheme` (colour from the terminal,
|
|
64
|
-
* glyphs `auto`, so `TERM=dumb` draws ASCII)
|
|
65
|
-
*
|
|
66
|
-
*
|
|
73
|
+
* glyphs `auto`, so `TERM=dumb` draws ASCII), `TerminalEnv` (the width the
|
|
74
|
+
* report-time human render lays out at, and whether stdout renders OSC 8),
|
|
75
|
+
* `Audience` and `CliLinks` (where a file link opens; the platform is
|
|
76
|
+
* provided outside `CliEnv.layer`, so a `.vscode/` directory is looked for
|
|
77
|
+
* and a relative path resolved against the working directory), and sets
|
|
78
|
+
* `CliInteractive` (a human audience with a TTY on stdin and stdout).
|
|
67
79
|
*
|
|
68
80
|
* @internal
|
|
69
81
|
*/
|
|
@@ -95,10 +107,10 @@ const startLiveView = (channel, env = LiveViewEnv) => {
|
|
|
95
107
|
const scope = Effect.runSync(Scope.make());
|
|
96
108
|
const subscription = Effect.runSync(PubSub.subscribe(channel).pipe(Scope.provide(scope)));
|
|
97
109
|
const mounted = Effect.runSync(Deferred.make());
|
|
98
|
-
const program = CliUi.live({
|
|
99
|
-
...
|
|
110
|
+
const program = resolveFileLink.pipe(Effect.flatMap((link) => CliUi.live({
|
|
111
|
+
...liveViewOptionsWith(link),
|
|
100
112
|
events: subscription
|
|
101
|
-
})
|
|
113
|
+
})), Effect.onExit((exit) => Deferred.done(mounted, exit)), Effect.flatMap((handle) => handle.done), Scope.provide(scope), Effect.provide(env));
|
|
102
114
|
Effect.runFork(program);
|
|
103
115
|
let closing;
|
|
104
116
|
let print;
|
|
@@ -117,4 +129,4 @@ const startLiveView = (channel, env = LiveViewEnv) => {
|
|
|
117
129
|
};
|
|
118
130
|
};
|
|
119
131
|
|
|
120
|
-
export { LiveViewEnv,
|
|
132
|
+
export { LiveViewEnv, liveViewOptionsWith, printAbove, startLiveView };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/reporter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Named VitestAgentReporterFactory implementations for the vitest-agent ecosystem.",
|
|
6
6
|
"keywords": [
|
|
@@ -40,17 +40,17 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@effect/platform-node": "^4.0.6",
|
|
43
|
-
"@vitest-agent/sdk": "
|
|
44
|
-
"@vitest-agent/ui": "5.
|
|
43
|
+
"@vitest-agent/sdk": "8.0.0",
|
|
44
|
+
"@vitest-agent/ui": "5.1.1",
|
|
45
45
|
"effect": "^4.0.6",
|
|
46
46
|
"ink": "^8.0.0",
|
|
47
47
|
"react": "^19.3.1"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@effected/cli": "^0.22.
|
|
50
|
+
"@effected/cli": "^0.22.1",
|
|
51
51
|
"@effected/env": "^0.5.0",
|
|
52
52
|
"@effected/glob": "^0.14.0",
|
|
53
|
-
"@effected/walker": "^0.
|
|
53
|
+
"@effected/walker": "^0.20.0",
|
|
54
54
|
"@vitest/coverage-istanbul": "^5.0.0",
|
|
55
55
|
"@vitest/coverage-v8": "^5.0.0",
|
|
56
56
|
"vitest": "^5.0.0"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { countSuiteFailures, isTimeoutError } from "@vitest-agent/sdk";
|
|
2
|
+
|
|
3
|
+
// src/projectSummary.ts
|
|
4
|
+
/** Timed-out tests in `report.failed[].tests[]`, identified per test by `isTimeoutError`. */
|
|
5
|
+
const countTimeouts = (report) => {
|
|
6
|
+
let timeoutCount = 0;
|
|
7
|
+
for (const mod of report.failed) for (const test of mod.tests) {
|
|
8
|
+
const firstError = test.errors?.[0];
|
|
9
|
+
if (test.state === "failed" && firstError !== void 0 && isTimeoutError({ message: firstError.message })) timeoutCount++;
|
|
10
|
+
}
|
|
11
|
+
return timeoutCount;
|
|
12
|
+
};
|
|
13
|
+
const collapseTagCounts = (entries) => {
|
|
14
|
+
const out = {};
|
|
15
|
+
if (entries === void 0) return out;
|
|
16
|
+
for (const [tag, entry] of Object.entries(entries)) {
|
|
17
|
+
const total = (entry.passed ?? 0) + (entry.failed ?? 0) + (entry.skipped ?? 0);
|
|
18
|
+
if (total > 0) out[tag] = total;
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
};
|
|
22
|
+
const summarizeProject = (report) => {
|
|
23
|
+
const collapsedTagCounts = report.tagCounts !== void 0 ? collapseTagCounts(report.tagCounts) : void 0;
|
|
24
|
+
const tagCountsHasEntries = collapsedTagCounts !== void 0 && Object.keys(collapsedTagCounts).length > 0;
|
|
25
|
+
const belowTargetCount = report.coverage?.belowTargetFiles?.length;
|
|
26
|
+
const violationsCount = report.coverage !== void 0 && report.coverage.lowCoverage.length > 0 ? report.coverage.lowCoverage.length : void 0;
|
|
27
|
+
const timeoutCount = countTimeouts(report);
|
|
28
|
+
return {
|
|
29
|
+
name: report.project ?? "default",
|
|
30
|
+
passCount: report.summary.passed,
|
|
31
|
+
failCount: report.summary.failed + countSuiteFailures(report) - timeoutCount,
|
|
32
|
+
skipCount: report.summary.skipped,
|
|
33
|
+
durationMs: report.summary.duration,
|
|
34
|
+
...timeoutCount > 0 ? { timeoutCount } : {},
|
|
35
|
+
...tagCountsHasEntries && collapsedTagCounts !== void 0 ? { tagCounts: collapsedTagCounts } : {},
|
|
36
|
+
...belowTargetCount !== void 0 ? { belowTarget: belowTargetCount } : {},
|
|
37
|
+
...violationsCount !== void 0 ? { violations: violationsCount } : {}
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { countTimeouts, summarizeProject };
|
package/streamView.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { SPINNER_FRAME_MS } from "@vitest-agent/ui";
|
|
2
|
-
import { StreamApp } from "@vitest-agent/ui/ink";
|
|
2
|
+
import { FileLinkProvider, StreamApp } from "@vitest-agent/ui/ink";
|
|
3
3
|
import { createElement } from "react";
|
|
4
4
|
|
|
5
5
|
// src/streamView.ts
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The view for one live view, closed over its link writer.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
const makeStreamView = (link) => (state, frame) => createElement(FileLinkProvider, { link }, createElement(StreamApp, {
|
|
7
12
|
state,
|
|
8
13
|
frameIndex: frame,
|
|
9
14
|
nowMs: frame * SPINNER_FRAME_MS
|
|
10
|
-
});
|
|
15
|
+
}));
|
|
11
16
|
|
|
12
|
-
export {
|
|
17
|
+
export { makeStreamView };
|