@vitest-agent/plugin 2.5.4 → 2.5.5
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/index.d.ts +36 -0
- package/layers/CoverageAnalyzerLive.js +1 -0
- package/package.json +6 -6
- package/plugin.js +39 -1
- package/reporter.js +83 -9
- package/utils/discover-projects.js +1 -1
- package/utils/discover-strategy.js +1 -1
- package/utils/find-test-files.js +1 -1
- package/utils/is-partial-run.js +26 -0
- package/utils/is-test-shaped-package.js +1 -1
- package/utils/process-failure.js +1 -1
- package/utils/resolve-coverage-dir-isolation.js +44 -0
- package/utils/run-script-lock.js +2 -2
package/index.d.ts
CHANGED
|
@@ -643,6 +643,35 @@ declare class AgentReporter {
|
|
|
643
643
|
* @internal
|
|
644
644
|
*/
|
|
645
645
|
private hookStartedAt;
|
|
646
|
+
/**
|
|
647
|
+
* Count of test specifications Vitest started for the current run,
|
|
648
|
+
* captured from `onTestRunStart`'s `specifications` argument. Used
|
|
649
|
+
* alongside a fresh `globTestSpecifications()` total in `onTestRunEnd`
|
|
650
|
+
* as one of `isPartialRun`'s signals (issue #160 gap 2) — a tags-only
|
|
651
|
+
* `run_tests` filter narrows the run without setting `filenamePattern`
|
|
652
|
+
* or `projectFilter`, so the spec-count comparison is the only signal
|
|
653
|
+
* that catches it. `undefined` when `onTestRunStart` never fired
|
|
654
|
+
* (tests invoking `onTestRunEnd` directly); `onTestRunEnd` falls back
|
|
655
|
+
* to the executed module count in that case.
|
|
656
|
+
*
|
|
657
|
+
* @internal
|
|
658
|
+
*/
|
|
659
|
+
private startedSpecCount;
|
|
660
|
+
/**
|
|
661
|
+
* Original values of coverage-threshold keys deleted from
|
|
662
|
+
* `vitest.coverageProvider.options.thresholds` while neutralizing a
|
|
663
|
+
* partial run (issue #160). In `run` mode Vitest re-initializes the
|
|
664
|
+
* provider on every `vitest.start`, so the snapshot is moot — but in
|
|
665
|
+
* watch mode the provider is created once and scoped reruns go through
|
|
666
|
+
* `rerunFiles` without re-initializing it, so a deleted key would stay
|
|
667
|
+
* gone for the rest of the watch session. `onTestRunStart` restores
|
|
668
|
+
* these keys (only if still absent — a legitimately re-initialized
|
|
669
|
+
* provider is left alone) and clears the map. Empty when the last run
|
|
670
|
+
* was not partial.
|
|
671
|
+
*
|
|
672
|
+
* @internal
|
|
673
|
+
*/
|
|
674
|
+
private neutralizedThresholdSnapshot;
|
|
646
675
|
constructor(options?: AgentReporterConstructorOptions);
|
|
647
676
|
/**
|
|
648
677
|
* The resolved reporter config built at construction time. Exposed for
|
|
@@ -1134,6 +1163,13 @@ interface CoverageOptions {
|
|
|
1134
1163
|
readonly baselines?: CoverageBaselines;
|
|
1135
1164
|
/** When true, include files with zero coverage rather than omitting them. */
|
|
1136
1165
|
readonly includeBareZero: boolean;
|
|
1166
|
+
/**
|
|
1167
|
+
* Total test-file count for the project, when known (issue #160 gap 1).
|
|
1168
|
+
* Only meaningful on a scoped run — threaded onto the returned
|
|
1169
|
+
* `CoverageReport.totalFiles` so the scoped-coverage note can render
|
|
1170
|
+
* "N of M test files" instead of just "N".
|
|
1171
|
+
*/
|
|
1172
|
+
readonly totalFiles?: number;
|
|
1137
1173
|
}
|
|
1138
1174
|
declare const CoverageAnalyzer_base: Context.ServiceClass<CoverageAnalyzer, "vitest-agent/CoverageAnalyzer", {
|
|
1139
1175
|
readonly process: (coverage: unknown, options: CoverageOptions) => Effect.Effect<Option.Option<CoverageReport>>;
|
|
@@ -112,6 +112,7 @@ function processCoverageInternal(coverageMap, options, testedFiles) {
|
|
|
112
112
|
} } : {},
|
|
113
113
|
scoped,
|
|
114
114
|
...scoped && testedFiles ? { scopedFiles: [...testedFiles] } : {},
|
|
115
|
+
...scoped && options.totalFiles !== void 0 ? { totalFiles: options.totalFiles } : {},
|
|
115
116
|
lowCoverage,
|
|
116
117
|
lowCoverageFiles: lowCoverage.map((f) => f.file),
|
|
117
118
|
...options.targets ? {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/plugin",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Vitest plugin for the vitest-agent ecosystem: owns persistence, classification, baselines, trends, and dispatches rendering to a configurable reporter.",
|
|
6
6
|
"keywords": [
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@effect/platform-node": "4.0.0-rc.109",
|
|
43
43
|
"@effect/sql-sqlite-node": "4.0.0-rc.109",
|
|
44
|
-
"@effected/workspaces": "^0.
|
|
45
|
-
"@vitest-agent/cli": "2.2.
|
|
46
|
-
"@vitest-agent/mcp": "2.4.
|
|
47
|
-
"@vitest-agent/reporter": "2.2.
|
|
48
|
-
"@vitest-agent/sdk": "2.
|
|
44
|
+
"@effected/workspaces": "^0.19.0",
|
|
45
|
+
"@vitest-agent/cli": "2.2.14",
|
|
46
|
+
"@vitest-agent/mcp": "2.4.13",
|
|
47
|
+
"@vitest-agent/reporter": "2.2.3",
|
|
48
|
+
"@vitest-agent/sdk": "2.5.0",
|
|
49
49
|
"effect": "4.0.0-rc.109",
|
|
50
50
|
"magic-string": "^1.2.3"
|
|
51
51
|
},
|
package/plugin.js
CHANGED
|
@@ -8,9 +8,13 @@ import { discoverProjects } from "./utils/discover-projects.js";
|
|
|
8
8
|
import { ensureGithubActionsReporter } from "./utils/ensure-github-reporter.js";
|
|
9
9
|
import { injectTags } from "./utils/inject-tags.js";
|
|
10
10
|
import { isBenignViteSourceMapWarning } from "./utils/is-benign-vite-source-map-warning.js";
|
|
11
|
+
import { resolveCoverageDirIsolation } from "./utils/resolve-coverage-dir-isolation.js";
|
|
11
12
|
import { DEFAULT_BUILT_RECENTLY_MS, DEFAULT_LOCK_STALE_MS, DEFAULT_LOCK_WAIT_TIMEOUT_MS, acquireRunScriptLock, markRunScriptDone, parseLockTimingOverride, releaseRunScriptLock } from "./utils/run-script-lock.js";
|
|
12
13
|
import { stripConsoleReporters } from "./utils/strip-console-reporters.js";
|
|
13
14
|
import { execSync } from "node:child_process";
|
|
15
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
16
|
+
import { tmpdir } from "node:os";
|
|
17
|
+
import { join } from "node:path";
|
|
14
18
|
import { AgentConsoleMode, CiConsoleMode, CoverageLevel, EnvironmentDetector, EnvironmentDetectorLive, HumanConsoleMode, SRC_DIR, TEST_DIR, formatFatalError, isTestFileName, resolveLogLevel } from "@vitest-agent/sdk";
|
|
15
19
|
import { Effect, Schema } from "effect";
|
|
16
20
|
|
|
@@ -94,6 +98,15 @@ function resolveFormat(mode) {
|
|
|
94
98
|
*/
|
|
95
99
|
const aggregatedReporterByVitest = /* @__PURE__ */ new WeakSet();
|
|
96
100
|
/**
|
|
101
|
+
* Guards the coverage.reportsDirectory isolation decision (issue #194) to
|
|
102
|
+
* run at most once per Vitest run — `configureVitest` fires once per
|
|
103
|
+
* project, but `coverage.reportsDirectory` is root-level config shared by
|
|
104
|
+
* every project in that run.
|
|
105
|
+
*
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
108
|
+
const coverageDirDecidedByVitest = /* @__PURE__ */ new WeakSet();
|
|
109
|
+
/**
|
|
97
110
|
* The version of this package, inlined at build time from
|
|
98
111
|
* `package.json#version` via rslib-builder's `__PACKAGE_VERSION__` substitution.
|
|
99
112
|
* Re-exported from the package barrel as the public symbol; defined here so
|
|
@@ -101,7 +114,7 @@ const aggregatedReporterByVitest = /* @__PURE__ */ new WeakSet();
|
|
|
101
114
|
*
|
|
102
115
|
* @public
|
|
103
116
|
*/
|
|
104
|
-
const CURRENT_PLUGIN_VERSION = "2.5.
|
|
117
|
+
const CURRENT_PLUGIN_VERSION = "2.5.5";
|
|
105
118
|
const TEST_FILE_DIR_RE = new RegExp(`/(?:${SRC_DIR}|${TEST_DIR})/`);
|
|
106
119
|
const isTestFile = (id) => isTestFileName(id) && TEST_FILE_DIR_RE.test(id);
|
|
107
120
|
/**
|
|
@@ -203,6 +216,31 @@ function AgentPlugin(options = {}, _layer) {
|
|
|
203
216
|
const rawTargets = options.coverageTargets;
|
|
204
217
|
const coverageTargets = rawTargets ? resolveThresholds(rawTargets) : void 0;
|
|
205
218
|
const coverageMode = coverageConfig?.enabled === false ? "ui-only" : "full";
|
|
219
|
+
if (coverageConfig && !coverageDirDecidedByVitest.has(vitest)) {
|
|
220
|
+
coverageDirDecidedByVitest.add(vitest);
|
|
221
|
+
const dirDecision = resolveCoverageDirIsolation({
|
|
222
|
+
executor,
|
|
223
|
+
coverageEnabled: coverageMode === "full",
|
|
224
|
+
env: process.env,
|
|
225
|
+
configured: coverageConfig.reportsDirectory
|
|
226
|
+
});
|
|
227
|
+
if (dirDecision.kind === "isolate") {
|
|
228
|
+
const isolatedDir = mkdtempSync(join(tmpdir(), "vitest-agent-cov-"));
|
|
229
|
+
log("isolating coverage.reportsDirectory ->", isolatedDir);
|
|
230
|
+
coverageConfig.reportsDirectory = isolatedDir;
|
|
231
|
+
vitest.onClose(() => {
|
|
232
|
+
try {
|
|
233
|
+
rmSync(isolatedDir, {
|
|
234
|
+
recursive: true,
|
|
235
|
+
force: true
|
|
236
|
+
});
|
|
237
|
+
} catch {}
|
|
238
|
+
});
|
|
239
|
+
} else if (dirDecision.kind === "explicit") {
|
|
240
|
+
log("using explicit VITEST_AGENT_COVERAGE_DIR ->", dirDecision.dir);
|
|
241
|
+
coverageConfig.reportsDirectory = dirDecision.dir;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
206
244
|
const transport = options.transport ?? { kind: "local" };
|
|
207
245
|
log("transport.kind:", transport.kind);
|
|
208
246
|
const passWithNoTestsRaw = vitest.config.passWithNoTests;
|
package/reporter.js
CHANGED
|
@@ -4,14 +4,15 @@ import { ReporterLive } from "./layers/ReporterLive.js";
|
|
|
4
4
|
import { buildReporterKit, normalizeReporters } from "./utils/build-reporter-kit.js";
|
|
5
5
|
import { captureEnvVars } from "./utils/capture-env.js";
|
|
6
6
|
import { captureSettings, hashSettings } from "./utils/capture-settings.js";
|
|
7
|
+
import { isPartialRun } from "./utils/is-partial-run.js";
|
|
7
8
|
import { processFailure } from "./utils/process-failure.js";
|
|
8
9
|
import { routeRenderedOutput } from "./utils/route-rendered-output.js";
|
|
9
10
|
import { stringifyFailureValue } from "./utils/stringify-failure-value.js";
|
|
11
|
+
import { mkdirSync } from "node:fs";
|
|
12
|
+
import { dirname } from "node:path";
|
|
10
13
|
import { DataReader, DataStore, DetailResolver, EnvironmentDetector, ExecutorResolver, FormatSelector, HistoryTracker, OutputPipelineLive, PathResolutionLive, buildAgentReport, coerceErrorField, computeTrend, ensureMigrated, formatFatalError, historyKey, isTimeoutError, probeHostMetadataFromEnv, resolveDataPath, resolveLogFile, resolveLogLevel } from "@vitest-agent/sdk";
|
|
11
14
|
import { Effect, Option, PubSub } from "effect";
|
|
12
15
|
import { randomUUID } from "node:crypto";
|
|
13
|
-
import { mkdirSync } from "node:fs";
|
|
14
|
-
import { dirname } from "node:path";
|
|
15
16
|
import { NodeServices } from "@effect/platform-node";
|
|
16
17
|
import { DefaultVitestAgentReporter } from "@vitest-agent/reporter";
|
|
17
18
|
|
|
@@ -199,6 +200,35 @@ var AgentReporter = class {
|
|
|
199
200
|
* @internal
|
|
200
201
|
*/
|
|
201
202
|
hookStartedAt = /* @__PURE__ */ new Map();
|
|
203
|
+
/**
|
|
204
|
+
* Count of test specifications Vitest started for the current run,
|
|
205
|
+
* captured from `onTestRunStart`'s `specifications` argument. Used
|
|
206
|
+
* alongside a fresh `globTestSpecifications()` total in `onTestRunEnd`
|
|
207
|
+
* as one of `isPartialRun`'s signals (issue #160 gap 2) — a tags-only
|
|
208
|
+
* `run_tests` filter narrows the run without setting `filenamePattern`
|
|
209
|
+
* or `projectFilter`, so the spec-count comparison is the only signal
|
|
210
|
+
* that catches it. `undefined` when `onTestRunStart` never fired
|
|
211
|
+
* (tests invoking `onTestRunEnd` directly); `onTestRunEnd` falls back
|
|
212
|
+
* to the executed module count in that case.
|
|
213
|
+
*
|
|
214
|
+
* @internal
|
|
215
|
+
*/
|
|
216
|
+
startedSpecCount;
|
|
217
|
+
/**
|
|
218
|
+
* Original values of coverage-threshold keys deleted from
|
|
219
|
+
* `vitest.coverageProvider.options.thresholds` while neutralizing a
|
|
220
|
+
* partial run (issue #160). In `run` mode Vitest re-initializes the
|
|
221
|
+
* provider on every `vitest.start`, so the snapshot is moot — but in
|
|
222
|
+
* watch mode the provider is created once and scoped reruns go through
|
|
223
|
+
* `rerunFiles` without re-initializing it, so a deleted key would stay
|
|
224
|
+
* gone for the rest of the watch session. `onTestRunStart` restores
|
|
225
|
+
* these keys (only if still absent — a legitimately re-initialized
|
|
226
|
+
* provider is left alone) and clears the map. Empty when the last run
|
|
227
|
+
* was not partial.
|
|
228
|
+
*
|
|
229
|
+
* @internal
|
|
230
|
+
*/
|
|
231
|
+
neutralizedThresholdSnapshot = /* @__PURE__ */ new Map();
|
|
202
232
|
constructor(options = {}) {
|
|
203
233
|
this.logLevel = resolveLogLevel();
|
|
204
234
|
this.logFile = resolveLogFile();
|
|
@@ -397,6 +427,16 @@ var AgentReporter = class {
|
|
|
397
427
|
* `RunStarted` event for live subscribers.
|
|
398
428
|
*/
|
|
399
429
|
onTestRunStart(_specifications) {
|
|
430
|
+
this.startedSpecCount = _specifications.length;
|
|
431
|
+
if (this.neutralizedThresholdSnapshot.size > 0) {
|
|
432
|
+
try {
|
|
433
|
+
const thresholds = (this._vitest?.coverageProvider)?.options?.thresholds;
|
|
434
|
+
if (thresholds !== void 0 && typeof thresholds === "object") {
|
|
435
|
+
for (const [key, value] of this.neutralizedThresholdSnapshot) if (!(key in thresholds)) thresholds[key] = value;
|
|
436
|
+
}
|
|
437
|
+
} catch {}
|
|
438
|
+
this.neutralizedThresholdSnapshot = /* @__PURE__ */ new Map();
|
|
439
|
+
}
|
|
400
440
|
if (!this.wantsRunEvents()) return;
|
|
401
441
|
this.currentRunId = randomUUID();
|
|
402
442
|
this.moduleStartedAt.clear();
|
|
@@ -791,6 +831,7 @@ var AgentReporter = class {
|
|
|
791
831
|
async onTestRunEnd(testModules, unhandledErrors, reason) {
|
|
792
832
|
if (this.rendered) return;
|
|
793
833
|
this.rendered = true;
|
|
834
|
+
const errors = unhandledErrors;
|
|
794
835
|
if (this.wantsRunEvents() && this.currentRunId !== null) {
|
|
795
836
|
let pass = 0;
|
|
796
837
|
let fail = 0;
|
|
@@ -820,11 +861,11 @@ var AgentReporter = class {
|
|
|
820
861
|
timeoutCount: timeout,
|
|
821
862
|
durationMs: totalDuration,
|
|
822
863
|
// @vitest-agent/ui, which both populate `collectedModules` on their
|
|
823
|
-
collectedModules: testModules.length
|
|
864
|
+
collectedModules: testModules.length,
|
|
865
|
+
...errors.length > 0 && { unhandledErrors: errors }
|
|
824
866
|
});
|
|
825
867
|
}
|
|
826
868
|
const modules = testModules;
|
|
827
|
-
const errors = unhandledErrors;
|
|
828
869
|
const opts = this.options;
|
|
829
870
|
const stashedCoverage = this.coverage;
|
|
830
871
|
const stashedVitest = this._vitest;
|
|
@@ -832,6 +873,31 @@ var AgentReporter = class {
|
|
|
832
873
|
const logFile = this.logFile;
|
|
833
874
|
const runEvents = this.runEvents;
|
|
834
875
|
const preBuiltReporters = this.reporters;
|
|
876
|
+
const vitestForPartialCheck = stashedVitest;
|
|
877
|
+
const startedSpecCount = this.startedSpecCount ?? modules.length;
|
|
878
|
+
let totalSpecCount = startedSpecCount;
|
|
879
|
+
try {
|
|
880
|
+
const specs = await vitestForPartialCheck?.globTestSpecifications?.();
|
|
881
|
+
if (specs !== void 0) totalSpecCount = specs.length;
|
|
882
|
+
} catch {}
|
|
883
|
+
const isPartial = isPartialRun({
|
|
884
|
+
filenamePattern: vitestForPartialCheck?.filenamePattern,
|
|
885
|
+
startedSpecCount,
|
|
886
|
+
totalSpecCount,
|
|
887
|
+
projectFilter: opts.projectFilter
|
|
888
|
+
});
|
|
889
|
+
const testedFiles = isPartial ? Array.from(new Set(modules.map((m) => m.relativeModuleId.replace(/\.test\.([^.]+)$/, ".$1").replace(/\.spec\.([^.]+)$/, ".$1")))) : void 0;
|
|
890
|
+
if (isPartial) {
|
|
891
|
+
this.neutralizedThresholdSnapshot = /* @__PURE__ */ new Map();
|
|
892
|
+
try {
|
|
893
|
+
const thresholds = (stashedVitest?.coverageProvider)?.options?.thresholds;
|
|
894
|
+
if (thresholds !== void 0 && typeof thresholds === "object") for (const key of Object.keys(thresholds)) {
|
|
895
|
+
if (key === "perFile" || key === "autoUpdate" || key === "100") continue;
|
|
896
|
+
this.neutralizedThresholdSnapshot.set(key, thresholds[key]);
|
|
897
|
+
delete thresholds[key];
|
|
898
|
+
}
|
|
899
|
+
} catch {}
|
|
900
|
+
}
|
|
835
901
|
const emitEvent = (event) => {
|
|
836
902
|
this.emit(event);
|
|
837
903
|
};
|
|
@@ -964,9 +1030,10 @@ var AgentReporter = class {
|
|
|
964
1030
|
thresholds: opts.coverageThresholds,
|
|
965
1031
|
includeBareZero: opts.includeBareZero,
|
|
966
1032
|
...opts.coverageTargets ? { targets: opts.coverageTargets } : {},
|
|
967
|
-
...baselines ? { baselines } : {}
|
|
1033
|
+
...baselines ? { baselines } : {},
|
|
1034
|
+
...isPartial ? { totalFiles: totalSpecCount } : {}
|
|
968
1035
|
};
|
|
969
|
-
const coverageResult = stashedCoverage && isFirstProject ? yield* analyzer.process(stashedCoverage, coverageOpts) : Option.none();
|
|
1036
|
+
const coverageResult = stashedCoverage && isFirstProject ? isPartial ? yield* analyzer.processScoped(stashedCoverage, coverageOpts, testedFiles ?? []) : yield* analyzer.process(stashedCoverage, coverageOpts) : Option.none();
|
|
970
1037
|
const coverageReport = Option.getOrUndefined(coverageResult);
|
|
971
1038
|
if (wantsRunEvents && coverageReport !== void 0) {
|
|
972
1039
|
const globalThresholds = coverageReport.thresholds.global;
|
|
@@ -978,9 +1045,12 @@ var AgentReporter = class {
|
|
|
978
1045
|
file: fc.file,
|
|
979
1046
|
missing: fc.summary,
|
|
980
1047
|
uncoveredLines: fc.uncoveredLines
|
|
981
|
-
}))
|
|
1048
|
+
})),
|
|
1049
|
+
...coverageReport.scoped ? { scoped: coverageReport.scoped } : {},
|
|
1050
|
+
...coverageReport.scopedFiles !== void 0 ? { scopedFiles: coverageReport.scopedFiles.length } : {},
|
|
1051
|
+
...coverageReport.totalFiles !== void 0 ? { totalFiles: coverageReport.totalFiles } : {}
|
|
982
1052
|
});
|
|
983
|
-
for (const metric of [
|
|
1053
|
+
if (!coverageReport.scoped) for (const metric of [
|
|
984
1054
|
"lines",
|
|
985
1055
|
"branches",
|
|
986
1056
|
"functions",
|
|
@@ -1029,7 +1099,7 @@ var AgentReporter = class {
|
|
|
1029
1099
|
passed: baseReport.summary.passed,
|
|
1030
1100
|
failed: baseReport.summary.failed,
|
|
1031
1101
|
skipped: baseReport.summary.skipped,
|
|
1032
|
-
scoped:
|
|
1102
|
+
scoped: isPartial,
|
|
1033
1103
|
actorType: attribution.actorType,
|
|
1034
1104
|
agentId: attribution.agentId,
|
|
1035
1105
|
conversationId: attribution.conversationId,
|
|
@@ -1311,6 +1381,10 @@ var AgentReporter = class {
|
|
|
1311
1381
|
const newBaselines = computeUpdatedBaselines(baselines, coverageReport.totals, opts.coverageTargets);
|
|
1312
1382
|
yield* store.writeBaselines(newBaselines);
|
|
1313
1383
|
}
|
|
1384
|
+
if (coverageReport && !coverageReport.scoped) {
|
|
1385
|
+
if (opts.coverageThresholds !== void 0) yield* store.writeThresholds(opts.coverageThresholds);
|
|
1386
|
+
if (opts.coverageTargets !== void 0) yield* store.writeTargets(opts.coverageTargets);
|
|
1387
|
+
}
|
|
1314
1388
|
let trendSummary;
|
|
1315
1389
|
if (coverageReport && !coverageReport.scoped) {
|
|
1316
1390
|
const firstProjectKey = Array.from(projectGroups.keys())[0];
|
|
@@ -2,8 +2,8 @@ import { toPosixPath } from "./to-posix-path.js";
|
|
|
2
2
|
import { nodeWalkerFs } from "./walker-fs.js";
|
|
3
3
|
import { DefaultDiscoverStrategy } from "./discover-strategy.js";
|
|
4
4
|
import { isTestShapedPackage } from "./is-test-shaped-package.js";
|
|
5
|
-
import { NON_DISCOVERABLE_DIRS, SRC_DIR, TEST_DIR } from "@vitest-agent/sdk";
|
|
6
5
|
import { isAbsolute, join, normalize, relative } from "node:path";
|
|
6
|
+
import { NON_DISCOVERABLE_DIRS, SRC_DIR, TEST_DIR } from "@vitest-agent/sdk";
|
|
7
7
|
import { findWorkspaceRootSync, getWorkspacePackagesSync } from "@effected/workspaces";
|
|
8
8
|
import { nodeSyncOps } from "@effected/workspaces/node-sync";
|
|
9
9
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { nodeWalkerFs } from "./walker-fs.js";
|
|
2
2
|
import { findTestFiles } from "./find-test-files.js";
|
|
3
3
|
import { Tag } from "./tag.js";
|
|
4
|
-
import { NON_DISCOVERABLE_DIRS, SRC_DIR, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS } from "@vitest-agent/sdk";
|
|
5
4
|
import { join, sep } from "node:path";
|
|
5
|
+
import { NON_DISCOVERABLE_DIRS, SRC_DIR, TEST_DIR, TEST_FILE_GLOB_SUFFIX, TEST_HELPER_DIRS } from "@vitest-agent/sdk";
|
|
6
6
|
import { configDefaults } from "vitest/config";
|
|
7
7
|
|
|
8
8
|
//#region src/utils/discover-strategy.ts
|
package/utils/find-test-files.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toPosixPath } from "./to-posix-path.js";
|
|
2
2
|
import { nodeWalkerFs } from "./walker-fs.js";
|
|
3
|
-
import { NON_DISCOVERABLE_DIRS } from "@vitest-agent/sdk";
|
|
4
3
|
import { join, relative } from "node:path";
|
|
4
|
+
import { NON_DISCOVERABLE_DIRS } from "@vitest-agent/sdk";
|
|
5
5
|
|
|
6
6
|
//#region src/utils/find-test-files.ts
|
|
7
7
|
function globToRegex(pattern) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/utils/is-partial-run.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pure decision function: was this Vitest run scoped to a subset of the
|
|
4
|
+
* project's test files?
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* A run is partial when any of the following holds:
|
|
8
|
+
* - Vitest's `filenamePattern` was set (a non-empty array) for this run.
|
|
9
|
+
* - Fewer specifications started than exist in total for the same project set.
|
|
10
|
+
* - An explicit `--project` filter was supplied.
|
|
11
|
+
*
|
|
12
|
+
* Any of these makes coverage's whole-project denominator meaningless for
|
|
13
|
+
* threshold enforcement (issue #160).
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
function isPartialRun(input) {
|
|
18
|
+
const { filenamePattern, startedSpecCount, totalSpecCount, projectFilter } = input;
|
|
19
|
+
if (filenamePattern !== void 0 && filenamePattern.length > 0) return true;
|
|
20
|
+
if (startedSpecCount < totalSpecCount) return true;
|
|
21
|
+
if (projectFilter !== void 0) return true;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { isPartialRun };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { nodeWalkerFs } from "./walker-fs.js";
|
|
2
2
|
import { findTestFiles } from "./find-test-files.js";
|
|
3
|
-
import { SRC_DIR, TEST_DIR, TEST_FILE_GLOB_SUFFIX } from "@vitest-agent/sdk";
|
|
4
3
|
import { join } from "node:path";
|
|
4
|
+
import { SRC_DIR, TEST_DIR, TEST_FILE_GLOB_SUFFIX } from "@vitest-agent/sdk";
|
|
5
5
|
|
|
6
6
|
//#region src/utils/is-test-shaped-package.ts
|
|
7
7
|
/**
|
package/utils/process-failure.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { computeFailureSignature, findFunctionBoundary } from "@vitest-agent/sdk";
|
|
2
1
|
import { readFileSync } from "node:fs";
|
|
2
|
+
import { computeFailureSignature, findFunctionBoundary } from "@vitest-agent/sdk";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/process-failure.ts
|
|
5
5
|
const FRAME_LINE_REGEX = /^\s*at\s+(?:([\w$.<>[\] ]+?)\s+)?\(?([^\n)]+):(\d+):(\d+)\)?\s*$/;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/utils/resolve-coverage-dir-isolation.ts
|
|
2
|
+
/**
|
|
3
|
+
* Values of `VITEST_AGENT_COVERAGE_DIR_ISOLATION` that opt out of the
|
|
4
|
+
* per-process coverage directory rewrite.
|
|
5
|
+
*/
|
|
6
|
+
const OPT_OUT_VALUES = /* @__PURE__ */ new Set([
|
|
7
|
+
"off",
|
|
8
|
+
"0",
|
|
9
|
+
"false"
|
|
10
|
+
]);
|
|
11
|
+
/**
|
|
12
|
+
* Pure decision function: should this Vitest run's `coverage.reportsDirectory`
|
|
13
|
+
* be isolated to a per-process temp directory?
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* Two concurrent plain-CLI `vitest run` invocations in one checkout share
|
|
17
|
+
* `coverage.reportsDirectory` by default; the v8 provider's `clean: true`
|
|
18
|
+
* default `rm -rf`s that directory at run start, so one run can delete the
|
|
19
|
+
* other's `.tmp` files mid-run (issue #194). The MCP `run_tests` path
|
|
20
|
+
* already isolates via `makeCoverageDirOverride()`; this function drives
|
|
21
|
+
* the equivalent decision for the plain-CLI (`AgentPlugin.configureVitest`)
|
|
22
|
+
* path.
|
|
23
|
+
*
|
|
24
|
+
* Only the `agent` executor is ever isolated — a human's `./coverage`
|
|
25
|
+
* artifacts, and CI's configured directory, are never relocated.
|
|
26
|
+
*
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
function resolveCoverageDirIsolation(input) {
|
|
30
|
+
const { executor, coverageEnabled, env } = input;
|
|
31
|
+
if (!coverageEnabled) return { kind: "keep" };
|
|
32
|
+
if (executor !== "agent") return { kind: "keep" };
|
|
33
|
+
const isolationOverride = env.VITEST_AGENT_COVERAGE_DIR_ISOLATION;
|
|
34
|
+
if (isolationOverride !== void 0 && OPT_OUT_VALUES.has(isolationOverride)) return { kind: "keep" };
|
|
35
|
+
const explicitDir = env.VITEST_AGENT_COVERAGE_DIR;
|
|
36
|
+
if (explicitDir !== void 0 && explicitDir.length > 0) return {
|
|
37
|
+
kind: "explicit",
|
|
38
|
+
dir: explicitDir
|
|
39
|
+
};
|
|
40
|
+
return { kind: "isolate" };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { resolveCoverageDirIsolation };
|
package/utils/run-script-lock.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
2
1
|
import { closeSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeFileSync, writeSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
2
|
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
5
5
|
|
|
6
6
|
//#region src/utils/run-script-lock.ts
|
|
7
7
|
/**
|