@vitest-agent/plugin 1.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/LICENSE +21 -0
- package/README.md +50 -0
- package/index.d.ts +1216 -0
- package/index.js +31 -0
- package/layers/ConfigValidationLive.js +148 -0
- package/layers/ConfigValidationTest.js +16 -0
- package/layers/CoverageAnalyzerLive.js +138 -0
- package/layers/CoverageAnalyzerTest.js +15 -0
- package/layers/ReporterLive.js +21 -0
- package/package.json +65 -0
- package/plugin.js +329 -0
- package/reporter.js +1349 -0
- package/services/ConfigValidation.js +11 -0
- package/services/CoverageAnalyzer.js +11 -0
- package/tsdoc-metadata.json +11 -0
- package/utils/build-module-info.js +62 -0
- package/utils/build-reporter-kit.js +49 -0
- package/utils/capture-env.js +23 -0
- package/utils/capture-settings.js +54 -0
- package/utils/classify-helpers.js +72 -0
- package/utils/discover-projects.js +68 -0
- package/utils/discover-strategy.js +158 -0
- package/utils/find-test-files.js +94 -0
- package/utils/inject-tags.js +94 -0
- package/utils/process-failure.js +89 -0
- package/utils/resolve-thresholds.js +65 -0
- package/utils/route-rendered-output.js +45 -0
- package/utils/stringify-failure-value.js +36 -0
- package/utils/strip-console-reporters.js +45 -0
- package/utils/tag.js +42 -0
- package/utils/to-posix-path.js +35 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,1216 @@
|
|
|
1
|
+
import { TestTagDefinition } from "@vitest/runner";
|
|
2
|
+
import { AgentPluginOptions, AgentReporterOptions, ConsoleMode, CoverageBaselines, CoverageInput, CoverageLevel, CoverageLevelName, CoverageLevelName as CoverageLevelName$1, CoverageReport, EnvironmentDetector, OutputFormat, ResolvedReporterConfig, ResolvedThresholds, RunEvent, SettingsInput, StackFrameInput, Transport, VitestAgentReporterFactory, resolveCoverageInput, validateCoverageConfig } from "@vitest-agent/sdk";
|
|
3
|
+
import { Context, Effect, Layer, LogLevel, Option } from "effect";
|
|
4
|
+
import { TestProjectInlineConfiguration } from "vitest/config";
|
|
5
|
+
import { ResolvedConfig, VitestPluginContext } from "vitest/node";
|
|
6
|
+
import { SourceMap } from "magic-string";
|
|
7
|
+
import * as NodeContext from "@effect/platform-node/NodeContext";
|
|
8
|
+
import * as SqliteMigrator from "@effect/sql-sqlite-node/SqliteMigrator";
|
|
9
|
+
|
|
10
|
+
//#region src/utils/tag.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* Options for a `Tag`, mirroring `TestTagDefinition` minus the `name` field.
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
type TagOptions = Omit<TestTagDefinition, "name">;
|
|
16
|
+
/**
|
|
17
|
+
* A validated Vitest tag with its `name` string and a `TestTagDefinition` for registration.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
declare class Tag {
|
|
21
|
+
/** The tag name string (validated on construction). */
|
|
22
|
+
readonly name: string;
|
|
23
|
+
/** The full `TestTagDefinition` object to pass to Vitest's `test.tags` config. */
|
|
24
|
+
readonly definition: TestTagDefinition;
|
|
25
|
+
private constructor();
|
|
26
|
+
/**
|
|
27
|
+
* Create a validated `Tag`.
|
|
28
|
+
* @param name - Tag identifier; must not be empty, reserved, or contain forbidden characters
|
|
29
|
+
* @param options - Optional timeout, retry, and other Vitest tag settings
|
|
30
|
+
* @returns A new `Tag` instance
|
|
31
|
+
*/
|
|
32
|
+
static make(name: string, options?: TagOptions): Tag;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/utils/discover-strategy.d.ts
|
|
36
|
+
/**
|
|
37
|
+
* Resolved metadata about a discovered test module.
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
interface ModuleInfo {
|
|
41
|
+
/** Absolute path to the module file. */
|
|
42
|
+
readonly path: string;
|
|
43
|
+
/** Path relative to the workspace root, using forward slashes. */
|
|
44
|
+
readonly relativePath: string;
|
|
45
|
+
/** Basename of the file (e.g. `"foo.test.ts"`). */
|
|
46
|
+
readonly filename: string;
|
|
47
|
+
/** The `name` field from the nearest `package.json`. */
|
|
48
|
+
readonly packageName: string;
|
|
49
|
+
/** Absolute path to the package directory. */
|
|
50
|
+
readonly packagePath: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parsed `package.json` fields that strategies may inspect.
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
interface PackageJson {
|
|
57
|
+
readonly name?: string;
|
|
58
|
+
readonly version?: string;
|
|
59
|
+
readonly private?: boolean;
|
|
60
|
+
readonly [key: string]: unknown;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Input passed to `DiscoverStrategy.buildProject` for each workspace package.
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
interface DiscoverInput {
|
|
67
|
+
/** Package name from `package.json`. */
|
|
68
|
+
readonly name: string;
|
|
69
|
+
/** Absolute path to the package directory. */
|
|
70
|
+
readonly path: string;
|
|
71
|
+
/** Path relative to the workspace root, using forward slashes. */
|
|
72
|
+
readonly relativePath: string;
|
|
73
|
+
/** Absolute path to the workspace root. */
|
|
74
|
+
readonly workspaceRoot: string;
|
|
75
|
+
/** Parsed `package.json` contents, when available. */
|
|
76
|
+
readonly packageJson?: PackageJson;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Context object passed to a `ClassifyFn`.
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
interface ClassifyContext {
|
|
83
|
+
/** Metadata for the module being classified. */
|
|
84
|
+
readonly module: ModuleInfo;
|
|
85
|
+
/** All tags registered on the active strategy. */
|
|
86
|
+
readonly tags: ReadonlyArray<Tag>;
|
|
87
|
+
/** Tag names returned by the previous classifier layer (empty for the base layer). */
|
|
88
|
+
readonly inherited: ReadonlyArray<string>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A function that maps a module to an array of tag names.
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
type ClassifyFn = (ctx: ClassifyContext) => ReadonlyArray<string>;
|
|
95
|
+
/**
|
|
96
|
+
* Options for `DiscoverStrategy.create`.
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
interface DiscoverStrategyCreateOptions {
|
|
100
|
+
/** Tags to register on the strategy. */
|
|
101
|
+
readonly tags: ReadonlyArray<Tag>;
|
|
102
|
+
/** Function that produces a Vitest project config for a package, or `null` to skip it. */
|
|
103
|
+
readonly buildProject: (input: DiscoverInput) => Promise<TestProjectInlineConfiguration | null>;
|
|
104
|
+
/** Function that maps a module to tag names. */
|
|
105
|
+
readonly classify: ClassifyFn;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Options for `DiscoverStrategy.extend`.
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
interface DiscoverStrategyExtendOptions {
|
|
112
|
+
/** Extra tags to append to the strategy's tag list. */
|
|
113
|
+
readonly additionalTags?: ReadonlyArray<Tag>;
|
|
114
|
+
/** Override or supplement the project-building logic. Receives the inherited result as a second argument. */
|
|
115
|
+
readonly buildProject?: (input: DiscoverInput, inherited: TestProjectInlineConfiguration | null) => Promise<TestProjectInlineConfiguration | null>;
|
|
116
|
+
/** Override or supplement the classification logic. */
|
|
117
|
+
readonly classify?: ClassifyFn;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Abstract base for workspace discovery strategies. Implement `buildProject` and
|
|
121
|
+
* `classify` to control which packages become Vitest projects and how their test
|
|
122
|
+
* files are tagged. Use `DiscoverStrategy.create` to build a concrete instance
|
|
123
|
+
* from plain functions, or extend `DefaultDiscoverStrategy` to layer on top of
|
|
124
|
+
* the built-in unit/int/e2e heuristics.
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
declare abstract class DiscoverStrategy {
|
|
128
|
+
abstract readonly tags: ReadonlyArray<Tag>;
|
|
129
|
+
abstract get tagDefinitions(): ReadonlyArray<TestTagDefinition>;
|
|
130
|
+
abstract buildProject(input: DiscoverInput): Promise<TestProjectInlineConfiguration | null>;
|
|
131
|
+
abstract classify(ctx: {
|
|
132
|
+
module: ModuleInfo;
|
|
133
|
+
}): ReadonlyArray<string>;
|
|
134
|
+
abstract extend(options: DiscoverStrategyExtendOptions): DiscoverStrategy;
|
|
135
|
+
static create(options: DiscoverStrategyCreateOptions): DiscoverStrategy;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The built-in `DiscoverStrategy` used by `AgentPlugin.discover` when no custom
|
|
139
|
+
* strategy is supplied. Registers `unit`, `int` (60 s timeout), and `e2e`
|
|
140
|
+
* (120 s timeout, retry in CI) tags and classifies test files by filename suffix
|
|
141
|
+
* (`.int.test.*` → `"int"`, `.e2e.test.*` → `"e2e"`, everything else → `"unit"`).
|
|
142
|
+
* @public
|
|
143
|
+
*/
|
|
144
|
+
declare class DefaultDiscoverStrategy extends DiscoverStrategy {
|
|
145
|
+
readonly tags: ReadonlyArray<Tag>;
|
|
146
|
+
get tagDefinitions(): ReadonlyArray<TestTagDefinition>;
|
|
147
|
+
classify(ctx: {
|
|
148
|
+
module: ModuleInfo;
|
|
149
|
+
}): ReadonlyArray<string>;
|
|
150
|
+
buildProject(input: DiscoverInput): Promise<TestProjectInlineConfiguration | null>;
|
|
151
|
+
extend(options: DiscoverStrategyExtendOptions): DiscoverStrategy;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/utils/inject-tags.d.ts
|
|
155
|
+
/**
|
|
156
|
+
* Return value of the Vite `transform` hook — the rewritten source code and its source map.
|
|
157
|
+
* @public
|
|
158
|
+
*/
|
|
159
|
+
interface InjectTagsResult {
|
|
160
|
+
/** The transformed source code string with injected `tags` arguments. */
|
|
161
|
+
code: string;
|
|
162
|
+
/** Source map correlating transformed positions back to the original source. */
|
|
163
|
+
map: SourceMap;
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/plugin.d.ts
|
|
167
|
+
/**
|
|
168
|
+
* Plugin options shape with the (function-typed) `reporter` factory added
|
|
169
|
+
* on top of the schema-defined `AgentPluginOptions`. Schema can't
|
|
170
|
+
* easily encode functions, so the factory lives outside the published
|
|
171
|
+
* Effect Schema.
|
|
172
|
+
* @public
|
|
173
|
+
*/
|
|
174
|
+
interface AgentPluginConstructorOptions extends AgentPluginOptions {
|
|
175
|
+
/**
|
|
176
|
+
* Factory that builds the reporter(s) the plugin will dispatch to.
|
|
177
|
+
* Defaults to the built-in `defaultReporter` from this package.
|
|
178
|
+
*
|
|
179
|
+
* Returning an array of reporters is supported: each is called once per
|
|
180
|
+
* run and their `RenderedOutput[]` results are concatenated and routed.
|
|
181
|
+
*
|
|
182
|
+
* Pass a factory function to swap out the default rendering pipeline:
|
|
183
|
+
* ```ts
|
|
184
|
+
* agentPlugin({ reporter: () => myReporter })
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
reporter?: VitestAgentReporterFactory;
|
|
188
|
+
/**
|
|
189
|
+
* Optional live event tap. The plugin forwards every per-test and
|
|
190
|
+
* per-module `RunEvent` to this callback as the test run
|
|
191
|
+
* progresses. Hosts drive a live renderer (Ink, debug logging) from
|
|
192
|
+
* here. Throwing taps are caught and logged to stderr.
|
|
193
|
+
*/
|
|
194
|
+
onRunEvent?: (event: RunEvent) => void;
|
|
195
|
+
/**
|
|
196
|
+
* Controls the Vite transform hook that rewrites test() and it() call
|
|
197
|
+
* options to inject filename-derived tags. Pass a DiscoverStrategy
|
|
198
|
+
* instance to customize classification, or false to disable the
|
|
199
|
+
* transform entirely.
|
|
200
|
+
*
|
|
201
|
+
* Defaults to a fresh DefaultDiscoverStrategy (unit / int / e2e by
|
|
202
|
+
* filename suffix).
|
|
203
|
+
*/
|
|
204
|
+
discoverStrategy?: DiscoverStrategy | false;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The version of this package, inlined at build time from
|
|
208
|
+
* `package.json#version` via rslib-builder's `__PACKAGE_VERSION__` substitution.
|
|
209
|
+
* Re-exported from the package barrel as the public symbol; defined here so
|
|
210
|
+
* the drift-check code path can read it without a circular import.
|
|
211
|
+
*
|
|
212
|
+
* @public
|
|
213
|
+
*/
|
|
214
|
+
declare const CURRENT_PLUGIN_VERSION: string;
|
|
215
|
+
/**
|
|
216
|
+
* Vitest plugin that injects `AgentReporter` into the reporter chain.
|
|
217
|
+
*
|
|
218
|
+
* @param options - Plugin configuration options
|
|
219
|
+
* @param _layer - Internal: override the EnvironmentDetector layer (for testing)
|
|
220
|
+
* @returns Vitest plugin object with `configureVitest` hook
|
|
221
|
+
*
|
|
222
|
+
* @public
|
|
223
|
+
*/
|
|
224
|
+
declare function AgentPlugin(options?: AgentPluginConstructorOptions, _layer?: Layer.Layer<EnvironmentDetector>): {
|
|
225
|
+
name: "vitest-agent";
|
|
226
|
+
configureVitest(ctx: VitestPluginContext): Promise<void>;
|
|
227
|
+
transform?: (code: string, id: string) => InjectTagsResult | null;
|
|
228
|
+
};
|
|
229
|
+
/**
|
|
230
|
+
* Dual-output preset shape returned by `AgentPlugin.COVERAGE_LEVELS` and
|
|
231
|
+
* `AgentPlugin.COVERAGE_LEVELS_PER_FILE`. The `thresholds` half is
|
|
232
|
+
* passed to Vitest's native `coverage.thresholds`; the `coverageTargets`
|
|
233
|
+
* half is passed to `AgentPlugin({ coverageTargets })`.
|
|
234
|
+
*
|
|
235
|
+
* `thresholds` carries the optional `perFile` flag; `coverageTargets`
|
|
236
|
+
* does not — it inherits `perFile` from `coverage.thresholds.perFile`.
|
|
237
|
+
* @public
|
|
238
|
+
*/
|
|
239
|
+
interface CoverageLevelPreset {
|
|
240
|
+
readonly thresholds: {
|
|
241
|
+
readonly lines: number;
|
|
242
|
+
readonly functions: number;
|
|
243
|
+
readonly branches: number;
|
|
244
|
+
readonly statements: number;
|
|
245
|
+
readonly perFile?: boolean;
|
|
246
|
+
};
|
|
247
|
+
readonly coverageTargets: {
|
|
248
|
+
readonly lines: number;
|
|
249
|
+
readonly functions: number;
|
|
250
|
+
readonly branches: number;
|
|
251
|
+
readonly statements: number;
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* An entry added via {@link DiscoverBuilder.addProject}.
|
|
256
|
+
* @public
|
|
257
|
+
*/
|
|
258
|
+
interface AddProjectInput {
|
|
259
|
+
readonly name: string;
|
|
260
|
+
readonly path: string;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* The resolved result of a {@link DiscoverBuilder} — a drop-in replacement for
|
|
264
|
+
* the old `DiscoverProjectsResult` on the public surface.
|
|
265
|
+
* @public
|
|
266
|
+
*/
|
|
267
|
+
interface DiscoverResult {
|
|
268
|
+
readonly projects: TestProjectInlineConfiguration[] | undefined;
|
|
269
|
+
readonly tags: TestTagDefinition[];
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Thenable builder returned by `AgentPlugin.discover`. Calling `.then()`
|
|
273
|
+
* (or `await`-ing) materializes the discovery: workspace packages + added
|
|
274
|
+
* entries are each run through the active strategy's `buildProject`, with
|
|
275
|
+
* conflict detection on `name` or normalized path collisions.
|
|
276
|
+
*
|
|
277
|
+
* The builder is **immutable** — each `.addProject()` call returns a new
|
|
278
|
+
* builder; the original is unchanged.
|
|
279
|
+
*
|
|
280
|
+
* @public
|
|
281
|
+
*/
|
|
282
|
+
interface DiscoverBuilder extends PromiseLike<DiscoverResult> {
|
|
283
|
+
addProject(input: AddProjectInput): DiscoverBuilder;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Static namespace attached to the `AgentPlugin` factory function.
|
|
287
|
+
* Exposes coverage-level preset maps, auto-update tolerance functions, and the `discover` thenable builder.
|
|
288
|
+
* @public
|
|
289
|
+
*/
|
|
290
|
+
declare namespace AgentPlugin {
|
|
291
|
+
const COVERAGE_LEVELS: Readonly<Record<CoverageLevelName$1, CoverageLevelPreset>>;
|
|
292
|
+
const COVERAGE_LEVELS_PER_FILE: Readonly<Record<CoverageLevelName$1, CoverageLevelPreset>>;
|
|
293
|
+
/**
|
|
294
|
+
* Tolerance functions for Vitest's `coverage.thresholds.autoUpdate` field.
|
|
295
|
+
*
|
|
296
|
+
* Vitest's contract: `autoUpdate?: boolean | ((newThreshold: number) => number)`.
|
|
297
|
+
* Pass one of these functions directly. `standard` floors; `strict` ceils;
|
|
298
|
+
* `lenient` floors and subtracts 2 (clamped to 0) to leave a slack buffer.
|
|
299
|
+
*
|
|
300
|
+
* ```ts
|
|
301
|
+
* defineConfig({
|
|
302
|
+
* test: { coverage: { thresholds: {
|
|
303
|
+
* autoUpdate: AgentPlugin.COVERAGE_AUTOUPDATE.standard,
|
|
304
|
+
* lines: 80,
|
|
305
|
+
* } } },
|
|
306
|
+
* });
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
const COVERAGE_AUTOUPDATE: Readonly<{
|
|
310
|
+
standard: (n: number) => number;
|
|
311
|
+
strict: (n: number) => number;
|
|
312
|
+
lenient: (n: number) => number;
|
|
313
|
+
}>;
|
|
314
|
+
/**
|
|
315
|
+
* Discover Vitest project configs and tag definitions from the workspace layout.
|
|
316
|
+
* Returns a thenable {@link DiscoverBuilder} that supports `.addProject()` for
|
|
317
|
+
* non-package folders that hold tests.
|
|
318
|
+
*
|
|
319
|
+
* Awaiting (or calling `.then`) materializes the result. Each `.addProject()`
|
|
320
|
+
* returns a new immutable builder; the original is unchanged.
|
|
321
|
+
*
|
|
322
|
+
* Process-level cache: only the no-arg, no-added-projects call path caches by
|
|
323
|
+
* workspace root. Any `.addProject()` chain or explicit strategy bypasses it.
|
|
324
|
+
*
|
|
325
|
+
* ```ts
|
|
326
|
+
* export default async () => {
|
|
327
|
+
* const { projects, tags } = await AgentPlugin.discover()
|
|
328
|
+
* .addProject({ name: "integration", path: "./test-only" });
|
|
329
|
+
* return defineConfig({
|
|
330
|
+
* plugins: [AgentPlugin()],
|
|
331
|
+
* test: { ...(projects ? { projects } : {}), tags },
|
|
332
|
+
* });
|
|
333
|
+
* };
|
|
334
|
+
* ```
|
|
335
|
+
*
|
|
336
|
+
* @param strategy - Optional strategy or options object `{ strategy?, cwd? }`.
|
|
337
|
+
* Pass a {@link DiscoverStrategy} directly, or an options object to also
|
|
338
|
+
* specify a custom workspace root via `cwd`.
|
|
339
|
+
*/
|
|
340
|
+
function discover(strategy?: DiscoverStrategy | {
|
|
341
|
+
strategy?: DiscoverStrategy;
|
|
342
|
+
cwd?: string;
|
|
343
|
+
}): DiscoverBuilder;
|
|
344
|
+
/**
|
|
345
|
+
* Run a shell command, suppressing all output unless the command fails.
|
|
346
|
+
* Designed for use in Vitest `globalSetup` files to run build steps or
|
|
347
|
+
* other preparatory scripts without polluting agent stdout.
|
|
348
|
+
*
|
|
349
|
+
* On failure the captured stderr and stdout are written to their respective
|
|
350
|
+
* streams before rethrowing, so the error is still visible to humans and
|
|
351
|
+
* surfaced in CI logs.
|
|
352
|
+
*
|
|
353
|
+
* ```ts
|
|
354
|
+
* // vitest.setup.ts
|
|
355
|
+
* import { AgentPlugin } from "@vitest-agent/plugin";
|
|
356
|
+
* export function setup() {
|
|
357
|
+
* AgentPlugin.runScript("pnpm exec turbo run build:dev --output-logs=errors-only");
|
|
358
|
+
* }
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
function runScript(command: string): void;
|
|
362
|
+
}
|
|
363
|
+
//#endregion
|
|
364
|
+
//#region src/reporter.d.ts
|
|
365
|
+
/**
|
|
366
|
+
* Constructor argument shape for {@link AgentReporter}.
|
|
367
|
+
*
|
|
368
|
+
* The reporter is constructed by `AgentPlugin` in production with a
|
|
369
|
+
* fully-resolved set of plugin-internal values; tests sometimes
|
|
370
|
+
* construct it directly with a subset, so every field is optional and
|
|
371
|
+
* the constructor applies internal defaults. Public consumers building
|
|
372
|
+
* custom reporters do not touch this — they implement
|
|
373
|
+
* `VitestAgentReporterFactory` and the plugin calls them with a
|
|
374
|
+
* resolved `ReporterKit`.
|
|
375
|
+
*
|
|
376
|
+
* Extends the schema-defined `AgentReporterOptions` (which after
|
|
377
|
+
* the 2.0 cleanup carries only `projectFilter`) plus the function-typed
|
|
378
|
+
* and plugin-resolved fields the reporter needs at construction.
|
|
379
|
+
* @public
|
|
380
|
+
*/
|
|
381
|
+
interface AgentReporterConstructorOptions extends AgentReporterOptions {
|
|
382
|
+
reporter?: VitestAgentReporterFactory;
|
|
383
|
+
/**
|
|
384
|
+
* Optional event tap. The reporter constructs a `RunEvent` for
|
|
385
|
+
* each Vitest streaming callback (`onTestRunStart`,
|
|
386
|
+
* `onTestModuleQueued`, `onTestModuleStart`, `onTestCaseResult`,
|
|
387
|
+
* `onTestModuleEnd`, `onTestRunEnd`) and invokes this callback with
|
|
388
|
+
* the event. Hosts drive a live renderer (Ink, debug logging, etc.)
|
|
389
|
+
* from here without coupling the reporter to a specific transport.
|
|
390
|
+
*
|
|
391
|
+
* Errors thrown by the callback are caught and written to stderr —
|
|
392
|
+
* a malfunctioning tap must not break persistence.
|
|
393
|
+
*/
|
|
394
|
+
onRunEvent?: (event: RunEvent) => void;
|
|
395
|
+
/**
|
|
396
|
+
* Operating mode resolved by AgentPlugin from Vitest's coverage.enabled.
|
|
397
|
+
* Defaults to "full" when AgentReporter is constructed directly (without
|
|
398
|
+
* the plugin). Phase 5 uses this to short-circuit persistence when ui-only.
|
|
399
|
+
*
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
402
|
+
coverageMode?: "full" | "ui-only";
|
|
403
|
+
consoleMode?: ConsoleMode;
|
|
404
|
+
format?: OutputFormat;
|
|
405
|
+
mcp?: boolean;
|
|
406
|
+
githubActions?: boolean;
|
|
407
|
+
transport?: Transport;
|
|
408
|
+
/**
|
|
409
|
+
* Optional `test.passWithNoTests` value the plugin captured from the
|
|
410
|
+
* resolved Vitest config. The reporter forwards it onto the
|
|
411
|
+
* `ResolvedReporterConfig` it surfaces to renderers and the MCP
|
|
412
|
+
* `run_tests` tool.
|
|
413
|
+
*/
|
|
414
|
+
passWithNoTests?: boolean;
|
|
415
|
+
coverageThresholds?: ResolvedThresholds | Record<string, unknown>;
|
|
416
|
+
coverageTargets?: ResolvedThresholds | Record<string, unknown>;
|
|
417
|
+
/**
|
|
418
|
+
* Test-only override: when set, bypasses the XDG path resolver and
|
|
419
|
+
* writes the SQLite database to `${cacheDir}/data.db`. Not a public
|
|
420
|
+
* user option — production deployments resolve the database path
|
|
421
|
+
* via the XDG / `vitest-agent.config.toml` stack inside the plugin.
|
|
422
|
+
*
|
|
423
|
+
* @internal
|
|
424
|
+
*/
|
|
425
|
+
cacheDir?: string;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Vitest Reporter that produces structured output for LLM coding agents.
|
|
429
|
+
*
|
|
430
|
+
* @remarks
|
|
431
|
+
* `AgentReporter` implements three Vitest Reporter lifecycle hooks:
|
|
432
|
+
*
|
|
433
|
+
* - {@link AgentReporter.onInit | onInit} -- stores the Vitest instance
|
|
434
|
+
* for project enumeration (used in Phase 2 overview generation)
|
|
435
|
+
* - {@link AgentReporter.onCoverage | onCoverage} -- stashes the istanbul
|
|
436
|
+
* CoverageMap for merging into reports
|
|
437
|
+
* - {@link AgentReporter.onTestRunEnd | onTestRunEnd} -- groups test
|
|
438
|
+
* modules by project, builds reports, writes data to SQLite,
|
|
439
|
+
* updates the manifest, and emits console/GFM output
|
|
440
|
+
*
|
|
441
|
+
* The reporter handles both single-package repos and monorepos by grouping
|
|
442
|
+
* results via Vitest's native `TestProject` API. In single-project mode,
|
|
443
|
+
* results are written with project name "default".
|
|
444
|
+
*
|
|
445
|
+
* @privateRemarks
|
|
446
|
+
* The `onCoverage` hook fires **before** `onTestRunEnd` in Vitest's lifecycle.
|
|
447
|
+
* Coverage data must be stashed as instance state and merged during
|
|
448
|
+
* `onTestRunEnd`. This ordering is a Vitest design constraint, not a bug.
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* import { AgentReporter } from "@vitest-agent/plugin";
|
|
453
|
+
* import { defineConfig } from "vitest/config";
|
|
454
|
+
*
|
|
455
|
+
* export default defineConfig({
|
|
456
|
+
* test: {
|
|
457
|
+
* reporters: [
|
|
458
|
+
* new AgentReporter({
|
|
459
|
+
* cacheDir: ".vitest-agent",
|
|
460
|
+
* consoleOutput: "failures",
|
|
461
|
+
* coverageThresholds: { global: { lines: 80 } },
|
|
462
|
+
* }),
|
|
463
|
+
* ],
|
|
464
|
+
* },
|
|
465
|
+
* });
|
|
466
|
+
* ```
|
|
467
|
+
*
|
|
468
|
+
* @see `AgentPlugin` for the convenience plugin wrapper
|
|
469
|
+
* @see {@link https://vitest.dev/api/advanced/reporters.html | Vitest Reporter API}
|
|
470
|
+
* @public
|
|
471
|
+
*/
|
|
472
|
+
declare class AgentReporter {
|
|
473
|
+
private options;
|
|
474
|
+
private dbPath;
|
|
475
|
+
/**
|
|
476
|
+
* Set to `true` after the first `onTestRunEnd` invocation completes.
|
|
477
|
+
*
|
|
478
|
+
* Vitest can fire `onTestRunEnd` more than once per `vitest run` in
|
|
479
|
+
* some multi-project configurations (e.g., once per project group, with
|
|
480
|
+
* the same `testModules` array each time). The plugin pushes only ONE
|
|
481
|
+
* AgentReporter per run, so a fresh instance always starts with this
|
|
482
|
+
* flag `false`; the first call does the work and the flag prevents
|
|
483
|
+
* subsequent invocations from re-rendering and re-writing to the DB.
|
|
484
|
+
*/
|
|
485
|
+
private rendered;
|
|
486
|
+
/**
|
|
487
|
+
* Stored Vitest instance from {@link AgentReporter.onInit | onInit}.
|
|
488
|
+
*
|
|
489
|
+
* @remarks
|
|
490
|
+
* Available for Phase 2 overview generation. Exposed as a public
|
|
491
|
+
* property (prefixed with `_`) for testing and extension purposes.
|
|
492
|
+
*
|
|
493
|
+
* @internal
|
|
494
|
+
*/
|
|
495
|
+
_vitest: unknown;
|
|
496
|
+
private coverage;
|
|
497
|
+
private logLevel;
|
|
498
|
+
private logFile;
|
|
499
|
+
private onRunEvent;
|
|
500
|
+
/**
|
|
501
|
+
* Run-event channel. The plugin publishes one `RunEvent` per
|
|
502
|
+
* Vitest streaming callback onto this `PubSub`; the reporter factory
|
|
503
|
+
* receives it on the `ReporterKit` and a live-painting reporter
|
|
504
|
+
* (the default reporter in `stream` mode) subscribes to drive its mount.
|
|
505
|
+
* The plugin no longer owns any render mount itself.
|
|
506
|
+
*
|
|
507
|
+
* @internal
|
|
508
|
+
*/
|
|
509
|
+
private runEvents;
|
|
510
|
+
/**
|
|
511
|
+
* Reporters resolved from the factory at run start (`onInit`). Reused
|
|
512
|
+
* by `onTestRunEnd` for the `render` call. Undefined when `onInit` did
|
|
513
|
+
* not run (tests invoking `onTestRunEnd` directly) — `onTestRunEnd`
|
|
514
|
+
* then resolves the factory itself.
|
|
515
|
+
*
|
|
516
|
+
* @internal
|
|
517
|
+
*/
|
|
518
|
+
private reporters;
|
|
519
|
+
/**
|
|
520
|
+
* Whether the user supplied a custom `reporter` factory. A custom
|
|
521
|
+
* reporter may subscribe to the run-event channel in any console mode,
|
|
522
|
+
* so the streaming hooks publish events whenever this is true.
|
|
523
|
+
*
|
|
524
|
+
* @internal
|
|
525
|
+
*/
|
|
526
|
+
private readonly hasCustomReporter;
|
|
527
|
+
private currentRunId;
|
|
528
|
+
private moduleStartedAt;
|
|
529
|
+
/**
|
|
530
|
+
* Wall-clock start stamp (`Date.now()`) for an in-flight Vitest
|
|
531
|
+
* setup/teardown hook, keyed `modulePath::hookType::scopeName`.
|
|
532
|
+
* `onHookStart` writes it, `onHookEnd` reads and clears it to derive
|
|
533
|
+
* `HookFinished.durationMs` — `ReportedHookContext` carries no
|
|
534
|
+
* duration of its own.
|
|
535
|
+
*
|
|
536
|
+
* @internal
|
|
537
|
+
*/
|
|
538
|
+
private hookStartedAt;
|
|
539
|
+
constructor(options?: AgentReporterConstructorOptions);
|
|
540
|
+
/**
|
|
541
|
+
* The resolved reporter config built at construction time. Exposed for
|
|
542
|
+
* test inspection and Phase 5 short-circuit logic.
|
|
543
|
+
*
|
|
544
|
+
* This getter is @internal — do not reference in user-facing docs.
|
|
545
|
+
* @internal
|
|
546
|
+
*/
|
|
547
|
+
get resolvedConfig(): ResolvedReporterConfig;
|
|
548
|
+
/**
|
|
549
|
+
* Store the Vitest instance for project enumeration.
|
|
550
|
+
*
|
|
551
|
+
* @remarks
|
|
552
|
+
* Called once at the start of the test run. The instance is stored
|
|
553
|
+
* for Phase 2 overview generation via `vitest.projects`.
|
|
554
|
+
*
|
|
555
|
+
* @param vitest - The Vitest instance
|
|
556
|
+
*/
|
|
557
|
+
onInit(vitest: unknown): Promise<void>;
|
|
558
|
+
/**
|
|
559
|
+
* Resolve the reporter factory at run start.
|
|
560
|
+
*
|
|
561
|
+
* The factory is invoked here — before any streaming hook fires — so a
|
|
562
|
+
* reporter that paints live (the default reporter's Ink mount in `stream`
|
|
563
|
+
* mode) can subscribe to {@link AgentReporter.runEvents | the run-event
|
|
564
|
+
* channel} before the first `RunStarted` event. The kit built here
|
|
565
|
+
* carries neutral run health; `detail` is resolved health-aware again
|
|
566
|
+
* for the render kit at `onTestRunEnd`, and that kit is the one handed
|
|
567
|
+
* to `render`.
|
|
568
|
+
*
|
|
569
|
+
* A resolution failure degrades silently: `this.reporters` stays unset
|
|
570
|
+
* and `onTestRunEnd` resolves the factory itself from the render kit.
|
|
571
|
+
*
|
|
572
|
+
* @internal
|
|
573
|
+
*/
|
|
574
|
+
private initReporters;
|
|
575
|
+
/**
|
|
576
|
+
* Should the streaming Vitest hooks publish `RunEvent`s? The
|
|
577
|
+
* hooks short-circuit when this is false to skip constructing event
|
|
578
|
+
* objects nothing will read: the default reporter consumes the channel
|
|
579
|
+
* only in `stream` mode, a custom reporter may consume it in any mode,
|
|
580
|
+
* and the user `onRunEvent` tap is fed off the same stream.
|
|
581
|
+
*
|
|
582
|
+
* @internal
|
|
583
|
+
*/
|
|
584
|
+
private wantsRunEvents;
|
|
585
|
+
/**
|
|
586
|
+
* Publish a `RunEvent` onto the run-event channel and the
|
|
587
|
+
* user-supplied tap. A throwing publish or tap is caught and logged to
|
|
588
|
+
* stderr — a live-render bug must not break persistence.
|
|
589
|
+
*
|
|
590
|
+
* @internal
|
|
591
|
+
*/
|
|
592
|
+
private emit;
|
|
593
|
+
/**
|
|
594
|
+
* Walk the suite parent chain to produce the suite-path array used
|
|
595
|
+
* in `RunEvent.TestStarted` / `RunEvent.TestFinished`.
|
|
596
|
+
*
|
|
597
|
+
* @internal
|
|
598
|
+
*/
|
|
599
|
+
private collectSuitePath;
|
|
600
|
+
/**
|
|
601
|
+
* Vitest streaming hook: a test run is about to start. Mints a
|
|
602
|
+
* synthetic `runId` for the duration of this run and emits a
|
|
603
|
+
* `RunStarted` event for live subscribers.
|
|
604
|
+
*/
|
|
605
|
+
onTestRunStart(_specifications: ReadonlyArray<unknown>): void;
|
|
606
|
+
/**
|
|
607
|
+
* Read the owning Vitest project name off a `TestModule`. Vitest 4.x
|
|
608
|
+
* attaches `project` to every module; an empty name (the unnamed
|
|
609
|
+
* default project) collapses to `undefined` so the renderer treats it
|
|
610
|
+
* as a single anonymous project.
|
|
611
|
+
*
|
|
612
|
+
* @internal
|
|
613
|
+
*/
|
|
614
|
+
private moduleProjectName;
|
|
615
|
+
/**
|
|
616
|
+
* Vitest streaming hook: a test module has been queued.
|
|
617
|
+
*/
|
|
618
|
+
onTestModuleQueued(testModule: {
|
|
619
|
+
relativeModuleId: string;
|
|
620
|
+
project?: {
|
|
621
|
+
name?: string;
|
|
622
|
+
};
|
|
623
|
+
}): void;
|
|
624
|
+
/**
|
|
625
|
+
* Vitest streaming hook: a test module is starting execution.
|
|
626
|
+
*/
|
|
627
|
+
onTestModuleStart(testModule: {
|
|
628
|
+
relativeModuleId: string;
|
|
629
|
+
project?: {
|
|
630
|
+
name?: string;
|
|
631
|
+
};
|
|
632
|
+
}): void;
|
|
633
|
+
/**
|
|
634
|
+
* Vitest streaming hook: a test module has finished collecting its
|
|
635
|
+
* tests and suites, before execution begins.
|
|
636
|
+
*/
|
|
637
|
+
onTestModuleCollected(testModule: {
|
|
638
|
+
relativeModuleId: string;
|
|
639
|
+
children: {
|
|
640
|
+
allTests(filter?: string): Iterable<unknown>;
|
|
641
|
+
allSuites(): Iterable<unknown>;
|
|
642
|
+
};
|
|
643
|
+
}): void;
|
|
644
|
+
/**
|
|
645
|
+
* Vitest streaming hook: a test case is about to run.
|
|
646
|
+
*
|
|
647
|
+
* Emits a standalone `TestStarted` so the renderer gets a render
|
|
648
|
+
* frame for the transient "running" state. `onTestCaseReady` fires
|
|
649
|
+
* before `onTestCaseResult`; emitting here — rather than synthesizing
|
|
650
|
+
* `TestStarted` back-to-back with `TestFinished` inside
|
|
651
|
+
* `onTestCaseResult` — gives the running state a real lifetime in the
|
|
652
|
+
* event stream and is the prerequisite for any future per-test
|
|
653
|
+
* animation.
|
|
654
|
+
*/
|
|
655
|
+
onTestCaseReady(testCase: {
|
|
656
|
+
name: string;
|
|
657
|
+
parent?: {
|
|
658
|
+
type: string;
|
|
659
|
+
name: string;
|
|
660
|
+
parent?: unknown;
|
|
661
|
+
};
|
|
662
|
+
module?: {
|
|
663
|
+
relativeModuleId: string;
|
|
664
|
+
};
|
|
665
|
+
}): void;
|
|
666
|
+
/**
|
|
667
|
+
* Vitest streaming hook: a test case has produced a result.
|
|
668
|
+
*
|
|
669
|
+
* Emits only `TestFinished` — the matching `TestStarted` is emitted
|
|
670
|
+
* by `onTestCaseReady`, which fires earlier in the lifecycle.
|
|
671
|
+
*/
|
|
672
|
+
onTestCaseResult(testCase: {
|
|
673
|
+
name: string;
|
|
674
|
+
parent?: {
|
|
675
|
+
type: string;
|
|
676
|
+
name: string;
|
|
677
|
+
parent?: unknown;
|
|
678
|
+
};
|
|
679
|
+
module?: {
|
|
680
|
+
relativeModuleId: string;
|
|
681
|
+
};
|
|
682
|
+
result(): {
|
|
683
|
+
state: string;
|
|
684
|
+
errors?: ReadonlyArray<{
|
|
685
|
+
message: string;
|
|
686
|
+
diff?: string;
|
|
687
|
+
expected?: unknown;
|
|
688
|
+
actual?: unknown;
|
|
689
|
+
stacks?: ReadonlyArray<unknown>;
|
|
690
|
+
}>;
|
|
691
|
+
} | undefined;
|
|
692
|
+
diagnostic(): {
|
|
693
|
+
duration: number;
|
|
694
|
+
} | undefined;
|
|
695
|
+
}): void;
|
|
696
|
+
/**
|
|
697
|
+
* Vitest streaming hook: a test module has finished. Emits a
|
|
698
|
+
* `ModuleFinished` carrying the tallied counts plus a duration
|
|
699
|
+
* derived from the module's diagnostic.
|
|
700
|
+
*/
|
|
701
|
+
onTestModuleEnd(testModule: {
|
|
702
|
+
relativeModuleId: string;
|
|
703
|
+
project?: {
|
|
704
|
+
name?: string;
|
|
705
|
+
};
|
|
706
|
+
children: {
|
|
707
|
+
allTests(filter?: string): Iterable<{
|
|
708
|
+
result(): {
|
|
709
|
+
state: string;
|
|
710
|
+
errors?: ReadonlyArray<{
|
|
711
|
+
message: string;
|
|
712
|
+
name?: string;
|
|
713
|
+
}>;
|
|
714
|
+
} | undefined;
|
|
715
|
+
tags?: ReadonlyArray<string>;
|
|
716
|
+
}>;
|
|
717
|
+
};
|
|
718
|
+
diagnostic(): {
|
|
719
|
+
duration: number;
|
|
720
|
+
} | undefined;
|
|
721
|
+
}): void;
|
|
722
|
+
/**
|
|
723
|
+
* Walk a suite/test entity's parent chain to its owning module path.
|
|
724
|
+
*
|
|
725
|
+
* @internal
|
|
726
|
+
*/
|
|
727
|
+
private entityModulePath;
|
|
728
|
+
/**
|
|
729
|
+
* Vitest streaming hook: a test suite is about to run.
|
|
730
|
+
*/
|
|
731
|
+
onTestSuiteReady(testSuite: {
|
|
732
|
+
name: string;
|
|
733
|
+
module?: {
|
|
734
|
+
relativeModuleId?: string;
|
|
735
|
+
};
|
|
736
|
+
parent?: {
|
|
737
|
+
type: string;
|
|
738
|
+
name: string;
|
|
739
|
+
parent?: unknown;
|
|
740
|
+
};
|
|
741
|
+
}): void;
|
|
742
|
+
/**
|
|
743
|
+
* Vitest streaming hook: a test suite has finished running.
|
|
744
|
+
*/
|
|
745
|
+
onTestSuiteResult(testSuite: {
|
|
746
|
+
name: string;
|
|
747
|
+
module?: {
|
|
748
|
+
relativeModuleId?: string;
|
|
749
|
+
};
|
|
750
|
+
parent?: {
|
|
751
|
+
type: string;
|
|
752
|
+
name: string;
|
|
753
|
+
parent?: unknown;
|
|
754
|
+
};
|
|
755
|
+
children: {
|
|
756
|
+
allTests(filter?: string): Iterable<{
|
|
757
|
+
result(): {
|
|
758
|
+
state: string;
|
|
759
|
+
} | undefined;
|
|
760
|
+
}>;
|
|
761
|
+
};
|
|
762
|
+
}): void;
|
|
763
|
+
/**
|
|
764
|
+
* Derive `modulePath` / `scopeName` / a stable key for a Vitest
|
|
765
|
+
* setup/teardown hook from its `ReportedHookContext` entity.
|
|
766
|
+
*
|
|
767
|
+
* @internal
|
|
768
|
+
*/
|
|
769
|
+
private hookScope;
|
|
770
|
+
/**
|
|
771
|
+
* Vitest streaming hook: a `beforeAll` / `afterAll` / `beforeEach` /
|
|
772
|
+
* `afterEach` hook is starting.
|
|
773
|
+
*/
|
|
774
|
+
onHookStart(hook: {
|
|
775
|
+
name: string;
|
|
776
|
+
entity?: {
|
|
777
|
+
name?: string;
|
|
778
|
+
relativeModuleId?: string;
|
|
779
|
+
module?: {
|
|
780
|
+
relativeModuleId?: string;
|
|
781
|
+
};
|
|
782
|
+
};
|
|
783
|
+
}): void;
|
|
784
|
+
/**
|
|
785
|
+
* Vitest streaming hook: a setup/teardown hook has finished. Duration
|
|
786
|
+
* is derived from the stamp `onHookStart` recorded — `ReportedHookContext`
|
|
787
|
+
* carries none of its own. Status is a best-effort read of the
|
|
788
|
+
* entity's collected errors.
|
|
789
|
+
*/
|
|
790
|
+
onHookEnd(hook: {
|
|
791
|
+
name: string;
|
|
792
|
+
entity?: {
|
|
793
|
+
name?: string;
|
|
794
|
+
relativeModuleId?: string;
|
|
795
|
+
module?: {
|
|
796
|
+
relativeModuleId?: string;
|
|
797
|
+
};
|
|
798
|
+
errors?: () => ReadonlyArray<{
|
|
799
|
+
message: string;
|
|
800
|
+
stack?: string;
|
|
801
|
+
diff?: string;
|
|
802
|
+
}>;
|
|
803
|
+
};
|
|
804
|
+
}): void;
|
|
805
|
+
/**
|
|
806
|
+
* Vitest streaming hook: a captured `console.log` / `console.error`
|
|
807
|
+
* from user test code. The Vitest log carries a `taskId` rather than
|
|
808
|
+
* a module/test path; resolving it back to one would need the task
|
|
809
|
+
* registry, so `modulePath` / `testName` are left unset.
|
|
810
|
+
*/
|
|
811
|
+
onUserConsoleLog(log: {
|
|
812
|
+
content: string;
|
|
813
|
+
type: "stdout" | "stderr";
|
|
814
|
+
time: number;
|
|
815
|
+
}): void;
|
|
816
|
+
/**
|
|
817
|
+
* Vitest streaming hook: the run exceeded its configured process
|
|
818
|
+
* timeout. Terminal — moves the renderer to a final frame.
|
|
819
|
+
*/
|
|
820
|
+
onProcessTimeout(): void;
|
|
821
|
+
/**
|
|
822
|
+
* Vitest streaming hook: a test case recorded an annotation.
|
|
823
|
+
*/
|
|
824
|
+
onTestCaseAnnotate(testCase: {
|
|
825
|
+
name: string;
|
|
826
|
+
parent?: {
|
|
827
|
+
type: string;
|
|
828
|
+
name: string;
|
|
829
|
+
parent?: unknown;
|
|
830
|
+
};
|
|
831
|
+
module?: {
|
|
832
|
+
relativeModuleId: string;
|
|
833
|
+
};
|
|
834
|
+
}, annotation: {
|
|
835
|
+
message: string;
|
|
836
|
+
}): void;
|
|
837
|
+
/**
|
|
838
|
+
* Vitest streaming hook: a test case recorded an artifact.
|
|
839
|
+
*/
|
|
840
|
+
onTestCaseArtifactRecord(testCase: {
|
|
841
|
+
name: string;
|
|
842
|
+
parent?: {
|
|
843
|
+
type: string;
|
|
844
|
+
name: string;
|
|
845
|
+
parent?: unknown;
|
|
846
|
+
};
|
|
847
|
+
module?: {
|
|
848
|
+
relativeModuleId: string;
|
|
849
|
+
};
|
|
850
|
+
}, artifact: {
|
|
851
|
+
type?: string;
|
|
852
|
+
}): void;
|
|
853
|
+
/**
|
|
854
|
+
* Vitest streaming hook: watch mode has finished its initial run and
|
|
855
|
+
* is waiting for file changes.
|
|
856
|
+
*/
|
|
857
|
+
onWatcherStart(): void;
|
|
858
|
+
/**
|
|
859
|
+
* Vitest streaming hook: watch mode is re-running because tracked
|
|
860
|
+
* files changed.
|
|
861
|
+
*/
|
|
862
|
+
onWatcherRerun(files: ReadonlyArray<string>, trigger?: string): void;
|
|
863
|
+
private ensureDbPath;
|
|
864
|
+
/**
|
|
865
|
+
* Stash coverage data for merging into reports.
|
|
866
|
+
*
|
|
867
|
+
* @privateRemarks
|
|
868
|
+
* This hook fires **before** `onTestRunEnd` in Vitest's lifecycle.
|
|
869
|
+
* The coverage value is an istanbul CoverageMap that will be duck-typed
|
|
870
|
+
* and processed during `onTestRunEnd`.
|
|
871
|
+
*
|
|
872
|
+
* @param coverage - Istanbul CoverageMap (duck-typed at processing time)
|
|
873
|
+
*
|
|
874
|
+
* @see `processCoverage` for the duck-typing logic
|
|
875
|
+
*/
|
|
876
|
+
onCoverage(coverage: unknown): void;
|
|
877
|
+
/**
|
|
878
|
+
* Process test results, write reports, and emit formatted output.
|
|
879
|
+
*
|
|
880
|
+
* @remarks
|
|
881
|
+
* This is the main lifecycle hook where all output is generated.
|
|
882
|
+
* Processing steps:
|
|
883
|
+
*
|
|
884
|
+
* 1. Group test modules by `testModule.project.name`
|
|
885
|
+
* 2. Process stashed coverage data (if available)
|
|
886
|
+
* 3. Build per-project `AgentReport` objects
|
|
887
|
+
* 4. Classify tests via HistoryTracker and attach classifications
|
|
888
|
+
* 5. Write settings, run, modules, test cases, and errors to SQLite
|
|
889
|
+
* 6. Write per-test history entries
|
|
890
|
+
* 7. Write baselines and trends
|
|
891
|
+
* 8. Emit console markdown (unless `"silent"`)
|
|
892
|
+
* 9. Write GFM summary to `GITHUB_STEP_SUMMARY` (if GitHub Actions)
|
|
893
|
+
*
|
|
894
|
+
* File write failures are logged to stderr but do not crash the test run.
|
|
895
|
+
*
|
|
896
|
+
* @param testModules - All test modules from the completed run
|
|
897
|
+
* @param unhandledErrors - Any unhandled errors during the run
|
|
898
|
+
* @param reason - Overall outcome: `"passed"`, `"failed"`, or `"interrupted"`
|
|
899
|
+
*/
|
|
900
|
+
onTestRunEnd(testModules: ReadonlyArray<unknown>, unhandledErrors: ReadonlyArray<unknown>, reason: "passed" | "failed" | "interrupted"): Promise<void>;
|
|
901
|
+
}
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region src/utils/discover-projects.d.ts
|
|
904
|
+
/**
|
|
905
|
+
* The resolved output of `discoverProjects` — projects and tag definitions ready for `defineConfig`.
|
|
906
|
+
* @public
|
|
907
|
+
*/
|
|
908
|
+
interface DiscoverProjectsResult {
|
|
909
|
+
readonly projects: TestProjectInlineConfiguration[] | undefined;
|
|
910
|
+
readonly tags: TestTagDefinition[];
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Options for `discoverProjects`.
|
|
914
|
+
* @public
|
|
915
|
+
*/
|
|
916
|
+
interface DiscoverProjectsOptions {
|
|
917
|
+
readonly strategy?: DiscoverStrategy;
|
|
918
|
+
readonly cwd?: string;
|
|
919
|
+
readonly additionalEntries?: ReadonlyArray<{
|
|
920
|
+
readonly name: string;
|
|
921
|
+
readonly path: string;
|
|
922
|
+
}>;
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Scan all workspace packages and additional entries through the active strategy and return projects + tags.
|
|
926
|
+
* @param options - Optional strategy, working directory, and extra project entries
|
|
927
|
+
* @returns Resolved projects and tag definitions
|
|
928
|
+
* @public
|
|
929
|
+
*/
|
|
930
|
+
declare function discoverProjects(options?: DiscoverProjectsOptions): Promise<DiscoverProjectsResult>;
|
|
931
|
+
//#endregion
|
|
932
|
+
//#region src/utils/classify-helpers.d.ts
|
|
933
|
+
/**
|
|
934
|
+
* Creates a ClassifyFn that maps filename suffix patterns to tag arrays.
|
|
935
|
+
*
|
|
936
|
+
* Accepts two forms:
|
|
937
|
+
* - `Record<string, ReadonlyArray<string>>` — keys are exact suffix strings
|
|
938
|
+
* (e.g. ".int.test.ts"); matched via `String.prototype.endsWith` against
|
|
939
|
+
* `module.filename`.
|
|
940
|
+
* - `ReadonlyArray<readonly [RegExp, ReadonlyArray<string>]>` — each tuple is a
|
|
941
|
+
* `[pattern, tags]` pair; matched via `RegExp.test` against `module.filename`.
|
|
942
|
+
* First match wins.
|
|
943
|
+
*
|
|
944
|
+
* No match returns an empty array.
|
|
945
|
+
* @public
|
|
946
|
+
*/
|
|
947
|
+
declare function classifyByFilename(suffixMap: Record<string, ReadonlyArray<string>> | ReadonlyArray<readonly [RegExp, ReadonlyArray<string>]>): ClassifyFn;
|
|
948
|
+
/**
|
|
949
|
+
* Creates a ClassifyFn that maps directory segment paths to tag arrays.
|
|
950
|
+
*
|
|
951
|
+
* Keys are directory-segment paths (e.g. `__test__/integration`). A module
|
|
952
|
+
* matches when `module.relativePath` contains the segment with `/` boundaries.
|
|
953
|
+
* Key `"integration"` matches `"integration/foo.test.ts"` and
|
|
954
|
+
* `"src/integration/foo.test.ts"` but NOT `"my-integration-tests/foo.test.ts"`.
|
|
955
|
+
*
|
|
956
|
+
* No match returns `[]`.
|
|
957
|
+
* @public
|
|
958
|
+
*/
|
|
959
|
+
declare function classifyByDirectory(dirMap: Record<string, ReadonlyArray<string>>): ClassifyFn;
|
|
960
|
+
/**
|
|
961
|
+
* Composes multiple `ClassifyFn` values into one. Each classifier is called with
|
|
962
|
+
* the same context; results are concatenated in order and deduplicated by tag
|
|
963
|
+
* name (first occurrence wins). An empty list returns a function that always
|
|
964
|
+
* returns `[]`.
|
|
965
|
+
* @public
|
|
966
|
+
*/
|
|
967
|
+
declare function combineClassifiers(...fns: ReadonlyArray<ClassifyFn>): ClassifyFn;
|
|
968
|
+
//#endregion
|
|
969
|
+
//#region src/utils/find-test-files.d.ts
|
|
970
|
+
/**
|
|
971
|
+
* Async file walker that returns matched absolute paths.
|
|
972
|
+
*
|
|
973
|
+
* Walks `dir` recursively via `node:fs/promises`. Skips `node_modules`, `.git`,
|
|
974
|
+
* and `dist` directories. Matches files against the supplied glob patterns
|
|
975
|
+
* relative to `dir` (e.g. `"src/**\/*.test.ts"`).
|
|
976
|
+
*
|
|
977
|
+
* Returns an empty array if `dir` does not exist or no files match.
|
|
978
|
+
* @param dir - Absolute path to the directory to walk
|
|
979
|
+
* @param patterns - Glob patterns to match against (relative to `dir`)
|
|
980
|
+
* @returns Absolute paths of matched test files
|
|
981
|
+
* @public
|
|
982
|
+
*/
|
|
983
|
+
declare function findTestFiles(dir: string, patterns: ReadonlyArray<string>): Promise<ReadonlyArray<string>>;
|
|
984
|
+
//#endregion
|
|
985
|
+
//#region src/layers/ReporterLive.d.ts
|
|
986
|
+
/**
|
|
987
|
+
* Composition layer for a single `AgentReporter` run. Wires SQLite, migrations, and all service layers.
|
|
988
|
+
* @public
|
|
989
|
+
*/
|
|
990
|
+
declare const ReporterLive: (dbPath: string, logLevel?: LogLevel.LogLevel, logFile?: string) => Layer.Layer<import("@vitest-agent/sdk").DataStore | import("@effect/sql/SqlClient").SqlClient | CoverageAnalyzer | import("@vitest-agent/sdk").HistoryTracker | import("@vitest-agent/sdk").DataReader | import("@vitest-agent/sdk").DetailResolver | import("@vitest-agent/sdk").EnvironmentDetector | import("@vitest-agent/sdk").ExecutorResolver | import("@vitest-agent/sdk").FormatSelector | import("@vitest-agent/sdk").OutputRenderer | import("@effect/sql-sqlite-node/SqliteClient").SqliteClient | NodeContext.NodeContext, import("@effect/sql/SqlError").SqlError | SqliteMigrator.MigrationError | import("effect/ConfigError").ConfigError, never>;
|
|
991
|
+
//#endregion
|
|
992
|
+
//#region src/services/CoverageAnalyzer.d.ts
|
|
993
|
+
interface CoverageOptions {
|
|
994
|
+
readonly thresholds: ResolvedThresholds;
|
|
995
|
+
readonly targets?: ResolvedThresholds;
|
|
996
|
+
readonly baselines?: CoverageBaselines;
|
|
997
|
+
readonly includeBareZero: boolean;
|
|
998
|
+
}
|
|
999
|
+
declare const CoverageAnalyzer_base: Context.TagClass<CoverageAnalyzer, "vitest-agent/CoverageAnalyzer", {
|
|
1000
|
+
readonly process: (coverage: unknown, options: CoverageOptions) => Effect.Effect<Option.Option<CoverageReport>>;
|
|
1001
|
+
readonly processScoped: (coverage: unknown, options: CoverageOptions, testedFiles: ReadonlyArray<string>) => Effect.Effect<Option.Option<CoverageReport>>;
|
|
1002
|
+
}>;
|
|
1003
|
+
/**
|
|
1004
|
+
* Effect service for processing istanbul coverage maps into structured reports.
|
|
1005
|
+
* @public
|
|
1006
|
+
*/
|
|
1007
|
+
declare class CoverageAnalyzer extends CoverageAnalyzer_base {}
|
|
1008
|
+
//#endregion
|
|
1009
|
+
//#region src/layers/CoverageAnalyzerLive.d.ts
|
|
1010
|
+
/**
|
|
1011
|
+
* Live implementation of the CoverageAnalyzer service backed by istanbul.
|
|
1012
|
+
* @public
|
|
1013
|
+
*/
|
|
1014
|
+
declare const CoverageAnalyzerLive: Layer.Layer<CoverageAnalyzer>;
|
|
1015
|
+
//#endregion
|
|
1016
|
+
//#region src/layers/CoverageAnalyzerTest.d.ts
|
|
1017
|
+
/**
|
|
1018
|
+
* Test-double layer factory for CoverageAnalyzer. Pass a pre-built `CoverageReport` to inject.
|
|
1019
|
+
* @public
|
|
1020
|
+
*/
|
|
1021
|
+
declare const CoverageAnalyzerTest: {
|
|
1022
|
+
readonly layer: (data?: CoverageReport) => Layer.Layer<CoverageAnalyzer>;
|
|
1023
|
+
};
|
|
1024
|
+
//#endregion
|
|
1025
|
+
//#region src/services/ConfigValidation.d.ts
|
|
1026
|
+
/**
|
|
1027
|
+
* A validation error produced by the ConfigValidation service.
|
|
1028
|
+
* @public
|
|
1029
|
+
*/
|
|
1030
|
+
interface ValidationError {
|
|
1031
|
+
readonly code: string;
|
|
1032
|
+
readonly path?: string;
|
|
1033
|
+
readonly message: string;
|
|
1034
|
+
readonly remediation?: string;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* A validation warning produced by the ConfigValidation service.
|
|
1038
|
+
* @public
|
|
1039
|
+
*/
|
|
1040
|
+
interface ValidationWarning {
|
|
1041
|
+
readonly code: string;
|
|
1042
|
+
readonly path?: string;
|
|
1043
|
+
readonly message: string;
|
|
1044
|
+
readonly remediation?: string;
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* An informational message produced by the ConfigValidation service.
|
|
1048
|
+
* @public
|
|
1049
|
+
*/
|
|
1050
|
+
interface ValidationInfo {
|
|
1051
|
+
readonly code: string;
|
|
1052
|
+
readonly message: string;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* The aggregated result of a ConfigValidation run.
|
|
1056
|
+
* @public
|
|
1057
|
+
*/
|
|
1058
|
+
interface ValidationResult {
|
|
1059
|
+
readonly errors: ReadonlyArray<ValidationError>;
|
|
1060
|
+
readonly warnings: ReadonlyArray<ValidationWarning>;
|
|
1061
|
+
readonly info: ReadonlyArray<ValidationInfo>;
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Input consumed by `ConfigValidation.validate`.
|
|
1065
|
+
* @public
|
|
1066
|
+
*/
|
|
1067
|
+
interface ValidationInput {
|
|
1068
|
+
readonly vitestConfig: ResolvedConfig;
|
|
1069
|
+
readonly pluginOptions: AgentPluginOptions;
|
|
1070
|
+
}
|
|
1071
|
+
declare const ConfigValidation_base: Context.TagClass<ConfigValidation, "vitest-agent/ConfigValidation", {
|
|
1072
|
+
readonly validate: (input: ValidationInput) => Effect.Effect<ValidationResult, never, never>;
|
|
1073
|
+
}>;
|
|
1074
|
+
/**
|
|
1075
|
+
* Effect service for validating Vitest + plugin coverage configuration.
|
|
1076
|
+
* @public
|
|
1077
|
+
*/
|
|
1078
|
+
declare class ConfigValidation extends ConfigValidation_base {}
|
|
1079
|
+
//#endregion
|
|
1080
|
+
//#region src/layers/ConfigValidationLive.d.ts
|
|
1081
|
+
/**
|
|
1082
|
+
* Live implementation of the ConfigValidation service running the built-in rule registry.
|
|
1083
|
+
* @public
|
|
1084
|
+
*/
|
|
1085
|
+
declare const ConfigValidationLive: Layer.Layer<ConfigValidation>;
|
|
1086
|
+
//#endregion
|
|
1087
|
+
//#region src/layers/ConfigValidationTest.d.ts
|
|
1088
|
+
/**
|
|
1089
|
+
* Test-double layer factory for ConfigValidation. Pass a pre-built `ValidationResult` to inject.
|
|
1090
|
+
* @public
|
|
1091
|
+
*/
|
|
1092
|
+
declare const ConfigValidationTest: {
|
|
1093
|
+
readonly layer: (override?: ValidationResult) => Layer.Layer<ConfigValidation>;
|
|
1094
|
+
};
|
|
1095
|
+
//#endregion
|
|
1096
|
+
//#region src/utils/capture-env.d.ts
|
|
1097
|
+
/**
|
|
1098
|
+
* Capture CI and GitHub Actions environment variables for persistence.
|
|
1099
|
+
* @param env - The process environment record to read from
|
|
1100
|
+
* @returns A filtered map of relevant environment variable keys and values
|
|
1101
|
+
* @public
|
|
1102
|
+
*/
|
|
1103
|
+
declare function captureEnvVars(env: Record<string, string | undefined>): Record<string, string>;
|
|
1104
|
+
//#endregion
|
|
1105
|
+
//#region src/utils/capture-settings.d.ts
|
|
1106
|
+
/**
|
|
1107
|
+
* Extract a serializable settings snapshot from the resolved Vitest config.
|
|
1108
|
+
* @param config - The resolved Vitest config record
|
|
1109
|
+
* @param vitestVersion - The running Vitest version string
|
|
1110
|
+
* @returns A `SettingsInput` ready for persistence
|
|
1111
|
+
* @public
|
|
1112
|
+
*/
|
|
1113
|
+
declare function captureSettings(config: Record<string, unknown>, vitestVersion: string): SettingsInput;
|
|
1114
|
+
/**
|
|
1115
|
+
* Compute a stable SHA-256 hash of a settings record for change detection.
|
|
1116
|
+
* @param settings - The settings record to hash (keys are sorted for stability)
|
|
1117
|
+
* @returns A hex-encoded SHA-256 digest
|
|
1118
|
+
* @public
|
|
1119
|
+
*/
|
|
1120
|
+
declare function hashSettings(settings: Record<string, unknown>): string;
|
|
1121
|
+
//#endregion
|
|
1122
|
+
//#region src/utils/process-failure.d.ts
|
|
1123
|
+
/**
|
|
1124
|
+
* A single parsed stack frame as Vitest represents it.
|
|
1125
|
+
* @public
|
|
1126
|
+
*/
|
|
1127
|
+
interface VitestStackFrameLike {
|
|
1128
|
+
/** Absolute file path for the frame. */
|
|
1129
|
+
readonly file?: string;
|
|
1130
|
+
/** 1-based source line number. */
|
|
1131
|
+
readonly line?: number;
|
|
1132
|
+
/** 1-based source column number. */
|
|
1133
|
+
readonly column?: number;
|
|
1134
|
+
/** Function or method name at the call site. */
|
|
1135
|
+
readonly method?: string;
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* A Vitest error object as passed to reporter hooks.
|
|
1139
|
+
* @public
|
|
1140
|
+
*/
|
|
1141
|
+
interface VitestErrorLike {
|
|
1142
|
+
readonly name?: string;
|
|
1143
|
+
readonly message: string;
|
|
1144
|
+
readonly stack?: string;
|
|
1145
|
+
readonly stacks?: ReadonlyArray<VitestStackFrameLike>;
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Convert a Vitest error into structured frame inputs (with source-map and
|
|
1149
|
+
* function-boundary annotations) plus a stable failure signature.
|
|
1150
|
+
*
|
|
1151
|
+
* Returns `null` for the signature when no usable top frame is found
|
|
1152
|
+
* (error has no stack, or every frame is in framework code). Frames may
|
|
1153
|
+
* still be populated even when the signature is null.
|
|
1154
|
+
* @public
|
|
1155
|
+
*/
|
|
1156
|
+
declare const processFailure: (error: VitestErrorLike) => {
|
|
1157
|
+
frames: ReadonlyArray<StackFrameInput>;
|
|
1158
|
+
signatureHash: string | null;
|
|
1159
|
+
};
|
|
1160
|
+
//#endregion
|
|
1161
|
+
//#region src/utils/resolve-thresholds.d.ts
|
|
1162
|
+
/**
|
|
1163
|
+
* Loose record type matching Vitest's `coverage.thresholds` config input.
|
|
1164
|
+
* @public
|
|
1165
|
+
*/
|
|
1166
|
+
type VitestThresholdsInput = Record<string, unknown>;
|
|
1167
|
+
/**
|
|
1168
|
+
* Parse Vitest `coverage.thresholds` format into a normalized `ResolvedThresholds`.
|
|
1169
|
+
* @param input - The raw `coverage.thresholds` object from Vitest config
|
|
1170
|
+
* @returns Normalized thresholds with global, perFile, and pattern entries
|
|
1171
|
+
* @public
|
|
1172
|
+
*/
|
|
1173
|
+
declare function resolveThresholds(input: VitestThresholdsInput | undefined): ResolvedThresholds;
|
|
1174
|
+
//#endregion
|
|
1175
|
+
//#region src/utils/strip-console-reporters.d.ts
|
|
1176
|
+
/**
|
|
1177
|
+
* Built-in Vitest reporters that write to the console (stdout).
|
|
1178
|
+
* These are the reporters suppressed when an agent takes over console output.
|
|
1179
|
+
*
|
|
1180
|
+
* @privateRemarks
|
|
1181
|
+
* `"agent"` is the built-in Vitest reporter added in v4.1 that reduces
|
|
1182
|
+
* console noise for AI agents. We strip it because our reporter replaces
|
|
1183
|
+
* its functionality with structured markdown output.
|
|
1184
|
+
*
|
|
1185
|
+
* @see {@link https://vitest.dev/api/advanced/reporters.html | Vitest Reporter docs}
|
|
1186
|
+
* @internal
|
|
1187
|
+
*/
|
|
1188
|
+
declare const CONSOLE_REPORTERS: Set<string>;
|
|
1189
|
+
/**
|
|
1190
|
+
* Filter out built-in console reporters from a Vitest reporters array.
|
|
1191
|
+
*
|
|
1192
|
+
* Keeps custom reporters (class instances, file paths) and non-console
|
|
1193
|
+
* built-in reporters (`json`, `junit`, `html`, `blob`, `github-actions`).
|
|
1194
|
+
* Used by `AgentPlugin` in agent mode to suppress noisy console output.
|
|
1195
|
+
*
|
|
1196
|
+
* @param reporters - The Vitest `config.reporters` array
|
|
1197
|
+
* @returns Filtered array with console reporters removed
|
|
1198
|
+
*
|
|
1199
|
+
* @internal
|
|
1200
|
+
*/
|
|
1201
|
+
declare function stripConsoleReporters(reporters: unknown[]): unknown[];
|
|
1202
|
+
//#endregion
|
|
1203
|
+
//#region src/index.d.ts
|
|
1204
|
+
/** Preset map for coverage levels without per-file enforcement. Mirrors `AgentPlugin.COVERAGE_LEVELS`. @public */
|
|
1205
|
+
declare const COVERAGE_LEVELS: Readonly<Record<import("@vitest-agent/sdk").CoverageLevelName, CoverageLevelPreset>>;
|
|
1206
|
+
/** Preset map for coverage levels with per-file enforcement. Mirrors `AgentPlugin.COVERAGE_LEVELS_PER_FILE`. @public */
|
|
1207
|
+
declare const COVERAGE_LEVELS_PER_FILE: Readonly<Record<import("@vitest-agent/sdk").CoverageLevelName, CoverageLevelPreset>>;
|
|
1208
|
+
/** Auto-update tolerance functions for `coverage.thresholds.autoUpdate`. Mirrors `AgentPlugin.COVERAGE_AUTOUPDATE`. @public */
|
|
1209
|
+
declare const COVERAGE_AUTOUPDATE: Readonly<{
|
|
1210
|
+
standard: (n: number) => number;
|
|
1211
|
+
strict: (n: number) => number;
|
|
1212
|
+
lenient: (n: number) => number;
|
|
1213
|
+
}>;
|
|
1214
|
+
//#endregion
|
|
1215
|
+
export { type AddProjectInput, AgentPlugin, type AgentPluginConstructorOptions, AgentReporter, type AgentReporterConstructorOptions, CONSOLE_REPORTERS, COVERAGE_AUTOUPDATE, COVERAGE_LEVELS, COVERAGE_LEVELS_PER_FILE, CURRENT_PLUGIN_VERSION, type ClassifyContext, type ClassifyFn, ConfigValidation, ConfigValidationLive, ConfigValidationTest, CoverageAnalyzer, CoverageAnalyzerLive, CoverageAnalyzerTest, type CoverageInput, CoverageLevel, type CoverageLevelName, type CoverageLevelPreset, DefaultDiscoverStrategy, type DiscoverBuilder, type DiscoverInput, type PackageJson as DiscoverPackageJson, type DiscoverProjectsOptions, type DiscoverProjectsResult, type DiscoverResult, DiscoverStrategy, type DiscoverStrategyCreateOptions, type DiscoverStrategyExtendOptions, type InjectTagsResult, type ModuleInfo, ReporterLive, Tag, type TagOptions, type ValidationError, type ValidationInfo, type ValidationInput, type ValidationResult, type ValidationWarning, type VitestErrorLike, type VitestStackFrameLike, type VitestThresholdsInput, captureEnvVars, captureSettings, classifyByDirectory, classifyByFilename, combineClassifiers, discoverProjects, findTestFiles, hashSettings, processFailure, resolveCoverageInput, resolveThresholds, stripConsoleReporters, validateCoverageConfig };
|
|
1216
|
+
//# sourceMappingURL=index.d.ts.map
|