@vitest-agent/reporter 4.1.0 → 5.0.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/defaultReporter.js +21 -13
- package/humanReport.js +36 -0
- package/index.d.ts +115 -13
- package/index.js +1 -1
- package/liveView.js +35 -13
- package/package.json +7 -7
- package/streamView.js +13 -0
package/defaultReporter.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { renderGithubLog, toDisplayPath } from "./githubLog.js";
|
|
2
2
|
import { LiveViewEnv, startLiveView } from "./liveView.js";
|
|
3
3
|
import { Doc, Render, Status } from "@effected/cli";
|
|
4
|
-
import { CliUi, UiProvider } from "@effected/cli/ui";
|
|
5
4
|
import { RUN_REPORT_FILE_SCHEMA_URL, countSuiteFailures, isTimeoutError } from "@vitest-agent/sdk";
|
|
6
|
-
import { classifyOutcome, classifyRunShape, dispatch,
|
|
7
|
-
import { Effect } from "effect";
|
|
8
|
-
import { createElement } from "react";
|
|
5
|
+
import { classifyOutcome, classifyRunShape, dispatch, reduceRenderStateAll, synthesizeFromAgentReport } from "@vitest-agent/ui";
|
|
9
6
|
|
|
10
7
|
//#region src/defaultReporter.ts
|
|
11
8
|
const countTimeouts = (report) => {
|
|
@@ -128,10 +125,21 @@ const renderAgentStringForReport = (report) => {
|
|
|
128
125
|
* Returns the agent-string fallback when the matched cell has no Ink
|
|
129
126
|
* half.
|
|
130
127
|
*
|
|
128
|
+
* The output is laid out at `options.width` when given, else at the
|
|
129
|
+
* terminal's width (stdout's columns, else the `COLUMNS` variable), else at
|
|
130
|
+
* 80 columns, which is what a non-TTY run gets. React and Ink are loaded on
|
|
131
|
+
* the first call, never by importing this package.
|
|
132
|
+
*
|
|
131
133
|
* @public
|
|
132
134
|
*/
|
|
133
|
-
const renderHumanStringForReport =
|
|
134
|
-
|
|
135
|
+
const renderHumanStringForReport = (report, options = {}) => renderHumanString(report, options, LiveViewEnv);
|
|
136
|
+
/**
|
|
137
|
+
* {@link renderHumanStringForReport} over an explicit environment, which
|
|
138
|
+
* decides the theme, the glyphs and the terminal width.
|
|
139
|
+
*
|
|
140
|
+
* @internal
|
|
141
|
+
*/
|
|
142
|
+
const renderHumanString = async (report, options, env) => {
|
|
135
143
|
const events = synthesizeFromAgentReport(report);
|
|
136
144
|
const state = reduceRenderStateAll(events);
|
|
137
145
|
const projects = [summarizeProject(report)];
|
|
@@ -148,11 +156,8 @@ const renderHumanStringForReport = async (report, options = {}) => {
|
|
|
148
156
|
noColor: false,
|
|
149
157
|
osc8: (_url, label) => label
|
|
150
158
|
};
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
const columns = options.width ?? 80;
|
|
154
|
-
const context = await Effect.runPromise(CliUi.context.pipe(Effect.provide(LiveViewEnv)));
|
|
155
|
-
return renderToString(createElement(UiProvider, { value: context }, cell.ink(inputs, opts)), { columns });
|
|
159
|
+
const { renderInkReport } = await import("./humanReport.js");
|
|
160
|
+
return await renderInkReport(inputs, opts, env, options.width) ?? dispatch(inputs, opts);
|
|
156
161
|
};
|
|
157
162
|
const NON_STABLE_SUMMARY_CLASSIFICATIONS = [
|
|
158
163
|
"new-failure",
|
|
@@ -329,7 +334,10 @@ const renderGithubSummary = (input) => {
|
|
|
329
334
|
const DefaultVitestAgentReporter = (kit) => {
|
|
330
335
|
const live = kit.config.consoleMode === "stream" && kit.runEvents !== void 0 ? startLiveView(kit.runEvents) : void 0;
|
|
331
336
|
return {
|
|
332
|
-
...live !== void 0 ? {
|
|
337
|
+
...live !== void 0 ? {
|
|
338
|
+
close: live.close,
|
|
339
|
+
printStrayLine: live.printStrayLine
|
|
340
|
+
} : {},
|
|
333
341
|
render(input, renderKit) {
|
|
334
342
|
const out = [];
|
|
335
343
|
if (shouldRenderForMode(renderKit.config.consoleMode)) {
|
|
@@ -371,4 +379,4 @@ const DefaultVitestAgentReporter = (kit) => {
|
|
|
371
379
|
};
|
|
372
380
|
|
|
373
381
|
//#endregion
|
|
374
|
-
export { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanStringForReport, resolveCellOptions };
|
|
382
|
+
export { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanString, renderHumanStringForReport, resolveCellOptions };
|
package/humanReport.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { CliUi, UiProvider } from "@effected/cli/ui";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { TerminalEnv } from "@effected/env";
|
|
4
|
+
import { dispatchInk } from "@vitest-agent/ui/ink";
|
|
5
|
+
import { renderToString } from "ink";
|
|
6
|
+
import { createElement } from "react";
|
|
7
|
+
|
|
8
|
+
//#region src/humanReport.ts
|
|
9
|
+
/** The width a report is laid out at when no terminal width is known. */
|
|
10
|
+
const FALLBACK_COLUMNS = 80;
|
|
11
|
+
/**
|
|
12
|
+
* Render the matched cell's Ink half to a string, or `null` when the cell
|
|
13
|
+
* has no Ink half (the caller falls back to the agent string).
|
|
14
|
+
*
|
|
15
|
+
* The layout width is `width` when given, else the terminal's
|
|
16
|
+
* (`TerminalEnv.width`: stdout's columns, else `COLUMNS`, else 80 — so a
|
|
17
|
+
* non-TTY run lays out at 80).
|
|
18
|
+
*
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
const renderInkReport = async (inputs, opts, env, width) => {
|
|
22
|
+
const element = dispatchInk(inputs, opts);
|
|
23
|
+
if (element === null) return null;
|
|
24
|
+
const { context, columns } = await Effect.runPromise(Effect.gen(function* () {
|
|
25
|
+
const context = yield* CliUi.context;
|
|
26
|
+
const terminal = yield* TerminalEnv;
|
|
27
|
+
return {
|
|
28
|
+
context,
|
|
29
|
+
columns: width ?? terminal.width(FALLBACK_COLUMNS)
|
|
30
|
+
};
|
|
31
|
+
}).pipe(Effect.provide(env)));
|
|
32
|
+
return renderToString(createElement(UiProvider, { value: context }, element), { columns });
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { renderInkReport };
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { PubSub, Schema } from "effect";
|
|
2
|
+
import "@effected/cli";
|
|
3
|
+
import "@effected/env";
|
|
2
4
|
//#region ../sdk/dist/dev/pkg/index.d.ts
|
|
3
5
|
/**
|
|
4
6
|
* Per-file coverage data including uncovered line ranges.
|
|
@@ -108,6 +110,21 @@ declare const RenderState: Schema.Struct<{
|
|
|
108
110
|
readonly scoped: Schema.optional<Schema.Boolean>;
|
|
109
111
|
readonly scopedFiles: Schema.optional<Schema.Number>;
|
|
110
112
|
readonly totalFiles: Schema.optional<Schema.Number>;
|
|
113
|
+
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
114
|
+
readonly pattern: Schema.String;
|
|
115
|
+
readonly summary: Schema.Struct<{
|
|
116
|
+
readonly statements: Schema.Number;
|
|
117
|
+
readonly branches: Schema.Number;
|
|
118
|
+
readonly functions: Schema.Number;
|
|
119
|
+
readonly lines: Schema.Number;
|
|
120
|
+
}>;
|
|
121
|
+
readonly thresholds: Schema.Struct<{
|
|
122
|
+
readonly lines: Schema.optional<Schema.Number>;
|
|
123
|
+
readonly functions: Schema.optional<Schema.Number>;
|
|
124
|
+
readonly branches: Schema.optional<Schema.Number>;
|
|
125
|
+
readonly statements: Schema.optional<Schema.Number>;
|
|
126
|
+
}>;
|
|
127
|
+
}>>>;
|
|
111
128
|
}>>;
|
|
112
129
|
readonly trend: Schema.NullOr<Schema.Struct<{
|
|
113
130
|
readonly direction: Schema.Literals<readonly ["improving", "regressing", "stable"]>;
|
|
@@ -156,6 +173,21 @@ declare const RenderState: Schema.Struct<{
|
|
|
156
173
|
readonly expected: Schema.optional<Schema.String>;
|
|
157
174
|
readonly received: Schema.optional<Schema.String>;
|
|
158
175
|
}>>;
|
|
176
|
+
/**
|
|
177
|
+
* What the run wrote straight to the terminal, past Vitest's console
|
|
178
|
+
* capture, folded from `RunFinished.strayOutput`. Absent when the run
|
|
179
|
+
* wrote nothing stray.
|
|
180
|
+
*/
|
|
181
|
+
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
182
|
+
readonly total: Schema.Number;
|
|
183
|
+
readonly stdout: Schema.Number;
|
|
184
|
+
readonly stderr: Schema.Number;
|
|
185
|
+
readonly bytes: Schema.Number;
|
|
186
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
187
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
188
|
+
readonly text: Schema.String;
|
|
189
|
+
}>>;
|
|
190
|
+
}>>;
|
|
159
191
|
}>;
|
|
160
192
|
/** @public */
|
|
161
193
|
type RenderState = typeof RenderState.Type;
|
|
@@ -458,6 +490,21 @@ declare const AgentReport: Schema.Struct<{
|
|
|
458
490
|
readonly uncoveredLines: Schema.String;
|
|
459
491
|
}>>>;
|
|
460
492
|
readonly belowTargetFiles: Schema.optional<Schema.$Array<Schema.String>>;
|
|
493
|
+
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
494
|
+
readonly pattern: Schema.String;
|
|
495
|
+
readonly summary: Schema.Struct<{
|
|
496
|
+
readonly statements: Schema.Number;
|
|
497
|
+
readonly branches: Schema.Number;
|
|
498
|
+
readonly functions: Schema.Number;
|
|
499
|
+
readonly lines: Schema.Number;
|
|
500
|
+
}>;
|
|
501
|
+
readonly thresholds: Schema.Struct<{
|
|
502
|
+
readonly lines: Schema.optional<Schema.Number>;
|
|
503
|
+
readonly functions: Schema.optional<Schema.Number>;
|
|
504
|
+
readonly branches: Schema.optional<Schema.Number>;
|
|
505
|
+
readonly statements: Schema.optional<Schema.Number>;
|
|
506
|
+
}>;
|
|
507
|
+
}>>>;
|
|
461
508
|
}>>;
|
|
462
509
|
readonly tagCounts: Schema.optional<Schema.$Record<Schema.String, Schema.Struct<{
|
|
463
510
|
readonly passed: Schema.optional<Schema.Number>;
|
|
@@ -479,6 +526,16 @@ declare const AgentReport: Schema.Struct<{
|
|
|
479
526
|
readonly files: Schema.Number;
|
|
480
527
|
}>>;
|
|
481
528
|
}>>;
|
|
529
|
+
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
530
|
+
readonly total: Schema.Number;
|
|
531
|
+
readonly stdout: Schema.Number;
|
|
532
|
+
readonly stderr: Schema.Number;
|
|
533
|
+
readonly bytes: Schema.Number;
|
|
534
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
535
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
536
|
+
readonly text: Schema.String;
|
|
537
|
+
}>>;
|
|
538
|
+
}>>;
|
|
482
539
|
}>;
|
|
483
540
|
/** @public */
|
|
484
541
|
type AgentReport = typeof AgentReport.Type;
|
|
@@ -511,13 +568,6 @@ type Environment = typeof Environment.Type;
|
|
|
511
568
|
declare const Executor: Schema.Literals<readonly ["human", "agent", "ci"]>;
|
|
512
569
|
/** @public */
|
|
513
570
|
type Executor = typeof Executor.Type;
|
|
514
|
-
/**
|
|
515
|
-
* Output format for the reporter pipeline.
|
|
516
|
-
* @public
|
|
517
|
-
*/
|
|
518
|
-
declare const OutputFormat: Schema.Literals<readonly ["terminal", "markdown", "json", "vitest-bypass", "silent", "ci-annotations"]>;
|
|
519
|
-
/** @public */
|
|
520
|
-
type OutputFormat = typeof OutputFormat.Type;
|
|
521
571
|
/**
|
|
522
572
|
* Level of detail in reporter output.
|
|
523
573
|
* @public
|
|
@@ -680,6 +730,21 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
|
|
|
680
730
|
readonly scoped: Schema.optional<Schema.Boolean>;
|
|
681
731
|
readonly scopedFiles: Schema.optional<Schema.Number>;
|
|
682
732
|
readonly totalFiles: Schema.optional<Schema.Number>;
|
|
733
|
+
readonly globShortfalls: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
734
|
+
readonly pattern: Schema.String;
|
|
735
|
+
readonly summary: Schema.Struct<{
|
|
736
|
+
readonly statements: Schema.Number;
|
|
737
|
+
readonly branches: Schema.Number;
|
|
738
|
+
readonly functions: Schema.Number;
|
|
739
|
+
readonly lines: Schema.Number;
|
|
740
|
+
}>;
|
|
741
|
+
readonly thresholds: Schema.Struct<{
|
|
742
|
+
readonly lines: Schema.optional<Schema.Number>;
|
|
743
|
+
readonly functions: Schema.optional<Schema.Number>;
|
|
744
|
+
readonly branches: Schema.optional<Schema.Number>;
|
|
745
|
+
readonly statements: Schema.optional<Schema.Number>;
|
|
746
|
+
}>;
|
|
747
|
+
}>>>;
|
|
683
748
|
}>, Schema.TaggedStruct<"ThresholdViolation", {
|
|
684
749
|
readonly metric: Schema.Literals<readonly ["lines", "branches", "functions", "statements"]>;
|
|
685
750
|
readonly expected: Schema.Number;
|
|
@@ -722,6 +787,21 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
|
|
|
722
787
|
readonly expected: Schema.optional<Schema.String>;
|
|
723
788
|
readonly received: Schema.optional<Schema.String>;
|
|
724
789
|
}>>>;
|
|
790
|
+
/**
|
|
791
|
+
* What the run wrote straight to the terminal, past Vitest's console
|
|
792
|
+
* capture (`AgentReport.strayOutput`). Optional: absent when the run
|
|
793
|
+
* wrote nothing stray, and in older replay data.
|
|
794
|
+
*/
|
|
795
|
+
readonly strayOutput: Schema.optional<Schema.Struct<{
|
|
796
|
+
readonly total: Schema.Number;
|
|
797
|
+
readonly stdout: Schema.Number;
|
|
798
|
+
readonly stderr: Schema.Number;
|
|
799
|
+
readonly bytes: Schema.Number;
|
|
800
|
+
readonly samples: Schema.$Array<Schema.Struct<{
|
|
801
|
+
readonly stream: Schema.Literals<readonly ["stdout", "stderr"]>;
|
|
802
|
+
readonly text: Schema.String;
|
|
803
|
+
}>>;
|
|
804
|
+
}>>;
|
|
725
805
|
}>]>;
|
|
726
806
|
/** @public */
|
|
727
807
|
type RunEvent = typeof RunEvent.Type;
|
|
@@ -784,10 +864,10 @@ type Transport = typeof Transport.Type;
|
|
|
784
864
|
* plugin always populates it in practice — the option exists for clarity
|
|
785
865
|
* and to leave room for future "no-persistence" experiments.
|
|
786
866
|
*
|
|
787
|
-
* `
|
|
788
|
-
*
|
|
789
|
-
*
|
|
790
|
-
*
|
|
867
|
+
* `detail` is pre-resolved by the plugin (via `DetailResolver`) but
|
|
868
|
+
* reporters that want to override can ignore it. `noColor` is the resolved
|
|
869
|
+
* value of the `NO_COLOR` env var; reporters use it to gate ANSI escapes and
|
|
870
|
+
* OSC-8 hyperlinks.
|
|
791
871
|
* @public
|
|
792
872
|
*/
|
|
793
873
|
interface ResolvedReporterConfig {
|
|
@@ -826,7 +906,6 @@ interface ResolvedReporterConfig {
|
|
|
826
906
|
* @internal
|
|
827
907
|
*/
|
|
828
908
|
readonly coverageMode: "full" | "ui-only";
|
|
829
|
-
readonly format: OutputFormat;
|
|
830
909
|
readonly detail: DetailLevel;
|
|
831
910
|
readonly noColor: boolean;
|
|
832
911
|
readonly runCommand?: string;
|
|
@@ -948,6 +1027,24 @@ interface VitestAgentReporter {
|
|
|
948
1027
|
* awaits it and treats a rejection as a logged warning.
|
|
949
1028
|
*/
|
|
950
1029
|
readonly close?: () => Promise<void>;
|
|
1030
|
+
/**
|
|
1031
|
+
* Print one whole line of stray output: bytes a test process wrote
|
|
1032
|
+
* straight to the terminal, past Vitest's console capture (a child
|
|
1033
|
+
* process spawned with inherited stdio is the usual source). A reporter
|
|
1034
|
+
* that draws in place (a live view) implements this to print the line
|
|
1035
|
+
* above its drawing instead of under it, where the next redraw would
|
|
1036
|
+
* strand it. Return `true` when the line was taken; `false` sends it to
|
|
1037
|
+
* the terminal unchanged.
|
|
1038
|
+
*
|
|
1039
|
+
* The plugin calls it synchronously, from the factory's return until just
|
|
1040
|
+
* before {@link VitestAgentReporter.close | close}, only for lines bound
|
|
1041
|
+
* for the process's own stdout or stderr (never when Vitest was given
|
|
1042
|
+
* other streams, as MCP's `run_tests` does), and only while it owns the
|
|
1043
|
+
* console (any mode but `passthrough`). The line carries no trailing
|
|
1044
|
+
* newline. Optional; without it every line passes through unchanged. The
|
|
1045
|
+
* run's stray output is reported on `AgentReport.strayOutput` either way.
|
|
1046
|
+
*/
|
|
1047
|
+
readonly printStrayLine?: (stream: "stdout" | "stderr", line: string) => boolean;
|
|
951
1048
|
}
|
|
952
1049
|
/**
|
|
953
1050
|
* Factory that the plugin's `reporter` option accepts. The plugin calls
|
|
@@ -1011,6 +1108,11 @@ export declare const renderAgentStringForReport: (report: AgentReport) => string
|
|
|
1011
1108
|
* Returns the agent-string fallback when the matched cell has no Ink
|
|
1012
1109
|
* half.
|
|
1013
1110
|
*
|
|
1111
|
+
* The output is laid out at `options.width` when given, else at the
|
|
1112
|
+
* terminal's width (stdout's columns, else the `COLUMNS` variable), else at
|
|
1113
|
+
* 80 columns, which is what a non-TTY run gets. React and Ink are loaded on
|
|
1114
|
+
* the first call, never by importing this package.
|
|
1115
|
+
*
|
|
1014
1116
|
* @public
|
|
1015
1117
|
*/
|
|
1016
1118
|
export declare const renderHumanStringForReport: (report: AgentReport, options?: {
|
|
@@ -1052,5 +1154,5 @@ export declare const DefaultVitestAgentReporter: VitestAgentReporterFactory;
|
|
|
1052
1154
|
*/
|
|
1053
1155
|
export declare const CURRENT_REPORTER_VERSION: string;
|
|
1054
1156
|
//#endregion
|
|
1055
|
-
export type { AgentReport, CellOptions, ConsoleMode, DetailLevel, DispatchInputs, Environment, Executor, FileCoverageReport,
|
|
1157
|
+
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 };
|
|
1056
1158
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForRe
|
|
|
8
8
|
*
|
|
9
9
|
* @public
|
|
10
10
|
*/
|
|
11
|
-
const CURRENT_REPORTER_VERSION = "
|
|
11
|
+
const CURRENT_REPORTER_VERSION = "5.0.0";
|
|
12
12
|
|
|
13
13
|
//#endregion
|
|
14
14
|
export { CURRENT_REPORTER_VERSION, DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForReport, renderHumanStringForReport, resolveCellOptions };
|
package/liveView.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { CliEnv, Doc } from "@effected/cli";
|
|
2
|
-
import { CliUi } from "@effected/cli/ui";
|
|
3
2
|
import { initialRenderState } from "@vitest-agent/sdk";
|
|
4
|
-
import { SPINNER_FRAME_MS,
|
|
5
|
-
import { Cause, Deferred, Effect, Exit, Layer, PubSub, Scope } from "effect";
|
|
6
|
-
import { createElement } from "react";
|
|
3
|
+
import { SPINNER_FRAME_MS, reduceRenderState, renderAgent } from "@vitest-agent/ui";
|
|
7
4
|
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
5
|
+
import { CliUi } from "@effected/cli/ui";
|
|
6
|
+
import { Cause, Deferred, Effect, Exit, Layer, PubSub, Scope } from "effect";
|
|
8
7
|
|
|
9
8
|
//#region src/liveView.ts
|
|
10
9
|
/**
|
|
11
10
|
* The `stream` console mode's live view: the plugin's run-event channel
|
|
12
|
-
* folded through `reduceRenderState` and drawn as
|
|
11
|
+
* folded through `reduceRenderState` and drawn as `StreamApp` by the
|
|
13
12
|
* kit's `CliUi.live`.
|
|
14
13
|
*
|
|
14
|
+
* The drawing lives in `streamView.ts`, loaded through `CliUi.lazyView`, so
|
|
15
|
+
* this module (and everything that imports it) loads neither React nor Ink:
|
|
16
|
+
* the view module loads only when a run first mounts its Ink frame, and an
|
|
17
|
+
* agent, CI or piped run, which prints the `final` document instead, never
|
|
18
|
+
* loads it.
|
|
19
|
+
*
|
|
15
20
|
* The kit owns the mount: a run mounts at `RunStarted` (or where `begins`
|
|
16
21
|
* says), redraws on every event and on an 80 ms tick, and commits its final
|
|
17
22
|
* frame to scrollback at the terminal event (`RunFinished` /
|
|
@@ -45,11 +50,7 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
|
45
50
|
const liveViewOptions = {
|
|
46
51
|
initial: initialRenderState,
|
|
47
52
|
reduce: reduceRenderState,
|
|
48
|
-
render: (
|
|
49
|
-
state,
|
|
50
|
-
frameIndex: frame,
|
|
51
|
-
nowMs: frame * SPINNER_FRAME_MS
|
|
52
|
-
}),
|
|
53
|
+
render: CliUi.lazyView(() => import("./streamView.js")),
|
|
53
54
|
isStart: (event) => event._tag === "RunStarted",
|
|
54
55
|
isTerminal: (event) => event._tag === "RunFinished" || event._tag === "RunTimedOut",
|
|
55
56
|
begins: (event, before, after) => event._tag === "RunStarted" || before.phase === "idle" && after.phase !== "idle",
|
|
@@ -60,13 +61,27 @@ const liveViewOptions = {
|
|
|
60
61
|
/**
|
|
61
62
|
* The environment a Vitest-hosted live view reads: the kit's `CliEnv` over
|
|
62
63
|
* Node's stdio and terminal. It builds `CliTheme` (colour from the terminal,
|
|
63
|
-
* glyphs `auto`, so `TERM=dumb` draws ASCII) and
|
|
64
|
+
* glyphs `auto`, so `TERM=dumb` draws ASCII) and `TerminalEnv` (the width
|
|
65
|
+
* the report-time human render lays out at), and sets `CliInteractive` (a
|
|
64
66
|
* human audience with a TTY on stdin and stdout).
|
|
65
67
|
*
|
|
66
68
|
* @internal
|
|
67
69
|
*/
|
|
68
70
|
const LiveViewEnv = CliEnv.layer().pipe(Layer.provide(NodeServices.layer));
|
|
69
71
|
/**
|
|
72
|
+
* Print one line of stray output (bytes a test process wrote straight to the
|
|
73
|
+
* terminal) through the view's `logConsole`: above the frame while a run is
|
|
74
|
+
* drawn, straight to the stream otherwise, so it never lands under the frame
|
|
75
|
+
* where the next redraw would strand it.
|
|
76
|
+
*
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
const printAbove = (handle) => (stream, line) => {
|
|
80
|
+
if (stream === "stderr") handle.logConsole.error(line);
|
|
81
|
+
else handle.logConsole.log(line);
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
70
85
|
* Start a live view over `channel`. The subscription is taken before this
|
|
71
86
|
* returns, so an event published right after is seen.
|
|
72
87
|
*
|
|
@@ -86,9 +101,16 @@ const startLiveView = (channel, env = LiveViewEnv) => {
|
|
|
86
101
|
}).pipe(Effect.onExit((exit) => Deferred.done(mounted, exit)), Effect.flatMap((handle) => handle.done), Scope.provide(scope), Effect.provide(env));
|
|
87
102
|
Effect.runFork(program);
|
|
88
103
|
let closing;
|
|
104
|
+
let print;
|
|
105
|
+
const handle = Effect.runPromise(Deferred.await(mounted));
|
|
106
|
+
handle.then((mountedHandle) => {
|
|
107
|
+
if (closing === void 0) print = printAbove(mountedHandle);
|
|
108
|
+
}, () => void 0);
|
|
89
109
|
return {
|
|
90
|
-
handle
|
|
110
|
+
handle,
|
|
111
|
+
printStrayLine: (stream, line) => print?.(stream, line) ?? false,
|
|
91
112
|
close: () => {
|
|
113
|
+
print = void 0;
|
|
92
114
|
closing ??= Effect.runPromise(Deferred.await(mounted).pipe(Effect.flatMap((handle) => handle.close), Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.void : Effect.logWarning("vitest-agent: the live view ended with an error", cause)), Effect.ensuring(Scope.close(scope, Exit.void))));
|
|
93
115
|
return closing;
|
|
94
116
|
}
|
|
@@ -96,4 +118,4 @@ const startLiveView = (channel, env = LiveViewEnv) => {
|
|
|
96
118
|
};
|
|
97
119
|
|
|
98
120
|
//#endregion
|
|
99
|
-
export { LiveViewEnv, liveViewOptions, startLiveView };
|
|
121
|
+
export { LiveViewEnv, liveViewOptions, printAbove, startLiveView };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/reporter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Named VitestAgentReporterFactory implementations for the vitest-agent ecosystem.",
|
|
6
6
|
"keywords": [
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@effect/platform-node": "^4.0.
|
|
43
|
-
"@vitest-agent/sdk": "
|
|
44
|
-
"@vitest-agent/ui": "
|
|
45
|
-
"effect": "^4.0.
|
|
46
|
-
"ink": "^
|
|
42
|
+
"@effect/platform-node": "^4.0.2",
|
|
43
|
+
"@vitest-agent/sdk": "7.0.0",
|
|
44
|
+
"@vitest-agent/ui": "5.0.0",
|
|
45
|
+
"effect": "^4.0.2",
|
|
46
|
+
"ink": "^8.0.0",
|
|
47
47
|
"react": "^19.3.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@effected/cli": "^0.
|
|
50
|
+
"@effected/cli": "^0.15.0",
|
|
51
51
|
"@effected/env": "^0.1.0",
|
|
52
52
|
"@effected/glob": "^0.10.0",
|
|
53
53
|
"@effected/walker": "^0.15.0",
|
package/streamView.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SPINNER_FRAME_MS } from "@vitest-agent/ui";
|
|
2
|
+
import { StreamApp } from "@vitest-agent/ui/ink";
|
|
3
|
+
import { createElement } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/streamView.ts
|
|
6
|
+
const streamView = (state, frame) => createElement(StreamApp, {
|
|
7
|
+
state,
|
|
8
|
+
frameIndex: frame,
|
|
9
|
+
nowMs: frame * SPINNER_FRAME_MS
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { streamView as default };
|