vitest 2.1.2 → 2.1.3

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.
Files changed (42) hide show
  1. package/LICENSE.md +320 -30
  2. package/dist/browser.d.ts +2 -2
  3. package/dist/browser.js +1 -1
  4. package/dist/chunks/{base.tiemDJX6.js → base.BO5Jx7vw.js} +1 -1
  5. package/dist/chunks/{cac.B9PaPYY1.js → cac.BSMVokHR.js} +4 -4
  6. package/dist/chunks/{cli-api.CHxC4-U8.js → cli-api.btGgw3PC.js} +2843 -2778
  7. package/dist/chunks/{console.DI3gHgtH.js → console.CfT1Wjed.js} +1 -1
  8. package/dist/chunks/{creator.Cf-MKt9i.js → creator.CBPphXqR.js} +1 -1
  9. package/dist/chunks/{globals.HsM2o-0O.js → globals.Bdzt04Qm.js} +2 -2
  10. package/dist/chunks/{index.mAqbj9F9.js → index.-d_XpZEA.js} +1 -1
  11. package/dist/chunks/{index.Ckn0Cw1h.js → index.4GFF2h22.js} +1 -1
  12. package/dist/chunks/{index.FcPVJkIQ.js → index.Dz2opmmU.js} +21 -4
  13. package/dist/chunks/{index.BpojBOif.js → index.X0nbfr6-.js} +3 -3
  14. package/dist/chunks/{reporters.DAfKSDh5.d.ts → reporters.C4ZHgdxQ.d.ts} +9 -9
  15. package/dist/chunks/{resolveConfig.D1DENLPF.js → resolveConfig.Dha6ilPI.js} +58 -40
  16. package/dist/chunks/{runBaseTests.D-Gcin7G.js → runBaseTests.Cx4wXyTR.js} +5 -5
  17. package/dist/chunks/{setup-common.DF96bIYE.js → setup-common.BKyF15v_.js} +1 -1
  18. package/dist/chunks/{vi.DUs2eKik.js → vi.BskyZC5g.js} +2 -0
  19. package/dist/chunks/{vite.8fk186v-.d.ts → vite.YH7MrecS.d.ts} +1 -1
  20. package/dist/chunks/{vm.CPXwWp4C.js → vm.DB_hLchi.js} +1 -1
  21. package/dist/chunks/{worker.Chrs-_NL.d.ts → worker.B6RjTtbk.d.ts} +40 -26
  22. package/dist/chunks/{worker.Qtv8v5nL.d.ts → worker.CcJLfX8w.d.ts} +1 -1
  23. package/dist/cli.js +1 -1
  24. package/dist/config.d.ts +16 -11
  25. package/dist/coverage.d.ts +65 -28
  26. package/dist/coverage.js +249 -108
  27. package/dist/execute.d.ts +1 -1
  28. package/dist/index.d.ts +29 -12
  29. package/dist/index.js +3 -3
  30. package/dist/node.d.ts +4 -4
  31. package/dist/node.js +7 -6
  32. package/dist/reporters.d.ts +1 -1
  33. package/dist/reporters.js +1 -1
  34. package/dist/runners.js +1 -1
  35. package/dist/workers/forks.js +1 -1
  36. package/dist/workers/runVmTests.js +4 -4
  37. package/dist/workers/threads.js +1 -1
  38. package/dist/workers/vmForks.js +2 -2
  39. package/dist/workers/vmThreads.js +2 -2
  40. package/dist/workers.d.ts +2 -2
  41. package/dist/workers.js +3 -3
  42. package/package.json +12 -12
@@ -1,12 +1,12 @@
1
1
  import * as vite from 'vite';
2
- import { B as BaseCoverageOptions, R as ResolvedCoverageOptions, V as Vitest } from './chunks/reporters.DAfKSDh5.js';
2
+ import { A as AfterSuiteRunMeta } from './chunks/environment.CzISCQ7o.js';
3
+ import { R as ResolvedCoverageOptions, V as Vitest, a as ReportContext } from './chunks/reporters.C4ZHgdxQ.js';
3
4
  import '@vitest/runner';
4
5
  import '@vitest/pretty-format';
5
6
  import './chunks/config.Crbj2GAb.js';
6
7
  import '@vitest/snapshot';
7
8
  import '@vitest/snapshot/environment';
8
9
  import 'vite-node';
9
- import './chunks/environment.CzISCQ7o.js';
10
10
  import 'node:stream';
11
11
  import 'vite-node/client';
12
12
  import '@vitest/snapshot/manager';
@@ -129,39 +129,76 @@ interface ResolvedThreshold {
129
129
  name: string;
130
130
  thresholds: Partial<Record<Threshold, number | undefined>>;
131
131
  }
132
- declare class BaseCoverageProvider {
133
- /**
134
- * Check if current coverage is above configured thresholds and bump the thresholds if needed
135
- */
136
- updateThresholds({ thresholds: allThresholds, perFile, configurationFile, onUpdate, }: {
137
- thresholds: ResolvedThreshold[];
138
- perFile?: boolean;
139
- configurationFile: unknown;
140
- onUpdate: () => void;
141
- }): void;
142
- /**
143
- * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
144
- */
145
- checkThresholds({ thresholds: allThresholds, perFile, onError, }: {
146
- thresholds: ResolvedThreshold[];
147
- perFile?: boolean;
148
- onError: (error: string) => void;
149
- }): void;
132
+ /**
133
+ * Holds info about raw coverage results that are stored on file system:
134
+ *
135
+ * ```json
136
+ * "project-a": {
137
+ * "web": {
138
+ * "tests/math.test.ts": "coverage-1.json",
139
+ * "tests/utils.test.ts": "coverage-2.json",
140
+ * // ^^^^^^^^^^^^^^^ Raw coverage on file system
141
+ * },
142
+ * "ssr": { ... },
143
+ * "browser": { ... },
144
+ * },
145
+ * "project-b": ...
146
+ * ```
147
+ */
148
+ type CoverageFiles = Map<NonNullable<AfterSuiteRunMeta['projectName']> | symbol, Record<AfterSuiteRunMeta['transformMode'], {
149
+ [TestFilenames: string]: string;
150
+ }>>;
151
+ declare class BaseCoverageProvider<Options extends ResolvedCoverageOptions<'istanbul' | 'v8'>> {
152
+ ctx: Vitest;
153
+ readonly name: 'v8' | 'istanbul';
154
+ version: string;
155
+ options: Options;
156
+ coverageFiles: CoverageFiles;
157
+ pendingPromises: Promise<void>[];
158
+ coverageFilesDirectory: string;
159
+ _initialize(ctx: Vitest): void;
160
+ createCoverageMap(): CoverageMap;
161
+ generateReports(_: CoverageMap, __: boolean | undefined): Promise<void>;
162
+ parseConfigModule(_: string): Promise<{
163
+ generate: () => {
164
+ code: string;
165
+ };
166
+ }>;
167
+ resolveOptions(): Options;
168
+ clean(clean?: boolean): Promise<void>;
169
+ onAfterSuiteRun({ coverage, transformMode, projectName, testFiles }: AfterSuiteRunMeta): void;
170
+ readCoverageFiles<CoverageType>({ onFileRead, onFinished, onDebug }: {
171
+ /** Callback invoked with a single coverage result */
172
+ onFileRead: (data: CoverageType) => void;
173
+ /** Callback invoked once all results of a project for specific transform mode are read */
174
+ onFinished: (project: Vitest['projects'][number], transformMode: AfterSuiteRunMeta['transformMode']) => Promise<void>;
175
+ onDebug: ((...logs: any[]) => void) & {
176
+ enabled: boolean;
177
+ };
178
+ }): Promise<void>;
179
+ cleanAfterRun(): Promise<void>;
180
+ onTestFailure(): Promise<void>;
181
+ reportCoverage(coverageMap: unknown, { allTestsRun }: ReportContext): Promise<void>;
182
+ reportThresholds(coverageMap: CoverageMap, allTestsRun: boolean | undefined): Promise<void>;
150
183
  /**
151
184
  * Constructs collected coverage and users' threshold options into separate sets
152
185
  * where each threshold set holds their own coverage maps. Threshold set is either
153
186
  * for specific files defined by glob pattern or global for all other files.
154
187
  */
155
- resolveThresholds({ coverageMap, thresholds, createCoverageMap, root, }: {
156
- coverageMap: CoverageMap;
157
- thresholds: NonNullable<BaseCoverageOptions['thresholds']>;
158
- createCoverageMap: () => CoverageMap;
159
- root: string;
160
- }): ResolvedThreshold[];
188
+ private resolveThresholds;
189
+ /**
190
+ * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
191
+ */
192
+ private checkThresholds;
161
193
  /**
162
- * Resolve reporters from various configuration options
194
+ * Check if current coverage is above configured thresholds and bump the thresholds if needed
163
195
  */
164
- resolveReporters(configReporters: NonNullable<BaseCoverageOptions['reporter']>): ResolvedCoverageOptions['reporter'];
196
+ updateThresholds({ thresholds: allThresholds, onUpdate, configurationFile }: {
197
+ thresholds: ResolvedThreshold[];
198
+ configurationFile: unknown;
199
+ onUpdate: () => void;
200
+ }): Promise<void>;
201
+ mergeReports(coverageMaps: unknown[]): Promise<void>;
165
202
  hasTerminalReporter(reporters: ResolvedCoverageOptions['reporter']): boolean;
166
203
  toSlices<T>(array: T[], size: number): T[][];
167
204
  createUncoveredFileTransformer(ctx: Vitest): (filename: string) => Promise<vite.TransformResult | null | undefined>;
package/dist/coverage.js CHANGED
@@ -1,7 +1,8 @@
1
- import { relative } from 'pathe';
2
- import { m as mm, r as resolveCoverageReporters } from './chunks/resolveConfig.D1DENLPF.js';
1
+ import { existsSync, promises, readdirSync, writeFileSync } from 'node:fs';
2
+ import { resolve, relative } from 'pathe';
3
+ import { c as coverageConfigDefaults, r as resolveCoverageReporters, m as mm } from './chunks/resolveConfig.Dha6ilPI.js';
4
+ import c from 'tinyrainbow';
3
5
  import 'node:path';
4
- import 'node:fs';
5
6
  import 'node:fs/promises';
6
7
  import 'node:process';
7
8
  import 'node:module';
@@ -9,7 +10,6 @@ import 'node:url';
9
10
  import 'node:assert';
10
11
  import 'node:v8';
11
12
  import 'node:util';
12
- import 'tinyrainbow';
13
13
  import './chunks/constants.fzPh7AOq.js';
14
14
  import 'node:os';
15
15
  import './chunks/env.CmHVDJnw.js';
@@ -42,98 +42,176 @@ const THRESHOLD_KEYS = [
42
42
  "branches"
43
43
  ];
44
44
  const GLOBAL_THRESHOLDS_KEY = "global";
45
+ const DEFAULT_PROJECT = Symbol.for("default-project");
46
+ let uniqueId = 0;
45
47
  class BaseCoverageProvider {
46
- /**
47
- * Check if current coverage is above configured thresholds and bump the thresholds if needed
48
- */
49
- updateThresholds({
50
- thresholds: allThresholds,
51
- perFile,
52
- configurationFile,
53
- onUpdate
54
- }) {
55
- let updatedThresholds = false;
56
- const config = resolveConfig(configurationFile);
57
- assertConfigurationModule(config);
58
- for (const { coverageMap, thresholds, name } of allThresholds) {
59
- const summaries = perFile ? coverageMap.files().map(
60
- (file) => coverageMap.fileCoverageFor(file).toSummary()
61
- ) : [coverageMap.getCoverageSummary()];
62
- const thresholdsToUpdate = [];
63
- for (const key of THRESHOLD_KEYS) {
64
- const threshold = thresholds[key] ?? 100;
65
- const actual = Math.min(
66
- ...summaries.map((summary) => summary[key].pct)
67
- );
68
- if (actual > threshold) {
69
- thresholdsToUpdate.push([key, actual]);
70
- }
71
- }
72
- if (thresholdsToUpdate.length === 0) {
73
- continue;
74
- }
75
- updatedThresholds = true;
76
- for (const [threshold, newValue] of thresholdsToUpdate) {
77
- if (name === GLOBAL_THRESHOLDS_KEY) {
78
- config.test.coverage.thresholds[threshold] = newValue;
79
- } else {
80
- const glob = config.test.coverage.thresholds[name];
81
- glob[threshold] = newValue;
82
- }
48
+ ctx;
49
+ name;
50
+ version;
51
+ options;
52
+ coverageFiles = /* @__PURE__ */ new Map();
53
+ pendingPromises = [];
54
+ coverageFilesDirectory;
55
+ _initialize(ctx) {
56
+ this.ctx = ctx;
57
+ if (ctx.version !== this.version) {
58
+ ctx.logger.warn(
59
+ c.yellow(
60
+ `Loaded ${c.inverse(c.yellow(` vitest@${ctx.version} `))} and ${c.inverse(c.yellow(` @vitest/coverage-${this.name}@${this.version} `))}.
61
+ Running mixed versions is not supported and may lead into bugs
62
+ Update your dependencies and make sure the versions match.`
63
+ )
64
+ );
65
+ }
66
+ const config = ctx.config.coverage;
67
+ this.options = {
68
+ ...coverageConfigDefaults,
69
+ // User's options
70
+ ...config,
71
+ // Resolved fields
72
+ provider: this.name,
73
+ reportsDirectory: resolve(
74
+ ctx.config.root,
75
+ config.reportsDirectory || coverageConfigDefaults.reportsDirectory
76
+ ),
77
+ reporter: resolveCoverageReporters(
78
+ config.reporter || coverageConfigDefaults.reporter
79
+ ),
80
+ thresholds: config.thresholds && {
81
+ ...config.thresholds,
82
+ lines: config.thresholds["100"] ? 100 : config.thresholds.lines,
83
+ branches: config.thresholds["100"] ? 100 : config.thresholds.branches,
84
+ functions: config.thresholds["100"] ? 100 : config.thresholds.functions,
85
+ statements: config.thresholds["100"] ? 100 : config.thresholds.statements
83
86
  }
87
+ };
88
+ const shard = this.ctx.config.shard;
89
+ const tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
90
+ this.coverageFilesDirectory = resolve(
91
+ this.options.reportsDirectory,
92
+ tempDirectory
93
+ );
94
+ }
95
+ createCoverageMap() {
96
+ throw new Error("BaseReporter's createCoverageMap was not overwritten");
97
+ }
98
+ async generateReports(_, __) {
99
+ throw new Error("BaseReporter's generateReports was not overwritten");
100
+ }
101
+ async parseConfigModule(_) {
102
+ throw new Error("BaseReporter's parseConfigModule was not overwritten");
103
+ }
104
+ resolveOptions() {
105
+ return this.options;
106
+ }
107
+ async clean(clean = true) {
108
+ if (clean && existsSync(this.options.reportsDirectory)) {
109
+ await promises.rm(this.options.reportsDirectory, {
110
+ recursive: true,
111
+ force: true,
112
+ maxRetries: 10
113
+ });
84
114
  }
85
- if (updatedThresholds) {
86
- console.log(
87
- "Updating thresholds to configuration file. You may want to push with updated coverage thresholds."
88
- );
89
- onUpdate();
115
+ if (existsSync(this.coverageFilesDirectory)) {
116
+ await promises.rm(this.coverageFilesDirectory, {
117
+ recursive: true,
118
+ force: true,
119
+ maxRetries: 10
120
+ });
90
121
  }
122
+ await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
123
+ this.coverageFiles = /* @__PURE__ */ new Map();
124
+ this.pendingPromises = [];
91
125
  }
92
- /**
93
- * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
94
- */
95
- checkThresholds({
96
- thresholds: allThresholds,
97
- perFile,
98
- onError
99
- }) {
100
- for (const { coverageMap, thresholds, name } of allThresholds) {
101
- if (thresholds.branches === void 0 && thresholds.functions === void 0 && thresholds.lines === void 0 && thresholds.statements === void 0) {
102
- continue;
103
- }
104
- const summaries = perFile ? coverageMap.files().map((file) => ({
105
- file,
106
- summary: coverageMap.fileCoverageFor(file).toSummary()
107
- })) : [
108
- {
109
- file: null,
110
- summary: coverageMap.getCoverageSummary()
111
- }
112
- ];
113
- for (const { summary, file } of summaries) {
114
- for (const thresholdKey of [
115
- "lines",
116
- "functions",
117
- "statements",
118
- "branches"
119
- ]) {
120
- const threshold = thresholds[thresholdKey];
121
- if (threshold !== void 0) {
122
- const coverage = summary.data[thresholdKey].pct;
123
- if (coverage < threshold) {
124
- process.exitCode = 1;
125
- let errorMessage = `ERROR: Coverage for ${thresholdKey} (${coverage}%) does not meet ${name === GLOBAL_THRESHOLDS_KEY ? name : `"${name}"`} threshold (${threshold}%)`;
126
- if (perFile && file) {
127
- errorMessage += ` for ${relative("./", file).replace(
128
- /\\/g,
129
- "/"
130
- )}`;
131
- }
132
- onError(errorMessage);
133
- }
126
+ onAfterSuiteRun({ coverage, transformMode, projectName, testFiles }) {
127
+ if (!coverage) {
128
+ return;
129
+ }
130
+ if (transformMode !== "web" && transformMode !== "ssr" && transformMode !== "browser") {
131
+ throw new Error(`Invalid transform mode: ${transformMode}`);
132
+ }
133
+ let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
134
+ if (!entry) {
135
+ entry = { web: {}, ssr: {}, browser: {} };
136
+ this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
137
+ }
138
+ const testFilenames = testFiles.join();
139
+ const filename = resolve(
140
+ this.coverageFilesDirectory,
141
+ `coverage-${uniqueId++}.json`
142
+ );
143
+ entry[transformMode][testFilenames] = filename;
144
+ const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
145
+ this.pendingPromises.push(promise);
146
+ }
147
+ async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
148
+ let index = 0;
149
+ const total = this.pendingPromises.length;
150
+ await Promise.all(this.pendingPromises);
151
+ this.pendingPromises = [];
152
+ for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) {
153
+ for (const [transformMode, coverageByTestfiles] of Object.entries(coveragePerProject)) {
154
+ const filenames = Object.values(coverageByTestfiles);
155
+ const project = this.ctx.getProjectByName(projectName);
156
+ for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
157
+ if (onDebug.enabled) {
158
+ index += chunk.length;
159
+ onDebug("Covered files %d/%d", index, total);
134
160
  }
161
+ await Promise.all(
162
+ chunk.map(async (filename) => {
163
+ const contents = await promises.readFile(filename, "utf-8");
164
+ const coverage = JSON.parse(contents);
165
+ onFileRead(coverage);
166
+ })
167
+ );
135
168
  }
169
+ await onFinished(project, transformMode);
170
+ }
171
+ }
172
+ }
173
+ async cleanAfterRun() {
174
+ this.coverageFiles = /* @__PURE__ */ new Map();
175
+ await promises.rm(this.coverageFilesDirectory, { recursive: true });
176
+ if (readdirSync(this.options.reportsDirectory).length === 0) {
177
+ await promises.rm(this.options.reportsDirectory, { recursive: true });
178
+ }
179
+ }
180
+ async onTestFailure() {
181
+ if (!this.options.reportOnFailure) {
182
+ await this.cleanAfterRun();
183
+ }
184
+ }
185
+ async reportCoverage(coverageMap, { allTestsRun }) {
186
+ await this.generateReports(
187
+ coverageMap || this.createCoverageMap(),
188
+ allTestsRun
189
+ );
190
+ const keepResults = !this.options.cleanOnRerun && this.ctx.config.watch;
191
+ if (!keepResults) {
192
+ await this.cleanAfterRun();
193
+ }
194
+ }
195
+ async reportThresholds(coverageMap, allTestsRun) {
196
+ const resolvedThresholds = this.resolveThresholds(coverageMap);
197
+ this.checkThresholds(resolvedThresholds);
198
+ if (this.options.thresholds?.autoUpdate && allTestsRun) {
199
+ if (!this.ctx.server.config.configFile) {
200
+ throw new Error(
201
+ 'Missing configurationFile. The "coverage.thresholds.autoUpdate" can only be enabled when configuration file is used.'
202
+ );
136
203
  }
204
+ const configFilePath = this.ctx.server.config.configFile;
205
+ const configModule = await this.parseConfigModule(configFilePath);
206
+ await this.updateThresholds({
207
+ thresholds: resolvedThresholds,
208
+ configurationFile: configModule,
209
+ onUpdate: () => writeFileSync(
210
+ configFilePath,
211
+ configModule.generate().code,
212
+ "utf-8"
213
+ )
214
+ });
137
215
  }
138
216
  }
139
217
  /**
@@ -141,26 +219,19 @@ class BaseCoverageProvider {
141
219
  * where each threshold set holds their own coverage maps. Threshold set is either
142
220
  * for specific files defined by glob pattern or global for all other files.
143
221
  */
144
- resolveThresholds({
145
- coverageMap,
146
- thresholds,
147
- createCoverageMap,
148
- root
149
- }) {
222
+ resolveThresholds(coverageMap) {
150
223
  const resolvedThresholds = [];
151
224
  const files = coverageMap.files();
152
- const globalCoverageMap = createCoverageMap();
153
- for (const key of Object.keys(
154
- thresholds
155
- )) {
225
+ const globalCoverageMap = this.createCoverageMap();
226
+ for (const key of Object.keys(this.options.thresholds)) {
156
227
  if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) {
157
228
  continue;
158
229
  }
159
230
  const glob = key;
160
- const globThresholds = resolveGlobThresholds(thresholds[glob]);
161
- const globCoverageMap = createCoverageMap();
231
+ const globThresholds = resolveGlobThresholds(this.options.thresholds[glob]);
232
+ const globCoverageMap = this.createCoverageMap();
162
233
  const matchingFiles = files.filter(
163
- (file) => mm.isMatch(relative(root, file), glob)
234
+ (file) => mm.isMatch(relative(this.ctx.config.root, file), glob)
164
235
  );
165
236
  for (const file of matchingFiles) {
166
237
  const fileCoverage = coverageMap.fileCoverageFor(file);
@@ -180,19 +251,89 @@ class BaseCoverageProvider {
180
251
  name: GLOBAL_THRESHOLDS_KEY,
181
252
  coverageMap: globalCoverageMap,
182
253
  thresholds: {
183
- branches: thresholds.branches,
184
- functions: thresholds.functions,
185
- lines: thresholds.lines,
186
- statements: thresholds.statements
254
+ branches: this.options.thresholds?.branches,
255
+ functions: this.options.thresholds?.functions,
256
+ lines: this.options.thresholds?.lines,
257
+ statements: this.options.thresholds?.statements
187
258
  }
188
259
  });
189
260
  return resolvedThresholds;
190
261
  }
191
262
  /**
192
- * Resolve reporters from various configuration options
263
+ * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
264
+ */
265
+ checkThresholds(allThresholds) {
266
+ for (const { coverageMap, thresholds, name } of allThresholds) {
267
+ if (thresholds.branches === void 0 && thresholds.functions === void 0 && thresholds.lines === void 0 && thresholds.statements === void 0) {
268
+ continue;
269
+ }
270
+ const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => ({
271
+ file,
272
+ summary: coverageMap.fileCoverageFor(file).toSummary()
273
+ })) : [{ file: null, summary: coverageMap.getCoverageSummary() }];
274
+ for (const { summary, file } of summaries) {
275
+ for (const thresholdKey of THRESHOLD_KEYS) {
276
+ const threshold = thresholds[thresholdKey];
277
+ if (threshold !== void 0) {
278
+ const coverage = summary.data[thresholdKey].pct;
279
+ if (coverage < threshold) {
280
+ process.exitCode = 1;
281
+ let errorMessage = `ERROR: Coverage for ${thresholdKey} (${coverage}%) does not meet ${name === GLOBAL_THRESHOLDS_KEY ? name : `"${name}"`} threshold (${threshold}%)`;
282
+ if (this.options.thresholds?.perFile && file) {
283
+ errorMessage += ` for ${relative("./", file).replace(/\\/g, "/")}`;
284
+ }
285
+ this.ctx.logger.error(errorMessage);
286
+ }
287
+ }
288
+ }
289
+ }
290
+ }
291
+ }
292
+ /**
293
+ * Check if current coverage is above configured thresholds and bump the thresholds if needed
193
294
  */
194
- resolveReporters(configReporters) {
195
- return resolveCoverageReporters(configReporters);
295
+ async updateThresholds({ thresholds: allThresholds, onUpdate, configurationFile }) {
296
+ let updatedThresholds = false;
297
+ const config = resolveConfig(configurationFile);
298
+ assertConfigurationModule(config);
299
+ for (const { coverageMap, thresholds, name } of allThresholds) {
300
+ const summaries = this.options.thresholds?.perFile ? coverageMap.files().map(
301
+ (file) => coverageMap.fileCoverageFor(file).toSummary()
302
+ ) : [coverageMap.getCoverageSummary()];
303
+ const thresholdsToUpdate = [];
304
+ for (const key of THRESHOLD_KEYS) {
305
+ const threshold = thresholds[key] ?? 100;
306
+ const actual = Math.min(
307
+ ...summaries.map((summary) => summary[key].pct)
308
+ );
309
+ if (actual > threshold) {
310
+ thresholdsToUpdate.push([key, actual]);
311
+ }
312
+ }
313
+ if (thresholdsToUpdate.length === 0) {
314
+ continue;
315
+ }
316
+ updatedThresholds = true;
317
+ for (const [threshold, newValue] of thresholdsToUpdate) {
318
+ if (name === GLOBAL_THRESHOLDS_KEY) {
319
+ config.test.coverage.thresholds[threshold] = newValue;
320
+ } else {
321
+ const glob = config.test.coverage.thresholds[name];
322
+ glob[threshold] = newValue;
323
+ }
324
+ }
325
+ }
326
+ if (updatedThresholds) {
327
+ this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds.");
328
+ onUpdate();
329
+ }
330
+ }
331
+ async mergeReports(coverageMaps) {
332
+ const coverageMap = this.createCoverageMap();
333
+ for (const coverage of coverageMaps) {
334
+ coverageMap.merge(coverage);
335
+ }
336
+ await this.generateReports(coverageMap, true);
196
337
  }
197
338
  hasTerminalReporter(reporters) {
198
339
  return reporters.some(
package/dist/execute.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import vm from 'node:vm';
2
2
  import { ViteNodeRunner } from 'vite-node/client';
3
3
  import { ViteNodeRunnerOptions } from 'vite-node';
4
- import { R as RuntimeRPC, W as WorkerGlobalState } from './chunks/worker.Chrs-_NL.js';
4
+ import { R as RuntimeRPC, W as WorkerGlobalState } from './chunks/worker.B6RjTtbk.js';
5
5
  import * as _vitest_mocker from '@vitest/mocker';
6
6
  import { MockedModuleType } from '@vitest/mocker';
7
7
  import { P as PendingSuiteMock, b as MockFactory, a as MockOptions } from './chunks/mocker.cRtM890J.js';
package/dist/index.d.ts CHANGED
@@ -1,25 +1,25 @@
1
- import './chunks/vite.8fk186v-.js';
1
+ import './chunks/vite.YH7MrecS.js';
2
2
  import { Plugin } from '@vitest/pretty-format';
3
3
  import { SnapshotState } from '@vitest/snapshot';
4
4
  export { SnapshotData, SnapshotMatchOptions, SnapshotResult, SnapshotSerializer, SnapshotStateOptions, SnapshotSummary, SnapshotUpdateState, UncheckedSnapshot } from '@vitest/snapshot';
5
5
  import { PromisifyAssertion, Tester, ExpectStatic } from '@vitest/expect';
6
6
  export { Assertion, AsymmetricMatchersContaining, ExpectPollOptions, ExpectStatic, JestAssertion } from '@vitest/expect';
7
- import { e as VitestEnvironment$1, S as SerializedTestSpecification, f as RawErrsMap$1, T as TscErrorInfo$1, g as CollectLineNumbers$1, h as CollectLines$1, i as RootAndTarget$1, j as Context$1, C as CoverageProvider$1, k as ReportContext$1, a as CoverageProviderModule$1, l as CoverageReporter$1, m as CoverageProviderName, n as CoverageOptions$1, R as ResolvedCoverageOptions$1, B as BaseCoverageOptions$1, o as CoverageIstanbulOptions$1, b as CoverageV8Options$1, p as CustomProviderOptions$1, q as Reporter$1, V as Vitest$1, r as BrowserScript$1, s as BrowserConfigOptions$1, t as BuiltinEnvironment$1, P as Pool$1, u as PoolOptions$1, v as CSSModuleScopeStrategy$1, A as ApiConfig$1, w as VitestRunMode$1, D as DepsOptimizationOptions$1, x as TransformModePatterns$1, I as InlineConfig$1, y as TypecheckConfig$1, z as UserConfig$1, E as ResolvedConfig$1, F as ProjectConfig$1, U as UserWorkspaceConfig$1, G as BenchmarkUserOptions$1 } from './chunks/reporters.DAfKSDh5.js';
7
+ import { f as VitestEnvironment$1, S as SerializedTestSpecification, g as RawErrsMap$1, T as TscErrorInfo$1, h as CollectLineNumbers$1, i as CollectLines$1, j as RootAndTarget$1, k as Context$1, C as CoverageProvider$1, a as ReportContext$1, b as CoverageProviderModule$1, l as CoverageReporter$1, m as CoverageProviderName, n as CoverageOptions$1, R as ResolvedCoverageOptions$1, B as BaseCoverageOptions$1, o as CoverageIstanbulOptions$1, c as CoverageV8Options$1, p as CustomProviderOptions$1, q as Reporter$1, V as Vitest$1, r as BrowserScript$1, s as BrowserConfigOptions$1, t as BuiltinEnvironment$1, P as Pool$1, u as PoolOptions$1, v as CSSModuleScopeStrategy$1, A as ApiConfig$1, w as VitestRunMode$1, D as DepsOptimizationOptions$1, x as TransformModePatterns$1, I as InlineConfig$1, y as TypecheckConfig$1, z as UserConfig$1, E as ResolvedConfig$1, F as ProjectConfig$1, U as UserWorkspaceConfig$1, G as BenchmarkUserOptions$1 } from './chunks/reporters.C4ZHgdxQ.js';
8
8
  import { B as BenchmarkResult } from './chunks/benchmark.JVlTzojj.js';
9
9
  export { b as BenchFunction, a as Benchmark, c as BenchmarkAPI } from './chunks/benchmark.JVlTzojj.js';
10
10
  import { U as UserConsoleLog, P as ProvidedContext, M as ModuleGraphData, b as Awaitable$1, N as Nullable$1, c as Arrayable$1, d as ArgumentsType$1, e as MutableArray$1, C as Constructable$1, O as OnServerRestartHandler$1, a as EnvironmentReturn$1, V as VmEnvironmentReturn$1, E as Environment$1, R as ResolvedTestEnvironment$1, J as JSDOMOptions$1, H as HappyDOMOptions$1, f as EnvironmentOptions$1 } from './chunks/environment.CzISCQ7o.js';
11
11
  export { A as AfterSuiteRunMeta, g as ModuleCache } from './chunks/environment.CzISCQ7o.js';
12
- import { TaskPopulated, TaskResultPack, File as File$1, Suite as Suite$1, Test as Test$1, Custom as Custom$1, Task as Task$1, SequenceHooks as SequenceHooks$1, SequenceSetupFiles as SequenceSetupFiles$1 } from '@vitest/runner';
13
- export { CancelReason, DoneCallback, ExtendedContext, HookCleanupCallback, HookListener, OnTestFailedHandler, RunMode, Custom as RunnerCustomCase, Task as RunnerTask, Test as RunnerTestCase, File as RunnerTestFile, Suite as RunnerTestSuite, RuntimeContext, SuiteAPI, SuiteCollector, SuiteFactory, SuiteHooks, TaskBase, TaskContext, TaskCustomOptions, TaskMeta, TaskResult, TaskResultPack, TaskState, TestAPI, TestContext, TestFunction, TestOptions, afterAll, afterEach, beforeAll, beforeEach, describe, it, onTestFailed, onTestFinished, suite, test } from '@vitest/runner';
12
+ import { TaskPopulated, TaskResultPack as TaskResultPack$1, File as File$1, Suite as Suite$1, Test as Test$1, Custom as Custom$1, Task as Task$1, TaskBase as TaskBase$1, TaskResult as TaskResult$1, DoneCallback as DoneCallback$1, RuntimeContext as RuntimeContext$1, SuiteHooks as SuiteHooks$1, SequenceHooks as SequenceHooks$1, SequenceSetupFiles as SequenceSetupFiles$1 } from '@vitest/runner';
13
+ export { CancelReason, ExtendedContext, HookCleanupCallback, HookListener, OnTestFailedHandler, OnTestFinishedHandler, RunMode, Custom as RunnerCustomCase, Task as RunnerTask, TaskBase as RunnerTaskBase, TaskResult as RunnerTaskResult, TaskResultPack as RunnerTaskResultPack, Test as RunnerTestCase, File as RunnerTestFile, Suite as RunnerTestSuite, SuiteAPI, SuiteCollector, SuiteFactory, TaskContext, TaskCustomOptions, TaskMeta, TaskState, TestAPI, TestContext, TestFunction, TestOptions, afterAll, afterEach, beforeAll, beforeEach, describe, it, onTestFailed, onTestFinished, suite, test } from '@vitest/runner';
14
+ import { a as BirpcReturn, b as WorkerRPC$1 } from './chunks/worker.B6RjTtbk.js';
15
+ export { C as ContextRPC, e as ContextTestEnvironment, d as ResolveIdFunction, c as RunnerRPC, R as RuntimeRPC, W as WorkerGlobalState } from './chunks/worker.B6RjTtbk.js';
16
+ import { W as WorkerContext$1 } from './chunks/worker.CcJLfX8w.js';
14
17
  export { b as bench } from './chunks/suite.BMWOKiTe.js';
15
18
  import { F as FakeTimerInstallOpts, R as RuntimeOptions, S as SerializedConfig } from './chunks/config.Crbj2GAb.js';
16
19
  export { b as RuntimeConfig, a as SerializedCoverageConfig } from './chunks/config.Crbj2GAb.js';
17
20
  import { M as MockFactoryWithHelper, a as MockOptions } from './chunks/mocker.cRtM890J.js';
18
21
  import { spyOn, fn, MaybeMockedDeep, MaybeMocked, MaybePartiallyMocked, MaybePartiallyMockedDeep, MockInstance } from '@vitest/spy';
19
22
  export { Mock, MockContext, MockInstance, Mocked, MockedClass, MockedFunction, MockedObject } from '@vitest/spy';
20
- import { a as BirpcReturn } from './chunks/worker.Chrs-_NL.js';
21
- export { C as ContextRPC, e as ContextTestEnvironment, c as ResolveIdFunction, b as RunnerRPC, R as RuntimeRPC, W as WorkerGlobalState, d as WorkerRPC } from './chunks/worker.Chrs-_NL.js';
22
- export { W as WorkerContext } from './chunks/worker.Qtv8v5nL.js';
23
23
  export { ErrorWithDiff, ParsedStack, SerializedError, TestError } from '@vitest/utils';
24
24
  export { DiffOptions } from '@vitest/utils/diff';
25
25
  import * as chai from 'chai';
@@ -35,9 +35,9 @@ import 'vite-node/server';
35
35
  import '@vitest/utils/source-map';
36
36
  import 'node:fs';
37
37
  import '@vitest/runner/utils';
38
+ import 'node:worker_threads';
38
39
  import '@vitest/snapshot/environment';
39
40
  import '@vitest/mocker';
40
- import 'node:worker_threads';
41
41
 
42
42
  declare global {
43
43
  namespace Chai {
@@ -1508,7 +1508,7 @@ interface VitestUtils {
1508
1508
  declare const vitest: VitestUtils;
1509
1509
  declare const vi: VitestUtils;
1510
1510
 
1511
- declare function getRunningMode(): "run" | "watch";
1511
+ declare function getRunningMode(): "watch" | "run";
1512
1512
  declare function isWatchMode(): boolean;
1513
1513
 
1514
1514
  /**
@@ -1543,7 +1543,7 @@ interface TransformResultWithSource {
1543
1543
  source?: string;
1544
1544
  }
1545
1545
  interface WebSocketHandlers {
1546
- onTaskUpdate: (packs: TaskResultPack[]) => void;
1546
+ onTaskUpdate: (packs: TaskResultPack$1[]) => void;
1547
1547
  getFiles: () => File$1[];
1548
1548
  getTestFiles: () => Promise<SerializedTestSpecification[]>;
1549
1549
  getPaths: () => string[];
@@ -1559,7 +1559,7 @@ interface WebSocketHandlers {
1559
1559
  interface WebSocketEvents {
1560
1560
  onCollected?: (files?: File$1[]) => Awaitable$1<void>;
1561
1561
  onFinished?: (files: File$1[], errors: unknown[], coverage?: unknown) => Awaitable$1<void>;
1562
- onTaskUpdate?: (packs: TaskResultPack[]) => Awaitable$1<void>;
1562
+ onTaskUpdate?: (packs: TaskResultPack$1[]) => Awaitable$1<void>;
1563
1563
  onUserConsoleLog?: (log: UserConsoleLog) => Awaitable$1<void>;
1564
1564
  onPathsCollected?: (paths?: string[]) => Awaitable$1<void>;
1565
1565
  onSpecsCollected?: (specs?: SerializedTestSpecification[]) => Awaitable$1<void>;
@@ -1589,6 +1589,23 @@ type Test = Test$1;
1589
1589
  type Custom = Custom$1;
1590
1590
  /** @deprecated use `RunnerTask` instead */
1591
1591
  type Task = Task$1;
1592
+ /** @deprecated use `RunnerTaskBase` instead */
1593
+ type TaskBase = TaskBase$1;
1594
+ /** @deprecated use `RunnerTaskResult` instead */
1595
+ type TaskResult = TaskResult$1;
1596
+ /** @deprecated use `RunnerTaskResultPack` instead */
1597
+ type TaskResultPack = TaskResultPack$1;
1598
+ /** @deprecated don't use `DoneCallback` since it's not supported */
1599
+ type DoneCallback = DoneCallback$1;
1600
+ /** @deprecated internal type, don't use it */
1601
+ type RuntimeContext = RuntimeContext$1;
1602
+ /** @deprecated internal type, don't use it */
1603
+ type SuiteHooks = SuiteHooks$1;
1604
+
1605
+ /** @deprecated import from `vitest/node` instead */
1606
+ type WorkerContext = WorkerContext$1;
1607
+ /** @deprecated import from `vitest/node` instead */
1608
+ type WorkerRPC = WorkerRPC$1;
1592
1609
 
1593
1610
  /** @deprecated do not use, internal helper */
1594
1611
  type Awaitable<T> = Awaitable$1<T>;
@@ -1690,4 +1707,4 @@ type SerializableSpec = SerializedTestSpecification;
1690
1707
  /** @deprecated import from `vitest/node` instead */
1691
1708
  type BenchmarkUserOptions = BenchmarkUserOptions$1;
1692
1709
 
1693
- export { type ApiConfig, type ArgumentsType, type Arrayable, type AssertType, type Awaitable, type BaseCoverageOptions, BenchmarkResult, type BenchmarkUserOptions, type BrowserConfigOptions, type BrowserScript, type BrowserUI, type BuiltinEnvironment, type CSSModuleScopeStrategy, type CollectLineNumbers, type CollectLines, type Constructable, type Context, type CoverageIstanbulOptions, type CoverageOptions, type CoverageProvider, type CoverageProviderModule, type CoverageReporter, type CoverageV8Options, type Custom, type CustomProviderOptions, type DepsOptimizationOptions, type Environment, type EnvironmentOptions, type EnvironmentReturn, type ExpectTypeOf, type File, type HappyDOMOptions, type InlineConfig, type JSDOMOptions, ModuleGraphData, type MutableArray, type Nullable, type OnServerRestartHandler, type Pool, type PoolOptions, type ProjectConfig, ProvidedContext, type RawErrsMap, type ReportContext, type Reporter, type ResolvedConfig, type ResolvedCoverageOptions, type ResolvedTestEnvironment, type RootAndTarget, type SequenceHooks, type SequenceSetupFiles, type SerializableSpec, SerializedConfig, SerializedTestSpecification, type Suite, type Task, type Test, type TransformModePatterns, type TransformResultWithSource, type TscErrorInfo, type TypecheckConfig, type UserConfig, UserConsoleLog, type UserWorkspaceConfig, type Vitest, type VitestEnvironment, type VitestRunMode, type VitestUtils, type VmEnvironmentReturn, type WebSocketEvents, type WebSocketHandlers, type WebSocketRPC, assertType, createExpect, globalExpect as expect, expectTypeOf, getRunningMode, inject, isFirstRun, isWatchMode, runOnce, vi, vitest };
1710
+ export { type ApiConfig, type ArgumentsType, type Arrayable, type AssertType, type Awaitable, type BaseCoverageOptions, BenchmarkResult, type BenchmarkUserOptions, type BrowserConfigOptions, type BrowserScript, type BrowserUI, type BuiltinEnvironment, type CSSModuleScopeStrategy, type CollectLineNumbers, type CollectLines, type Constructable, type Context, type CoverageIstanbulOptions, type CoverageOptions, type CoverageProvider, type CoverageProviderModule, type CoverageReporter, type CoverageV8Options, type Custom, type CustomProviderOptions, type DepsOptimizationOptions, type DoneCallback, type Environment, type EnvironmentOptions, type EnvironmentReturn, type ExpectTypeOf, type File, type HappyDOMOptions, type InlineConfig, type JSDOMOptions, ModuleGraphData, type MutableArray, type Nullable, type OnServerRestartHandler, type Pool, type PoolOptions, type ProjectConfig, ProvidedContext, type RawErrsMap, type ReportContext, type Reporter, type ResolvedConfig, type ResolvedCoverageOptions, type ResolvedTestEnvironment, type RootAndTarget, type RuntimeContext, type SequenceHooks, type SequenceSetupFiles, type SerializableSpec, SerializedConfig, SerializedTestSpecification, type Suite, type SuiteHooks, type Task, type TaskBase, type TaskResult, type TaskResultPack, type Test, type TransformModePatterns, type TransformResultWithSource, type TscErrorInfo, type TypecheckConfig, type UserConfig, UserConsoleLog, type UserWorkspaceConfig, type Vitest, type VitestEnvironment, type VitestRunMode, type VitestUtils, type VmEnvironmentReturn, type WebSocketEvents, type WebSocketHandlers, type WebSocketRPC, type WorkerContext, type WorkerRPC, assertType, createExpect, globalExpect as expect, expectTypeOf, getRunningMode, inject, isFirstRun, isWatchMode, runOnce, vi, vitest };