@vitest-agent/ui 5.0.9 → 5.1.0
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/cells/single-file-fail.js +2 -2
- package/dispatcher/cells/single-project-fail.js +2 -2
- package/dispatcher/cells/single-project-threshold.js +2 -2
- package/dispatcher/cells/single-test-fail.js +2 -2
- package/dispatcher/cells/workspace-fail.js +2 -2
- package/dispatcher/cells/workspace-threshold.js +2 -2
- package/dispatcher/dispatch.js +37 -4
- package/dispatcher/helpers.js +44 -16
- package/index.d.ts +240 -190
- package/index.js +1 -1
- package/ink/CoverageBlock.js +3 -1
- package/ink/FailureSection.js +3 -1
- package/ink/FailuresSection.js +5 -3
- package/ink/FileLink.js +24 -0
- package/ink/StreamApp.js +34 -16
- package/ink/dispatch-ink.js +11 -4
- package/ink.d.ts +129 -64
- package/ink.js +2 -1
- package/package.json +2 -2
- package/reducer.js +2 -1
- package/render-agent.js +10 -14
- package/synthesize.js +2 -1
package/index.js
CHANGED
|
@@ -17,6 +17,6 @@ import { ActionSeverity, CoverageFile, CoverageGap, CoverageMetric, CoverageRend
|
|
|
17
17
|
*
|
|
18
18
|
* @public
|
|
19
19
|
*/
|
|
20
|
-
const CURRENT_UI_VERSION = "5.0
|
|
20
|
+
const CURRENT_UI_VERSION = "5.1.0";
|
|
21
21
|
|
|
22
22
|
export { ActionSeverity, CURRENT_UI_VERSION, CoverageFile, CoverageGap, CoverageMetric, CoverageRenderState, FailureRecord, ModuleRecord, RenderState, RenderTotals, RunEvent, SPINNER_FRAMES, SPINNER_FRAME_MS, SUITE_FAILURE_LABEL, SUITE_LOAD_FAILURE_LABEL, SuggestedActionRecord, TestRecord, VitestAgentStatus, VitestAgentTokens, buildFooter, classifyOutcome, classifyRunShape, dispatch, dispatcherTable, dominantClassification, formatDisplayDuration, initialRenderState, inkStyle, reduceRenderState, reduceRenderStateAll, renderAgent, spinnerFrame, statusGlyph, statusInkStyle, synthesizeFromAgentReport, synthesizeRunEvents };
|
package/ink/CoverageBlock.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inkStyle } from "../theme.js";
|
|
2
|
+
import { useFileLink } from "./FileLink.js";
|
|
2
3
|
import { Fmt } from "@effected/cli";
|
|
3
4
|
import { Box, Text } from "ink";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -27,6 +28,7 @@ const CoverageBlock = ({ coverage, maxGaps = 3 }) => {
|
|
|
27
28
|
const sortedGaps = [...coverage.gaps].sort((a, b) => b.missing.lines - a.missing.lines);
|
|
28
29
|
const topGaps = maxGaps > 0 ? sortedGaps.slice(0, maxGaps) : [];
|
|
29
30
|
const elidedGaps = maxGaps > 0 ? coverage.gaps.length - topGaps.length : 0;
|
|
31
|
+
const link = useFileLink();
|
|
30
32
|
return /* @__PURE__ */ jsxs(Box, {
|
|
31
33
|
flexDirection: "column",
|
|
32
34
|
children: [
|
|
@@ -71,7 +73,7 @@ const CoverageBlock = ({ coverage, maxGaps = 3 }) => {
|
|
|
71
73
|
bold: true,
|
|
72
74
|
children: "Gaps"
|
|
73
75
|
}),
|
|
74
|
-
topGaps.map((g) => /* @__PURE__ */ jsxs(Text, { children: [` ${g.file}`, g.uncoveredLines !== void 0 ? `: ${g.uncoveredLines}` : ""] }, g.file)),
|
|
76
|
+
topGaps.map((g) => /* @__PURE__ */ jsxs(Text, { children: [` ${link({ file: g.file }, g.file)}`, g.uncoveredLines !== void 0 ? `: ${g.uncoveredLines}` : ""] }, g.file)),
|
|
75
77
|
elidedGaps > 0 ? /* @__PURE__ */ jsx(Text, {
|
|
76
78
|
dimColor: true,
|
|
77
79
|
children: ` (+${Fmt.plural(elidedGaps, "more gap", "more gaps")})`
|
package/ink/FailureSection.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inkStyle, statusGlyph, statusInkStyle } from "../theme.js";
|
|
2
|
+
import { useFileLink } from "./FileLink.js";
|
|
2
3
|
import { useGlyphs } from "@effected/cli/ui";
|
|
3
4
|
import { Box, Text } from "ink";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -12,6 +13,7 @@ const FailureRow = ({ failure, includeStack }) => {
|
|
|
12
13
|
const suite = failure.suitePath.length > 0 ? `${failure.suitePath.join(" > ")} > ` : "";
|
|
13
14
|
const classification = failure.classification !== null ? ` [${failure.classification}]` : "";
|
|
14
15
|
const glyphs = useGlyphs();
|
|
16
|
+
const link = useFileLink();
|
|
15
17
|
return /* @__PURE__ */ jsxs(Box, {
|
|
16
18
|
flexDirection: "column",
|
|
17
19
|
children: [
|
|
@@ -20,7 +22,7 @@ const FailureRow = ({ failure, includeStack }) => {
|
|
|
20
22
|
...statusInkStyle("failure"),
|
|
21
23
|
children: statusGlyph("failure", glyphs)
|
|
22
24
|
}),
|
|
23
|
-
` ${failure.modulePath} > ${suite}${failure.testName}`,
|
|
25
|
+
` ${link({ file: failure.modulePath }, failure.modulePath)} > ${suite}${failure.testName}`,
|
|
24
26
|
failure.classification !== null ? /* @__PURE__ */ jsx(Text, {
|
|
25
27
|
color: "yellow",
|
|
26
28
|
children: classification
|
package/ink/FailuresSection.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { VitestAgentTokens, inkStyle, statusGlyph, statusInkStyle } from "../theme.js";
|
|
2
|
+
import { useFileLink } from "./FileLink.js";
|
|
2
3
|
import { useGlyphs } from "@effected/cli/ui";
|
|
3
4
|
import { Box, Text } from "ink";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -13,9 +14,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
13
14
|
* `module › suite › test` path, the classification tag when known, and
|
|
14
15
|
* one indented line — the first line of the error message.
|
|
15
16
|
*/
|
|
16
|
-
const pathOf = (f) => {
|
|
17
|
+
const pathOf = (f, link) => {
|
|
17
18
|
return [
|
|
18
|
-
f.modulePath,
|
|
19
|
+
link({ file: f.modulePath }, f.modulePath),
|
|
19
20
|
...f.suitePath,
|
|
20
21
|
f.testName
|
|
21
22
|
].join(" › ");
|
|
@@ -33,6 +34,7 @@ const FailuresSection = ({ failures, limit }) => {
|
|
|
33
34
|
const shown = failures.slice(0, limit);
|
|
34
35
|
const overflow = failures.length - shown.length;
|
|
35
36
|
const glyphs = useGlyphs();
|
|
37
|
+
const link = useFileLink();
|
|
36
38
|
return /* @__PURE__ */ jsxs(Box, {
|
|
37
39
|
flexDirection: "column",
|
|
38
40
|
children: [
|
|
@@ -54,7 +56,7 @@ const FailuresSection = ({ failures, limit }) => {
|
|
|
54
56
|
children: statusGlyph(failureStatus(f), glyphs)
|
|
55
57
|
}),
|
|
56
58
|
" ",
|
|
57
|
-
pathOf(f),
|
|
59
|
+
pathOf(f, link),
|
|
58
60
|
f.classification !== null ? /* @__PURE__ */ jsxs(Text, {
|
|
59
61
|
...inkStyle(VitestAgentTokens.classification),
|
|
60
62
|
children: [
|
package/ink/FileLink.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
|
|
4
|
+
// src/ink/FileLink.tsx
|
|
5
|
+
const plain = (_target, label) => label;
|
|
6
|
+
const FileLinkContext = createContext(plain);
|
|
7
|
+
/**
|
|
8
|
+
* Provide the file-link writer to an Ink tree.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
const FileLinkProvider = ({ link, children }) => /* @__PURE__ */ jsx(FileLinkContext.Provider, {
|
|
13
|
+
value: link ?? plain,
|
|
14
|
+
children
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* The file-link writer of the tree: the nearest {@link FileLinkProvider}'s,
|
|
18
|
+
* or the label alone.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
const useFileLink = () => useContext(FileLinkContext);
|
|
23
|
+
|
|
24
|
+
export { FileLinkProvider, useFileLink };
|
package/ink/StreamApp.js
CHANGED
|
@@ -3,12 +3,13 @@ import { VitestAgentTokens, inkStyle, statusGlyph, statusInkStyle } from "../the
|
|
|
3
3
|
import { formatDisplayDuration } from "../format-duration.js";
|
|
4
4
|
import { spinnerFrame } from "../spinner.js";
|
|
5
5
|
import { CountColumns, DURATION_CELL_WIDTH } from "./CountColumns.js";
|
|
6
|
+
import { useFileLink } from "./FileLink.js";
|
|
6
7
|
import { StatusIcon } from "./StatusIcon.js";
|
|
7
8
|
import { TagColumns, tagUnion } from "./TagColumns.js";
|
|
8
9
|
import { ProjectRow } from "./ProjectRow.js";
|
|
9
10
|
import { TestRow } from "./TestRow.js";
|
|
10
11
|
import { TrendLine } from "./TrendLine.js";
|
|
11
|
-
import { formatStrayOutputNote } from "@vitest-agent/sdk";
|
|
12
|
+
import { formatConsoleLeaksNote, formatStrayOutputNote } from "@vitest-agent/sdk";
|
|
12
13
|
import { Fmt } from "@effected/cli";
|
|
13
14
|
import { useGlyphs } from "@effected/cli/ui";
|
|
14
15
|
import { Box, Text } from "ink";
|
|
@@ -109,6 +110,7 @@ const moduleIcon = (m) => {
|
|
|
109
110
|
* and finished rows (resolved glyph, `running=false`).
|
|
110
111
|
*/
|
|
111
112
|
const ModuleStreamRow = ({ module, nowMs, frame, nameWidth, timedOut, tagUnion: rowTagUnion }) => {
|
|
113
|
+
const link = useFileLink();
|
|
112
114
|
const running = moduleRunning(module);
|
|
113
115
|
const queued = module.status === "queued";
|
|
114
116
|
let glyph;
|
|
@@ -131,7 +133,8 @@ const ModuleStreamRow = ({ module, nowMs, frame, nameWidth, timedOut, tagUnion:
|
|
|
131
133
|
glyph,
|
|
132
134
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
133
135
|
" ",
|
|
134
|
-
module.modulePath.
|
|
136
|
+
link({ file: module.modulePath }, module.modulePath),
|
|
137
|
+
" ".repeat(Math.max(0, nameWidth - module.modulePath.length)),
|
|
135
138
|
" "
|
|
136
139
|
] }),
|
|
137
140
|
/* @__PURE__ */ jsx(CountColumns, {
|
|
@@ -204,8 +207,8 @@ const UnhandledErrorItem = ({ error }) => {
|
|
|
204
207
|
});
|
|
205
208
|
};
|
|
206
209
|
const testKey = (modulePath, t) => `test:${modulePath}::${t.suitePath.join("/")}::${t.testName}`;
|
|
207
|
-
const failurePath = (f) => [
|
|
208
|
-
f.modulePath,
|
|
210
|
+
const failurePath = (f, link) => [
|
|
211
|
+
link({ file: f.modulePath }, f.modulePath),
|
|
209
212
|
...f.suitePath,
|
|
210
213
|
f.testName
|
|
211
214
|
].join(" › ");
|
|
@@ -213,6 +216,7 @@ const failureStatus = (f) => f.timedOut === true ? "timeout" : "failure";
|
|
|
213
216
|
/** A failure entry rendered in the Live region. */
|
|
214
217
|
const FailureItem = ({ failure }) => {
|
|
215
218
|
const glyphs = useGlyphs();
|
|
219
|
+
const link = useFileLink();
|
|
216
220
|
return /* @__PURE__ */ jsxs(Box, {
|
|
217
221
|
flexDirection: "column",
|
|
218
222
|
children: [/* @__PURE__ */ jsxs(Text, { children: [
|
|
@@ -222,7 +226,7 @@ const FailureItem = ({ failure }) => {
|
|
|
222
226
|
children: statusGlyph(failureStatus(failure), glyphs)
|
|
223
227
|
}),
|
|
224
228
|
" ",
|
|
225
|
-
failurePath(failure),
|
|
229
|
+
failurePath(failure, link),
|
|
226
230
|
failure.classification !== null ? /* @__PURE__ */ jsxs(Text, {
|
|
227
231
|
...inkStyle(VitestAgentTokens.classification),
|
|
228
232
|
children: [
|
|
@@ -290,7 +294,7 @@ const samePath = (a, b) => {
|
|
|
290
294
|
* run picture — all rows in discovery order, with running rows capped
|
|
291
295
|
* at MAX_LIVE_RUNNING_ROWS and finished rows always shown.
|
|
292
296
|
*/
|
|
293
|
-
const liveRegion = (state, shape, nowMs, frame, glyphs) => {
|
|
297
|
+
const liveRegion = (state, shape, nowMs, frame, glyphs, link) => {
|
|
294
298
|
const groups = groupByProject(state);
|
|
295
299
|
const timedOut = state.phase === "timed-out";
|
|
296
300
|
const finished = state.phase === "finished" || state.phase === "timed-out";
|
|
@@ -471,7 +475,7 @@ const liveRegion = (state, shape, nowMs, frame, glyphs) => {
|
|
|
471
475
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
472
476
|
/* @__PURE__ */ jsxs(Text, { children: [/* @__PURE__ */ jsx(Text, {
|
|
473
477
|
bold: true,
|
|
474
|
-
children: sole.modulePath
|
|
478
|
+
children: link({ file: sole.modulePath }, sole.modulePath)
|
|
475
479
|
}), /* @__PURE__ */ jsxs(Text, {
|
|
476
480
|
dimColor: true,
|
|
477
481
|
children: [
|
|
@@ -505,21 +509,35 @@ const liveRegion = (state, shape, nowMs, frame, glyphs) => {
|
|
|
505
509
|
const StreamApp = ({ state, frameIndex, nowMs }) => {
|
|
506
510
|
const now = nowMs ?? Date.now();
|
|
507
511
|
const glyphs = useGlyphs();
|
|
512
|
+
const link = useFileLink();
|
|
508
513
|
const frame = spinnerFrame(frameIndex, glyphs);
|
|
509
514
|
const projects = groupByProject(state).map((g) => buildProjectSummary(g.name, sumCounts(g.modules)));
|
|
510
515
|
const shape = classifyRunShape(state, projects);
|
|
511
|
-
const liveContent = liveRegion(state, shape, now, frame, glyphs);
|
|
516
|
+
const liveContent = liveRegion(state, shape, now, frame, glyphs, link);
|
|
512
517
|
const stray = state.strayOutput !== void 0 ? formatStrayOutputNote(state.strayOutput) : null;
|
|
518
|
+
const leakLines = state.consoleLeaks !== void 0 ? formatConsoleLeaksNote(state.consoleLeaks) : [];
|
|
519
|
+
const leaks = leakLines.length > 0 ? leakLines : null;
|
|
513
520
|
return /* @__PURE__ */ jsxs(Box, {
|
|
514
521
|
flexDirection: "column",
|
|
515
|
-
children: [
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
children: line
|
|
521
|
-
|
|
522
|
-
|
|
522
|
+
children: [
|
|
523
|
+
liveContent,
|
|
524
|
+
stray !== null ? /* @__PURE__ */ jsx(Box, {
|
|
525
|
+
flexDirection: "column",
|
|
526
|
+
marginTop: 1,
|
|
527
|
+
children: stray.map((line, i) => /* @__PURE__ */ jsx(Text, {
|
|
528
|
+
...i === 0 ? statusInkStyle("warning") : {},
|
|
529
|
+
children: line
|
|
530
|
+
}, `stray:${i}`))
|
|
531
|
+
}) : null,
|
|
532
|
+
leaks !== null ? /* @__PURE__ */ jsx(Box, {
|
|
533
|
+
flexDirection: "column",
|
|
534
|
+
marginTop: 1,
|
|
535
|
+
children: leaks.map((line, i) => /* @__PURE__ */ jsx(Text, {
|
|
536
|
+
...i === 0 ? statusInkStyle("warning") : {},
|
|
537
|
+
children: line
|
|
538
|
+
}, `leaks:${i}`))
|
|
539
|
+
}) : null
|
|
540
|
+
]
|
|
523
541
|
});
|
|
524
542
|
};
|
|
525
543
|
|
package/ink/dispatch-ink.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dispatcherTable, scopedCoverageNoteFor, strayOutputNoteFor } from "../dispatcher/dispatch.js";
|
|
1
|
+
import { consoleLeaksNoteFor, dispatcherTable, scopedCoverageNoteFor, strayOutputNoteFor, suggestedActionsNoteFor } from "../dispatcher/dispatch.js";
|
|
2
2
|
import { renderAgentStringAsInk } from "./ink-helpers.js";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
4
|
import { createElement } from "react";
|
|
@@ -52,9 +52,16 @@ const dispatchInk = (inputs, opts) => {
|
|
|
52
52
|
if (cell === void 0) return null;
|
|
53
53
|
const element = cell(inputs, opts);
|
|
54
54
|
const note = scopedCoverageNoteFor(inputs);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const trailers = [
|
|
56
|
+
suggestedActionsNoteFor(inputs),
|
|
57
|
+
strayOutputNoteFor(inputs),
|
|
58
|
+
consoleLeaksNoteFor(inputs)
|
|
59
|
+
].filter((trailer) => trailer !== null);
|
|
60
|
+
if (note === null && trailers.length === 0) return element;
|
|
61
|
+
return createElement(Box, { flexDirection: "column" }, element, note !== null ? createElement(Text, null, note) : null, ...trailers.map((trailer, index) => createElement(Box, {
|
|
62
|
+
key: `trailer:${index}`,
|
|
63
|
+
marginTop: 1
|
|
64
|
+
}, createElement(Text, null, trailer))));
|
|
58
65
|
};
|
|
59
66
|
|
|
60
67
|
export { dispatchInk, inkDispatcherTable };
|
package/ink.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
import { FC, ReactElement } from "react";
|
|
2
|
+
import { FC, ReactElement, ReactNode } from "react";
|
|
3
3
|
/**
|
|
4
4
|
* Per-file coverage data including uncovered line ranges.
|
|
5
5
|
* @public
|
|
@@ -7,10 +7,10 @@ import { FC, ReactElement } from "react";
|
|
|
7
7
|
declare const FileCoverageReport: Schema.Struct<{
|
|
8
8
|
readonly file: Schema.String;
|
|
9
9
|
readonly summary: Schema.Struct<{
|
|
10
|
-
readonly statements: Schema.
|
|
11
|
-
readonly branches: Schema.
|
|
12
|
-
readonly functions: Schema.
|
|
13
|
-
readonly lines: Schema.
|
|
10
|
+
readonly statements: Schema.Finite;
|
|
11
|
+
readonly branches: Schema.Finite;
|
|
12
|
+
readonly functions: Schema.Finite;
|
|
13
|
+
readonly lines: Schema.Finite;
|
|
14
14
|
}>;
|
|
15
15
|
readonly uncoveredLines: Schema.String;
|
|
16
16
|
}>;
|
|
@@ -107,24 +107,24 @@ type FailureRecord = typeof FailureRecord.Type;
|
|
|
107
107
|
*/
|
|
108
108
|
declare const CoverageRenderState: Schema.Struct<{
|
|
109
109
|
readonly metrics: Schema.Struct<{
|
|
110
|
-
readonly statements: Schema.
|
|
111
|
-
readonly branches: Schema.
|
|
112
|
-
readonly functions: Schema.
|
|
113
|
-
readonly lines: Schema.
|
|
110
|
+
readonly statements: Schema.Finite;
|
|
111
|
+
readonly branches: Schema.Finite;
|
|
112
|
+
readonly functions: Schema.Finite;
|
|
113
|
+
readonly lines: Schema.Finite;
|
|
114
114
|
}>;
|
|
115
115
|
readonly thresholds: Schema.Struct<{
|
|
116
|
-
readonly lines: Schema.optional<Schema.
|
|
117
|
-
readonly functions: Schema.optional<Schema.
|
|
118
|
-
readonly branches: Schema.optional<Schema.
|
|
119
|
-
readonly statements: Schema.optional<Schema.
|
|
116
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
117
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
118
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
119
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
120
120
|
}>;
|
|
121
121
|
readonly gaps: Schema.$Array<Schema.Struct<{
|
|
122
122
|
readonly file: Schema.String;
|
|
123
123
|
readonly missing: Schema.Struct<{
|
|
124
|
-
readonly statements: Schema.
|
|
125
|
-
readonly branches: Schema.
|
|
126
|
-
readonly functions: Schema.
|
|
127
|
-
readonly lines: Schema.
|
|
124
|
+
readonly statements: Schema.Finite;
|
|
125
|
+
readonly branches: Schema.Finite;
|
|
126
|
+
readonly functions: Schema.Finite;
|
|
127
|
+
readonly lines: Schema.Finite;
|
|
128
128
|
}>;
|
|
129
129
|
readonly uncoveredLines: Schema.optional<Schema.String>;
|
|
130
130
|
}>>;
|
|
@@ -139,16 +139,16 @@ declare const CoverageRenderState: Schema.Struct<{
|
|
|
139
139
|
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
140
140
|
readonly pattern: Schema.String;
|
|
141
141
|
readonly summary: Schema.Struct<{
|
|
142
|
-
readonly statements: Schema.
|
|
143
|
-
readonly branches: Schema.
|
|
144
|
-
readonly functions: Schema.
|
|
145
|
-
readonly lines: Schema.
|
|
142
|
+
readonly statements: Schema.Finite;
|
|
143
|
+
readonly branches: Schema.Finite;
|
|
144
|
+
readonly functions: Schema.Finite;
|
|
145
|
+
readonly lines: Schema.Finite;
|
|
146
146
|
}>;
|
|
147
147
|
readonly thresholds: Schema.Struct<{
|
|
148
|
-
readonly lines: Schema.optional<Schema.
|
|
149
|
-
readonly functions: Schema.optional<Schema.
|
|
150
|
-
readonly branches: Schema.optional<Schema.
|
|
151
|
-
readonly statements: Schema.optional<Schema.
|
|
148
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
149
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
150
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
151
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
152
152
|
}>;
|
|
153
153
|
}>>>;
|
|
154
154
|
}>;
|
|
@@ -229,24 +229,24 @@ declare const RenderState: Schema.Struct<{
|
|
|
229
229
|
}>;
|
|
230
230
|
readonly coverage: Schema.NullOr<Schema.Struct<{
|
|
231
231
|
readonly metrics: Schema.Struct<{
|
|
232
|
-
readonly statements: Schema.
|
|
233
|
-
readonly branches: Schema.
|
|
234
|
-
readonly functions: Schema.
|
|
235
|
-
readonly lines: Schema.
|
|
232
|
+
readonly statements: Schema.Finite;
|
|
233
|
+
readonly branches: Schema.Finite;
|
|
234
|
+
readonly functions: Schema.Finite;
|
|
235
|
+
readonly lines: Schema.Finite;
|
|
236
236
|
}>;
|
|
237
237
|
readonly thresholds: Schema.Struct<{
|
|
238
|
-
readonly lines: Schema.optional<Schema.
|
|
239
|
-
readonly functions: Schema.optional<Schema.
|
|
240
|
-
readonly branches: Schema.optional<Schema.
|
|
241
|
-
readonly statements: Schema.optional<Schema.
|
|
238
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
239
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
240
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
241
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
242
242
|
}>;
|
|
243
243
|
readonly gaps: Schema.$Array<Schema.Struct<{
|
|
244
244
|
readonly file: Schema.String;
|
|
245
245
|
readonly missing: Schema.Struct<{
|
|
246
|
-
readonly statements: Schema.
|
|
247
|
-
readonly branches: Schema.
|
|
248
|
-
readonly functions: Schema.
|
|
249
|
-
readonly lines: Schema.
|
|
246
|
+
readonly statements: Schema.Finite;
|
|
247
|
+
readonly branches: Schema.Finite;
|
|
248
|
+
readonly functions: Schema.Finite;
|
|
249
|
+
readonly lines: Schema.Finite;
|
|
250
250
|
}>;
|
|
251
251
|
readonly uncoveredLines: Schema.optional<Schema.String>;
|
|
252
252
|
}>>;
|
|
@@ -261,16 +261,16 @@ declare const RenderState: Schema.Struct<{
|
|
|
261
261
|
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
262
262
|
readonly pattern: Schema.String;
|
|
263
263
|
readonly summary: Schema.Struct<{
|
|
264
|
-
readonly statements: Schema.
|
|
265
|
-
readonly branches: Schema.
|
|
266
|
-
readonly functions: Schema.
|
|
267
|
-
readonly lines: Schema.
|
|
264
|
+
readonly statements: Schema.Finite;
|
|
265
|
+
readonly branches: Schema.Finite;
|
|
266
|
+
readonly functions: Schema.Finite;
|
|
267
|
+
readonly lines: Schema.Finite;
|
|
268
268
|
}>;
|
|
269
269
|
readonly thresholds: Schema.Struct<{
|
|
270
|
-
readonly lines: Schema.optional<Schema.
|
|
271
|
-
readonly functions: Schema.optional<Schema.
|
|
272
|
-
readonly branches: Schema.optional<Schema.
|
|
273
|
-
readonly statements: Schema.optional<Schema.
|
|
270
|
+
readonly lines: Schema.optional<Schema.Finite>;
|
|
271
|
+
readonly functions: Schema.optional<Schema.Finite>;
|
|
272
|
+
readonly branches: Schema.optional<Schema.Finite>;
|
|
273
|
+
readonly statements: Schema.optional<Schema.Finite>;
|
|
274
274
|
}>;
|
|
275
275
|
}>>>;
|
|
276
276
|
}>>;
|
|
@@ -327,15 +327,35 @@ declare const RenderState: Schema.Struct<{
|
|
|
327
327
|
* wrote nothing stray.
|
|
328
328
|
*/
|
|
329
329
|
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
330
|
-
readonly total: Schema.
|
|
331
|
-
readonly stdout: Schema.
|
|
332
|
-
readonly stderr: Schema.
|
|
333
|
-
readonly bytes: Schema.
|
|
330
|
+
readonly total: Schema.Finite;
|
|
331
|
+
readonly stdout: Schema.Finite;
|
|
332
|
+
readonly stderr: Schema.Finite;
|
|
333
|
+
readonly bytes: Schema.Finite;
|
|
334
334
|
readonly samples: Schema.$Array<Schema.Struct<{
|
|
335
335
|
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
336
336
|
readonly text: Schema.String;
|
|
337
337
|
}>>;
|
|
338
338
|
}>>;
|
|
339
|
+
/**
|
|
340
|
+
* `console.*` output Vitest captured from tests that did not fail, folded
|
|
341
|
+
* from `RunFinished.consoleLeaks`. Absent when the run logged nothing
|
|
342
|
+
* (issue #544).
|
|
343
|
+
*/
|
|
344
|
+
readonly consoleLeaks: Schema.optional<Schema.Struct<{
|
|
345
|
+
readonly total: Schema.Finite;
|
|
346
|
+
readonly byFile: Schema.$Array<Schema.Struct<{
|
|
347
|
+
readonly file: Schema.String;
|
|
348
|
+
readonly stdout: Schema.Finite;
|
|
349
|
+
readonly stderr: Schema.Finite;
|
|
350
|
+
readonly tests: Schema.optional<Schema.$Array<Schema.String>>;
|
|
351
|
+
readonly sample: Schema.optional<Schema.String>;
|
|
352
|
+
}>>;
|
|
353
|
+
readonly truncated: Schema.optional<Schema.Boolean>;
|
|
354
|
+
readonly fromFailingTests: Schema.optional<Schema.Struct<{
|
|
355
|
+
readonly total: Schema.Finite;
|
|
356
|
+
readonly files: Schema.Finite;
|
|
357
|
+
}>>;
|
|
358
|
+
}>>;
|
|
339
359
|
}>;
|
|
340
360
|
/** @public */
|
|
341
361
|
type RenderState = typeof RenderState.Type;
|
|
@@ -438,27 +458,41 @@ interface DispatchInputs {
|
|
|
438
458
|
readonly runCommand: string | null;
|
|
439
459
|
}
|
|
440
460
|
/**
|
|
441
|
-
*
|
|
442
|
-
* `ReporterKit`. These are concerns the cell needs to format its
|
|
443
|
-
* output but that do not belong on the state-derived
|
|
444
|
-
* {@link DispatchInputs} struct.
|
|
461
|
+
* Where a file link points: a path, with an optional line and column.
|
|
445
462
|
*
|
|
446
|
-
* The
|
|
463
|
+
* The file half of `@effected/cli`'s `LinkTarget`, spelled structurally so
|
|
464
|
+
* this platform-free contract names no kit type. A relative path resolves
|
|
465
|
+
* against the working directory the kit's `CliLinks` layer was built with.
|
|
466
|
+
* @public
|
|
467
|
+
*/
|
|
468
|
+
interface FileLinkTarget {
|
|
469
|
+
readonly file: string;
|
|
470
|
+
readonly line?: number;
|
|
471
|
+
readonly col?: number;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Render-time options threaded into each cell. These are concerns the
|
|
475
|
+
* cell needs to format its output but that do not belong on the
|
|
476
|
+
* state-derived {@link DispatchInputs} struct.
|
|
477
|
+
*
|
|
478
|
+
* The caller builds this once per render and passes it into
|
|
447
479
|
* `dispatch(inputs, opts)`. Cells destructure only what they consume.
|
|
448
480
|
* @public
|
|
449
481
|
*/
|
|
450
482
|
interface CellOptions {
|
|
451
483
|
/**
|
|
452
|
-
*
|
|
453
|
-
*
|
|
484
|
+
* Writes a file path as a link for the audience and terminal the output
|
|
485
|
+
* is for: the label wrapped in an OSC 8 hyperlink when the reader can
|
|
486
|
+
* follow it, the label unchanged otherwise. The kit decides both halves
|
|
487
|
+
* (`@effected/cli`'s `CliLinks.linker`, `Render.context("stdout").link`):
|
|
488
|
+
* `vscode://file/<path>:<line>:<col>` under VS Code, `file://` otherwise,
|
|
489
|
+
* never for an agent audience, and nothing on a terminal that cannot
|
|
490
|
+
* render hyperlinks (a pipe, tmux, GNU screen, `NO_COLOR`).
|
|
491
|
+
*
|
|
492
|
+
* Omitted, every path is its label alone, which is what an agent reads;
|
|
493
|
+
* a cell therefore never writes an escape sequence of its own.
|
|
454
494
|
*/
|
|
455
|
-
readonly
|
|
456
|
-
/**
|
|
457
|
-
* Pre-bound OSC-8 hyperlink helper. The plugin has already decided
|
|
458
|
-
* whether OSC-8 should be enabled (`target=stdout`, `!noColor`) so
|
|
459
|
-
* cells can call this directly without re-consulting the environment.
|
|
460
|
-
*/
|
|
461
|
-
readonly osc8: (url: string, label: string) => string;
|
|
495
|
+
readonly link?: (target: FileLinkTarget, label: string) => string;
|
|
462
496
|
}
|
|
463
497
|
/**
|
|
464
498
|
* Props for the `CountColumns` component.
|
|
@@ -574,6 +608,37 @@ interface FailuresSectionProps {
|
|
|
574
608
|
* @public
|
|
575
609
|
*/
|
|
576
610
|
export declare const FailuresSection: FC<FailuresSectionProps>;
|
|
611
|
+
/**
|
|
612
|
+
* The link writer: a file target and its label in, the label (linked when
|
|
613
|
+
* the reader can follow it) out. The same shape as `CellOptions.link`.
|
|
614
|
+
*
|
|
615
|
+
* @public
|
|
616
|
+
*/
|
|
617
|
+
type FileLink = NonNullable<CellOptions["link"]>;
|
|
618
|
+
/**
|
|
619
|
+
* Props for {@link FileLinkProvider}.
|
|
620
|
+
*
|
|
621
|
+
* @public
|
|
622
|
+
*/
|
|
623
|
+
interface FileLinkProviderProps {
|
|
624
|
+
/** The writer; omitted, paths are their labels alone. */
|
|
625
|
+
readonly link?: FileLink | undefined;
|
|
626
|
+
/** The tree. */
|
|
627
|
+
readonly children?: ReactNode;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Provide the file-link writer to an Ink tree.
|
|
631
|
+
*
|
|
632
|
+
* @public
|
|
633
|
+
*/
|
|
634
|
+
export declare const FileLinkProvider: FC<FileLinkProviderProps>;
|
|
635
|
+
/**
|
|
636
|
+
* The file-link writer of the tree: the nearest {@link FileLinkProvider}'s,
|
|
637
|
+
* or the label alone.
|
|
638
|
+
*
|
|
639
|
+
* @public
|
|
640
|
+
*/
|
|
641
|
+
export declare const useFileLink: () => FileLink;
|
|
577
642
|
/**
|
|
578
643
|
* Props for the `ModuleHeader` component.
|
|
579
644
|
*
|
|
@@ -753,5 +818,5 @@ interface TrendLineProps {
|
|
|
753
818
|
* @public
|
|
754
819
|
*/
|
|
755
820
|
export declare const TrendLine: FC<TrendLineProps>;
|
|
756
|
-
export type { CellOptions, CountColumnsProps, CoverageBlockProps, CoverageRenderState, DispatchInputs, FailureRecord, FailureSectionProps, FailuresSectionProps, FileCoverageReport, InkCellFn, ModuleHeaderProps, ModuleRecord, ProjectRowProps, ProjectSummary, RenderState, RunOutcome, RunShape, StatusIconKind, StatusIconProps, StreamAppProps, SuggestedActionRecord, SuggestedActionsProps, TagColumnsProps, TestRecord, TestRowProps, TrendLineProps, TrendSummary };
|
|
821
|
+
export type { CellOptions, CountColumnsProps, CoverageBlockProps, CoverageRenderState, DispatchInputs, FailureRecord, FailureSectionProps, FailuresSectionProps, FileCoverageReport, FileLink, FileLinkProviderProps, FileLinkTarget, InkCellFn, ModuleHeaderProps, ModuleRecord, ProjectRowProps, ProjectSummary, RenderState, RunOutcome, RunShape, StatusIconKind, StatusIconProps, StreamAppProps, SuggestedActionRecord, SuggestedActionsProps, TagColumnsProps, TestRecord, TestRowProps, TrendLineProps, TrendSummary };
|
|
757
822
|
//# sourceMappingURL=ink.d.ts.map
|
package/ink.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CountColumns, DURATION_CELL_WIDTH } from "./ink/CountColumns.js";
|
|
2
|
+
import { FileLinkProvider, useFileLink } from "./ink/FileLink.js";
|
|
2
3
|
import { CoverageBlock } from "./ink/CoverageBlock.js";
|
|
3
4
|
import { dispatchInk, inkDispatcherTable } from "./ink/dispatch-ink.js";
|
|
4
5
|
import { FailureSection } from "./ink/FailureSection.js";
|
|
@@ -12,4 +13,4 @@ import { TrendLine } from "./ink/TrendLine.js";
|
|
|
12
13
|
import { StreamApp } from "./ink/StreamApp.js";
|
|
13
14
|
import { SuggestedActions } from "./ink/SuggestedActions.js";
|
|
14
15
|
|
|
15
|
-
export { CountColumns, CoverageBlock, DURATION_CELL_WIDTH, FailureSection, FailuresSection, ModuleHeader, ProjectRow, StatusIcon, StreamApp, SuggestedActions, TagColumns, TestRow, TrendLine, dispatchInk, inkDispatcherTable, tagUnion };
|
|
16
|
+
export { CountColumns, CoverageBlock, DURATION_CELL_WIDTH, FailureSection, FailuresSection, FileLinkProvider, ModuleHeader, ProjectRow, StatusIcon, StreamApp, SuggestedActions, TagColumns, TestRow, TrendLine, dispatchInk, inkDispatcherTable, tagUnion, useFileLink };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/ui",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
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": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"./package.json": "./package.json"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@vitest-agent/sdk": "
|
|
47
|
+
"@vitest-agent/sdk": "8.0.0",
|
|
48
48
|
"effect": "^4.0.6"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
package/reducer.js
CHANGED
|
@@ -225,7 +225,8 @@ const reduceRenderState = (state, event) => {
|
|
|
225
225
|
},
|
|
226
226
|
...e.collectedModules !== void 0 && { collectedModules: e.collectedModules },
|
|
227
227
|
...e.unhandledErrors !== void 0 && { unhandledErrors: e.unhandledErrors },
|
|
228
|
-
...e.strayOutput !== void 0 && { strayOutput: e.strayOutput }
|
|
228
|
+
...e.strayOutput !== void 0 && { strayOutput: e.strayOutput },
|
|
229
|
+
...e.consoleLeaks !== void 0 && { consoleLeaks: e.consoleLeaks }
|
|
229
230
|
}),
|
|
230
231
|
RunTimedOut: () => ({
|
|
231
232
|
...state,
|
package/render-agent.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatTotalsLine } from "./counts.js";
|
|
2
|
-
import { formatFailure as formatFailure$1, formatGlobShortfallLines } from "./dispatcher/helpers.js";
|
|
3
|
-
import { formatStrayOutputNote } from "@vitest-agent/sdk";
|
|
2
|
+
import { formatFailure as formatFailure$1, formatGlobShortfallLines, formatSuggestedActionLines } from "./dispatcher/helpers.js";
|
|
3
|
+
import { formatConsoleLeaksNote, formatStrayOutputNote } from "@vitest-agent/sdk";
|
|
4
4
|
import { Fmt } from "@effected/cli";
|
|
5
5
|
|
|
6
6
|
// src/render-agent.ts
|
|
@@ -16,11 +16,6 @@ import { Fmt } from "@effected/cli";
|
|
|
16
16
|
*/
|
|
17
17
|
const DEFAULT_WIDTH = 80;
|
|
18
18
|
const DEFAULT_MAX_GAPS = 3;
|
|
19
|
-
const severityLabel = {
|
|
20
|
-
info: "info",
|
|
21
|
-
warn: "warn",
|
|
22
|
-
blocker: "blocker"
|
|
23
|
-
};
|
|
24
19
|
const formatHeader = (state) => formatTotalsLine({
|
|
25
20
|
label: "Tests",
|
|
26
21
|
...state.totals,
|
|
@@ -106,14 +101,13 @@ const formatCoverageSection = (state, maxGaps) => {
|
|
|
106
101
|
return lines.join("\n");
|
|
107
102
|
};
|
|
108
103
|
const formatActionsSection = (state) => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
for (const a of state.suggestedActions) lines.push(formatAction(a));
|
|
112
|
-
return lines.join("\n");
|
|
104
|
+
const lines = formatSuggestedActionLines(state.suggestedActions);
|
|
105
|
+
return lines.length === 0 ? null : lines.join("\n");
|
|
113
106
|
};
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
const formatConsoleLeaksSection = (state) => {
|
|
108
|
+
if (state.consoleLeaks === void 0) return null;
|
|
109
|
+
const lines = formatConsoleLeaksNote(state.consoleLeaks);
|
|
110
|
+
return lines.length === 0 ? null : lines.join("\n");
|
|
117
111
|
};
|
|
118
112
|
/**
|
|
119
113
|
* Project the reduced `RenderState` into a single string suitable
|
|
@@ -143,6 +137,8 @@ const renderAgent = (state, options = {}) => {
|
|
|
143
137
|
const actions = formatActionsSection(state);
|
|
144
138
|
if (actions !== null) sections.push(actions);
|
|
145
139
|
if (state.strayOutput !== void 0) sections.push(formatStrayOutputNote(state.strayOutput).join("\n"));
|
|
140
|
+
const leaks = formatConsoleLeaksSection(state);
|
|
141
|
+
if (leaks !== null) sections.push(leaks);
|
|
146
142
|
return `${sections.join("\n\n")}\n`;
|
|
147
143
|
};
|
|
148
144
|
|
package/synthesize.js
CHANGED
|
@@ -387,7 +387,8 @@ const synthesizeFromAgentReport = (report, options = {}) => {
|
|
|
387
387
|
...totalTimeoutCount > 0 && { timeoutCount: totalTimeoutCount },
|
|
388
388
|
...report.summary.modules !== void 0 && { collectedModules: report.summary.modules },
|
|
389
389
|
...report.unhandledErrors.length > 0 && { unhandledErrors: report.unhandledErrors },
|
|
390
|
-
...report.strayOutput !== void 0 && { strayOutput: report.strayOutput }
|
|
390
|
+
...report.strayOutput !== void 0 && { strayOutput: report.strayOutput },
|
|
391
|
+
...report.consoleLeaks !== void 0 && { consoleLeaks: report.consoleLeaks }
|
|
391
392
|
});
|
|
392
393
|
return events;
|
|
393
394
|
};
|