vitest 2.1.2 → 2.1.4

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 (74) hide show
  1. package/LICENSE.md +320 -238
  2. package/dist/browser.d.ts +21 -21
  3. package/dist/browser.js +7 -7
  4. package/dist/chunks/{RandomSequencer.Bh5-tlNJ.js → RandomSequencer.CMRlh2v4.js} +143 -142
  5. package/dist/chunks/{base.tiemDJX6.js → base.C3xNdjV6.js} +3 -3
  6. package/dist/chunks/{benchmark.C8CRJYG4.js → benchmark.Cdu9hjj4.js} +2 -2
  7. package/dist/chunks/{benchmark.JVlTzojj.d.ts → benchmark.geERunq4.d.ts} +1 -1
  8. package/dist/chunks/{cac.B9PaPYY1.js → cac.DrfPaMvZ.js} +11 -10
  9. package/dist/chunks/{cli-api.CHxC4-U8.js → cli-api.CKrRYkw8.js} +3573 -3353
  10. package/dist/chunks/{console.DI3gHgtH.js → console.BYGVloWk.js} +3 -7
  11. package/dist/chunks/{coverage.zlNdAMHK.js → coverage.BoMDb1ip.js} +1 -1
  12. package/dist/chunks/{creator.Cf-MKt9i.js → creator.IIqd8RWT.js} +8 -8
  13. package/dist/chunks/{execute._eQQfgI8.js → execute.2pr0rHgK.js} +8 -6
  14. package/dist/chunks/{globals.HsM2o-0O.js → globals.Bp645TTJ.js} +8 -13
  15. package/dist/chunks/{index.BpSiYbpB.js → index.68735LiX.js} +28 -5
  16. package/dist/chunks/{index.BpojBOif.js → index.BJDntFik.js} +10 -10
  17. package/dist/chunks/{index.FcPVJkIQ.js → index.Bn81VaWg.js} +3828 -3790
  18. package/dist/chunks/{index.CPD77dLA.js → index.CqYx2Nsr.js} +7 -7
  19. package/dist/chunks/{index.Ckn0Cw1h.js → index.D3d79vc8.js} +6 -6
  20. package/dist/chunks/index.Dqe5k2Rk.js +54 -0
  21. package/dist/chunks/{node.Bx4JZjMG.js → node.AKq966Jp.js} +1 -5
  22. package/dist/chunks/{reporters.DAfKSDh5.d.ts → reporters.anwo7Y6a.d.ts} +1159 -1144
  23. package/dist/chunks/{resolveConfig.D1DENLPF.js → resolveConfig.DPmbhVlP.js} +2610 -2574
  24. package/dist/chunks/{rpc.B7Mfb-Yf.js → rpc.C3q9uwRX.js} +2 -2
  25. package/dist/chunks/{run-once.Sxe67Wng.js → run-once.2ogXb3JV.js} +1 -1
  26. package/dist/chunks/{runBaseTests.D-Gcin7G.js → runBaseTests.Dm-659zB.js} +21 -25
  27. package/dist/chunks/{setup-common.DF96bIYE.js → setup-common.DDmVKp6O.js} +3 -3
  28. package/dist/chunks/{suite.BMWOKiTe.d.ts → suite.B2jumIFP.d.ts} +2 -2
  29. package/dist/chunks/{utils.CY6Spixo.js → utils.BB4zjzR8.js} +2 -2
  30. package/dist/chunks/utils.C8RiOc4B.js +77 -0
  31. package/dist/chunks/{vi.DUs2eKik.js → vi.JMQoNY_Z.js} +94 -139
  32. package/dist/chunks/{vite.8fk186v-.d.ts → vite.BdBj-UWY.d.ts} +1 -1
  33. package/dist/chunks/{vm.CPXwWp4C.js → vm.jpyrB0xy.js} +5 -8
  34. package/dist/chunks/{worker.Chrs-_NL.d.ts → worker.BAlI9hII.d.ts} +50 -27
  35. package/dist/chunks/{worker.Qtv8v5nL.d.ts → worker.DHnGaO2M.d.ts} +1 -1
  36. package/dist/cli.js +3 -4
  37. package/dist/config.cjs +4 -4
  38. package/dist/config.d.ts +24 -19
  39. package/dist/config.js +4 -4
  40. package/dist/coverage.d.ts +72 -35
  41. package/dist/coverage.js +256 -118
  42. package/dist/environments.d.ts +2 -2
  43. package/dist/environments.js +1 -1
  44. package/dist/execute.d.ts +53 -53
  45. package/dist/execute.js +7 -6
  46. package/dist/index.d.ts +92 -1099
  47. package/dist/index.js +7 -18
  48. package/dist/node.d.ts +43 -41
  49. package/dist/node.js +23 -28
  50. package/dist/path.js +1 -1
  51. package/dist/reporters.d.ts +10 -10
  52. package/dist/reporters.js +12 -18
  53. package/dist/runners.d.ts +12 -19
  54. package/dist/runners.js +142 -148
  55. package/dist/snapshot.js +2 -6
  56. package/dist/suite.d.ts +2 -2
  57. package/dist/suite.js +2 -6
  58. package/dist/worker.js +5 -6
  59. package/dist/workers/forks.js +9 -8
  60. package/dist/workers/runVmTests.js +16 -20
  61. package/dist/workers/threads.js +9 -8
  62. package/dist/workers/vmForks.js +9 -12
  63. package/dist/workers/vmThreads.js +9 -12
  64. package/dist/workers.d.ts +13 -13
  65. package/dist/workers.js +14 -18
  66. package/package.json +27 -27
  67. package/dist/chunks/base.DwXGwWst.js +0 -89
  68. package/dist/chunks/env.CmHVDJnw.js +0 -7
  69. package/dist/chunks/index.CxRxs566.js +0 -23
  70. package/dist/chunks/index.mAqbj9F9.js +0 -140
  71. package/dist/chunks/tasks.BZnCS9aT.js +0 -18
  72. package/dist/chunks/utils.Ck2hJTRs.js +0 -23
  73. package/dist/chunks/{environment.CzISCQ7o.d.ts → environment.LoooBwUu.d.ts} +24 -24
  74. package/dist/chunks/{index.lVXYBqEP.js → index.BMoXz_-n.js} +186 -186
package/dist/config.d.ts CHANGED
@@ -1,23 +1,23 @@
1
- import './chunks/vite.8fk186v-.js';
2
- import { ConfigEnv, UserConfig } from 'vite';
3
- export { ConfigEnv, Plugin, UserConfig, mergeConfig } from 'vite';
4
- import { R as ResolvedCoverageOptions, b as CoverageV8Options, U as UserWorkspaceConfig, c as UserProjectConfigFn, d as UserProjectConfigExport, W as WorkspaceProjectConfiguration } from './chunks/reporters.DAfKSDh5.js';
5
- import '@vitest/runner';
6
- import '@vitest/pretty-format';
1
+ import { UserConfig as UserConfig$1, ConfigEnv } from 'vite';
2
+ export { ConfigEnv, Plugin, UserConfig as ViteUserConfig, mergeConfig } from 'vite';
3
+ import { R as ResolvedCoverageOptions, c as CoverageV8Options, U as UserWorkspaceConfig, d as UserProjectConfigFn, e as UserProjectConfigExport, W as WorkspaceProjectConfiguration } from './chunks/reporters.anwo7Y6a.js';
4
+ import './chunks/vite.BdBj-UWY.js';
7
5
  import './chunks/config.Crbj2GAb.js';
6
+ import '@vitest/pretty-format';
7
+ import '@vitest/runner';
8
8
  import '@vitest/snapshot';
9
9
  import '@vitest/snapshot/environment';
10
10
  import 'vite-node';
11
- import './chunks/environment.CzISCQ7o.js';
11
+ import 'chai';
12
+ import './chunks/environment.LoooBwUu.js';
12
13
  import 'node:stream';
13
- import 'vite-node/client';
14
- import '@vitest/snapshot/manager';
15
- import 'vite-node/server';
16
14
  import '@vitest/utils';
17
15
  import '@vitest/utils/source-map';
16
+ import 'vite-node/client';
17
+ import 'vite-node/server';
18
+ import '@vitest/snapshot/manager';
18
19
  import 'node:fs';
19
- import 'chai';
20
- import './chunks/benchmark.JVlTzojj.js';
20
+ import './chunks/benchmark.geERunq4.js';
21
21
  import '@vitest/runner/utils';
22
22
  import 'tinybench';
23
23
 
@@ -71,12 +71,17 @@ declare const configDefaults: Readonly<{
71
71
  disableConsoleIntercept: false;
72
72
  }>;
73
73
 
74
- type UserConfigFnObject = (env: ConfigEnv) => UserConfig;
75
- type UserConfigFnPromise = (env: ConfigEnv) => Promise<UserConfig>;
76
- type UserConfigFn = (env: ConfigEnv) => UserConfig | Promise<UserConfig>;
77
- type UserConfigExport = UserConfig | Promise<UserConfig> | UserConfigFnObject | UserConfigFnPromise | UserConfigFn;
78
- declare function defineConfig(config: UserConfig): UserConfig;
79
- declare function defineConfig(config: Promise<UserConfig>): Promise<UserConfig>;
74
+ /**
75
+ * @deprecated Use `ViteUserConfig` instead
76
+ */
77
+ type UserConfig = UserConfig$1;
78
+
79
+ type UserConfigFnObject = (env: ConfigEnv) => UserConfig$1;
80
+ type UserConfigFnPromise = (env: ConfigEnv) => Promise<UserConfig$1>;
81
+ type UserConfigFn = (env: ConfigEnv) => UserConfig$1 | Promise<UserConfig$1>;
82
+ type UserConfigExport = UserConfig$1 | Promise<UserConfig$1> | UserConfigFnObject | UserConfigFnPromise | UserConfigFn;
83
+ declare function defineConfig(config: UserConfig$1): UserConfig$1;
84
+ declare function defineConfig(config: Promise<UserConfig$1>): Promise<UserConfig$1>;
80
85
  declare function defineConfig(config: UserConfigFnObject): UserConfigFnObject;
81
86
  declare function defineConfig(config: UserConfigExport): UserConfigExport;
82
87
  declare function defineProject(config: UserWorkspaceConfig): UserWorkspaceConfig;
@@ -85,4 +90,4 @@ declare function defineProject(config: UserProjectConfigFn): UserProjectConfigFn
85
90
  declare function defineProject(config: UserProjectConfigExport): UserProjectConfigExport;
86
91
  declare function defineWorkspace(config: WorkspaceProjectConfiguration[]): WorkspaceProjectConfiguration[];
87
92
 
88
- export { type UserConfigExport, type UserConfigFn, type UserConfigFnObject, type UserConfigFnPromise, UserProjectConfigExport, UserProjectConfigFn, UserWorkspaceConfig, WorkspaceProjectConfiguration, configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude, defineConfig, defineProject, defineWorkspace, extraInlineDeps };
93
+ export { type UserConfig, type UserConfigExport, type UserConfigFn, type UserConfigFnObject, type UserConfigFnPromise, UserProjectConfigExport, UserProjectConfigFn, UserWorkspaceConfig, WorkspaceProjectConfiguration, configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude, defineConfig, defineProject, defineWorkspace, extraInlineDeps };
package/dist/config.js CHANGED
@@ -2,10 +2,6 @@ import os from 'node:os';
2
2
  import { isCI } from 'std-env';
3
3
  export { mergeConfig } from 'vite';
4
4
 
5
- const isNode = typeof process < "u" && typeof process.stdout < "u" && !process.versions?.deno && !globalThis.window;
6
- const isDeno = typeof process < "u" && typeof process.stdout < "u" && process.versions?.deno !== void 0;
7
- (isNode || isDeno) && process.platform === "win32";
8
-
9
5
  const defaultBrowserPort = 63315;
10
6
  const extraInlineDeps = [
11
7
  /^(?!.*node_modules).*\.mjs$/,
@@ -14,6 +10,10 @@ const extraInlineDeps = [
14
10
  /vite\w*\/dist\/client\/env.mjs/
15
11
  ];
16
12
 
13
+ const isNode = typeof process < "u" && typeof process.stdout < "u" && !process.versions?.deno && !globalThis.window;
14
+ const isDeno = typeof process < "u" && typeof process.stdout < "u" && process.versions?.deno !== void 0;
15
+ (isNode || isDeno) && process.platform === "win32";
16
+
17
17
  const defaultInclude = ["**/*.{test,spec}.?(c|m)[jt]s?(x)"];
18
18
  const defaultExclude = [
19
19
  "**/node_modules/**",
@@ -1,21 +1,21 @@
1
1
  import * as vite from 'vite';
2
- import { B as BaseCoverageOptions, R as ResolvedCoverageOptions, V as Vitest } from './chunks/reporters.DAfKSDh5.js';
3
- import '@vitest/runner';
4
- import '@vitest/pretty-format';
2
+ import { R as ResolvedCoverageOptions, V as Vitest, a as ReportContext } from './chunks/reporters.anwo7Y6a.js';
3
+ import { A as AfterSuiteRunMeta } from './chunks/environment.LoooBwUu.js';
5
4
  import './chunks/config.Crbj2GAb.js';
5
+ import '@vitest/pretty-format';
6
+ import '@vitest/runner';
6
7
  import '@vitest/snapshot';
7
8
  import '@vitest/snapshot/environment';
8
9
  import 'vite-node';
9
- import './chunks/environment.CzISCQ7o.js';
10
+ import 'chai';
10
11
  import 'node:stream';
11
- import 'vite-node/client';
12
- import '@vitest/snapshot/manager';
13
- import 'vite-node/server';
14
12
  import '@vitest/utils';
15
13
  import '@vitest/utils/source-map';
14
+ import 'vite-node/client';
15
+ import 'vite-node/server';
16
+ import '@vitest/snapshot/manager';
16
17
  import 'node:fs';
17
- import 'chai';
18
- import './chunks/benchmark.JVlTzojj.js';
18
+ import './chunks/benchmark.geERunq4.js';
19
19
  import '@vitest/runner/utils';
20
20
  import 'tinybench';
21
21
 
@@ -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;
161
189
  /**
162
- * Resolve reporters from various configuration options
190
+ * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
191
+ */
192
+ private checkThresholds;
193
+ /**
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,9 @@
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 { c as coverageConfigDefaults, r as resolveCoverageReporters, m as mm } from './chunks/resolveConfig.DPmbhVlP.js';
3
+ import { resolve, relative } from 'pathe';
4
+ import c from 'tinyrainbow';
5
+ import '@vitest/utils';
3
6
  import 'node:path';
4
- import 'node:fs';
5
7
  import 'node:fs/promises';
6
8
  import 'node:process';
7
9
  import 'node:module';
@@ -9,29 +11,24 @@ import 'node:url';
9
11
  import 'node:assert';
10
12
  import 'node:v8';
11
13
  import 'node:util';
12
- import 'tinyrainbow';
13
14
  import './chunks/constants.fzPh7AOq.js';
14
15
  import 'node:os';
15
- import './chunks/env.CmHVDJnw.js';
16
+ import './chunks/RandomSequencer.CMRlh2v4.js';
16
17
  import 'std-env';
17
- import '@vitest/runner/utils';
18
- import './chunks/base.DwXGwWst.js';
19
- import '@vitest/utils';
20
- import 'node:crypto';
21
- import './chunks/RandomSequencer.Bh5-tlNJ.js';
22
18
  import 'node:perf_hooks';
19
+ import '@vitest/runner/utils';
23
20
  import '@vitest/utils/source-map';
24
21
  import 'tinyexec';
25
- import 'path';
26
- import 'fs';
27
- import 'module';
28
22
  import 'vite';
23
+ import 'fs';
29
24
  import 'vite-node/utils';
25
+ import 'node:crypto';
30
26
  import './chunks/_commonjsHelpers.BFTU3MAI.js';
31
27
  import 'util';
28
+ import 'path';
32
29
  import 'node:events';
30
+ import './chunks/index.68735LiX.js';
33
31
  import 'tinypool';
34
- import './chunks/index.BpSiYbpB.js';
35
32
  import 'node:worker_threads';
36
33
  import './path.js';
37
34
 
@@ -42,125 +39,196 @@ const THRESHOLD_KEYS = [
42
39
  "branches"
43
40
  ];
44
41
  const GLOBAL_THRESHOLDS_KEY = "global";
42
+ const DEFAULT_PROJECT = Symbol.for("default-project");
43
+ let uniqueId = 0;
45
44
  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
- }
45
+ ctx;
46
+ name;
47
+ version;
48
+ options;
49
+ coverageFiles = /* @__PURE__ */ new Map();
50
+ pendingPromises = [];
51
+ coverageFilesDirectory;
52
+ _initialize(ctx) {
53
+ this.ctx = ctx;
54
+ if (ctx.version !== this.version) {
55
+ ctx.logger.warn(
56
+ c.yellow(
57
+ `Loaded ${c.inverse(c.yellow(` vitest@${ctx.version} `))} and ${c.inverse(c.yellow(` @vitest/coverage-${this.name}@${this.version} `))}.
58
+ Running mixed versions is not supported and may lead into bugs
59
+ Update your dependencies and make sure the versions match.`
60
+ )
61
+ );
62
+ }
63
+ const config = ctx.config.coverage;
64
+ this.options = {
65
+ ...coverageConfigDefaults,
66
+ // User's options
67
+ ...config,
68
+ // Resolved fields
69
+ provider: this.name,
70
+ reportsDirectory: resolve(
71
+ ctx.config.root,
72
+ config.reportsDirectory || coverageConfigDefaults.reportsDirectory
73
+ ),
74
+ reporter: resolveCoverageReporters(
75
+ config.reporter || coverageConfigDefaults.reporter
76
+ ),
77
+ thresholds: config.thresholds && {
78
+ ...config.thresholds,
79
+ lines: config.thresholds["100"] ? 100 : config.thresholds.lines,
80
+ branches: config.thresholds["100"] ? 100 : config.thresholds.branches,
81
+ functions: config.thresholds["100"] ? 100 : config.thresholds.functions,
82
+ statements: config.thresholds["100"] ? 100 : config.thresholds.statements
83
83
  }
84
+ };
85
+ const shard = this.ctx.config.shard;
86
+ const tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
87
+ this.coverageFilesDirectory = resolve(
88
+ this.options.reportsDirectory,
89
+ tempDirectory
90
+ );
91
+ }
92
+ createCoverageMap() {
93
+ throw new Error("BaseReporter's createCoverageMap was not overwritten");
94
+ }
95
+ async generateReports(_, __) {
96
+ throw new Error("BaseReporter's generateReports was not overwritten");
97
+ }
98
+ async parseConfigModule(_) {
99
+ throw new Error("BaseReporter's parseConfigModule was not overwritten");
100
+ }
101
+ resolveOptions() {
102
+ return this.options;
103
+ }
104
+ async clean(clean = true) {
105
+ if (clean && existsSync(this.options.reportsDirectory)) {
106
+ await promises.rm(this.options.reportsDirectory, {
107
+ recursive: true,
108
+ force: true,
109
+ maxRetries: 10
110
+ });
84
111
  }
85
- if (updatedThresholds) {
86
- console.log(
87
- "Updating thresholds to configuration file. You may want to push with updated coverage thresholds."
88
- );
89
- onUpdate();
112
+ if (existsSync(this.coverageFilesDirectory)) {
113
+ await promises.rm(this.coverageFilesDirectory, {
114
+ recursive: true,
115
+ force: true,
116
+ maxRetries: 10
117
+ });
90
118
  }
119
+ await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
120
+ this.coverageFiles = /* @__PURE__ */ new Map();
121
+ this.pendingPromises = [];
91
122
  }
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
- }
123
+ onAfterSuiteRun({ coverage, transformMode, projectName, testFiles }) {
124
+ if (!coverage) {
125
+ return;
126
+ }
127
+ if (transformMode !== "web" && transformMode !== "ssr" && transformMode !== "browser") {
128
+ throw new Error(`Invalid transform mode: ${transformMode}`);
129
+ }
130
+ let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
131
+ if (!entry) {
132
+ entry = { web: {}, ssr: {}, browser: {} };
133
+ this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
134
+ }
135
+ const testFilenames = testFiles.join();
136
+ const filename = resolve(
137
+ this.coverageFilesDirectory,
138
+ `coverage-${uniqueId++}.json`
139
+ );
140
+ entry[transformMode][testFilenames] = filename;
141
+ const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
142
+ this.pendingPromises.push(promise);
143
+ }
144
+ async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
145
+ let index = 0;
146
+ const total = this.pendingPromises.length;
147
+ await Promise.all(this.pendingPromises);
148
+ this.pendingPromises = [];
149
+ for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) {
150
+ for (const [transformMode, coverageByTestfiles] of Object.entries(coveragePerProject)) {
151
+ const filenames = Object.values(coverageByTestfiles);
152
+ const project = this.ctx.getProjectByName(projectName);
153
+ for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
154
+ if (onDebug.enabled) {
155
+ index += chunk.length;
156
+ onDebug("Covered files %d/%d", index, total);
134
157
  }
158
+ await Promise.all(
159
+ chunk.map(async (filename) => {
160
+ const contents = await promises.readFile(filename, "utf-8");
161
+ const coverage = JSON.parse(contents);
162
+ onFileRead(coverage);
163
+ })
164
+ );
135
165
  }
166
+ await onFinished(project, transformMode);
136
167
  }
137
168
  }
138
169
  }
170
+ async cleanAfterRun() {
171
+ this.coverageFiles = /* @__PURE__ */ new Map();
172
+ await promises.rm(this.coverageFilesDirectory, { recursive: true });
173
+ if (readdirSync(this.options.reportsDirectory).length === 0) {
174
+ await promises.rm(this.options.reportsDirectory, { recursive: true });
175
+ }
176
+ }
177
+ async onTestFailure() {
178
+ if (!this.options.reportOnFailure) {
179
+ await this.cleanAfterRun();
180
+ }
181
+ }
182
+ async reportCoverage(coverageMap, { allTestsRun }) {
183
+ await this.generateReports(
184
+ coverageMap || this.createCoverageMap(),
185
+ allTestsRun
186
+ );
187
+ const keepResults = !this.options.cleanOnRerun && this.ctx.config.watch;
188
+ if (!keepResults) {
189
+ await this.cleanAfterRun();
190
+ }
191
+ }
192
+ async reportThresholds(coverageMap, allTestsRun) {
193
+ const resolvedThresholds = this.resolveThresholds(coverageMap);
194
+ this.checkThresholds(resolvedThresholds);
195
+ if (this.options.thresholds?.autoUpdate && allTestsRun) {
196
+ if (!this.ctx.server.config.configFile) {
197
+ throw new Error(
198
+ 'Missing configurationFile. The "coverage.thresholds.autoUpdate" can only be enabled when configuration file is used.'
199
+ );
200
+ }
201
+ const configFilePath = this.ctx.server.config.configFile;
202
+ const configModule = await this.parseConfigModule(configFilePath);
203
+ await this.updateThresholds({
204
+ thresholds: resolvedThresholds,
205
+ configurationFile: configModule,
206
+ onUpdate: () => writeFileSync(
207
+ configFilePath,
208
+ configModule.generate().code,
209
+ "utf-8"
210
+ )
211
+ });
212
+ }
213
+ }
139
214
  /**
140
215
  * Constructs collected coverage and users' threshold options into separate sets
141
216
  * where each threshold set holds their own coverage maps. Threshold set is either
142
217
  * for specific files defined by glob pattern or global for all other files.
143
218
  */
144
- resolveThresholds({
145
- coverageMap,
146
- thresholds,
147
- createCoverageMap,
148
- root
149
- }) {
219
+ resolveThresholds(coverageMap) {
150
220
  const resolvedThresholds = [];
151
221
  const files = coverageMap.files();
152
- const globalCoverageMap = createCoverageMap();
153
- for (const key of Object.keys(
154
- thresholds
155
- )) {
222
+ const globalCoverageMap = this.createCoverageMap();
223
+ for (const key of Object.keys(this.options.thresholds)) {
156
224
  if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) {
157
225
  continue;
158
226
  }
159
227
  const glob = key;
160
- const globThresholds = resolveGlobThresholds(thresholds[glob]);
161
- const globCoverageMap = createCoverageMap();
228
+ const globThresholds = resolveGlobThresholds(this.options.thresholds[glob]);
229
+ const globCoverageMap = this.createCoverageMap();
162
230
  const matchingFiles = files.filter(
163
- (file) => mm.isMatch(relative(root, file), glob)
231
+ (file) => mm.isMatch(relative(this.ctx.config.root, file), glob)
164
232
  );
165
233
  for (const file of matchingFiles) {
166
234
  const fileCoverage = coverageMap.fileCoverageFor(file);
@@ -180,19 +248,89 @@ class BaseCoverageProvider {
180
248
  name: GLOBAL_THRESHOLDS_KEY,
181
249
  coverageMap: globalCoverageMap,
182
250
  thresholds: {
183
- branches: thresholds.branches,
184
- functions: thresholds.functions,
185
- lines: thresholds.lines,
186
- statements: thresholds.statements
251
+ branches: this.options.thresholds?.branches,
252
+ functions: this.options.thresholds?.functions,
253
+ lines: this.options.thresholds?.lines,
254
+ statements: this.options.thresholds?.statements
187
255
  }
188
256
  });
189
257
  return resolvedThresholds;
190
258
  }
191
259
  /**
192
- * Resolve reporters from various configuration options
260
+ * Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
261
+ */
262
+ checkThresholds(allThresholds) {
263
+ for (const { coverageMap, thresholds, name } of allThresholds) {
264
+ if (thresholds.branches === void 0 && thresholds.functions === void 0 && thresholds.lines === void 0 && thresholds.statements === void 0) {
265
+ continue;
266
+ }
267
+ const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => ({
268
+ file,
269
+ summary: coverageMap.fileCoverageFor(file).toSummary()
270
+ })) : [{ file: null, summary: coverageMap.getCoverageSummary() }];
271
+ for (const { summary, file } of summaries) {
272
+ for (const thresholdKey of THRESHOLD_KEYS) {
273
+ const threshold = thresholds[thresholdKey];
274
+ if (threshold !== void 0) {
275
+ const coverage = summary.data[thresholdKey].pct;
276
+ if (coverage < threshold) {
277
+ process.exitCode = 1;
278
+ let errorMessage = `ERROR: Coverage for ${thresholdKey} (${coverage}%) does not meet ${name === GLOBAL_THRESHOLDS_KEY ? name : `"${name}"`} threshold (${threshold}%)`;
279
+ if (this.options.thresholds?.perFile && file) {
280
+ errorMessage += ` for ${relative("./", file).replace(/\\/g, "/")}`;
281
+ }
282
+ this.ctx.logger.error(errorMessage);
283
+ }
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
289
+ /**
290
+ * Check if current coverage is above configured thresholds and bump the thresholds if needed
193
291
  */
194
- resolveReporters(configReporters) {
195
- return resolveCoverageReporters(configReporters);
292
+ async updateThresholds({ thresholds: allThresholds, onUpdate, configurationFile }) {
293
+ let updatedThresholds = false;
294
+ const config = resolveConfig(configurationFile);
295
+ assertConfigurationModule(config);
296
+ for (const { coverageMap, thresholds, name } of allThresholds) {
297
+ const summaries = this.options.thresholds?.perFile ? coverageMap.files().map(
298
+ (file) => coverageMap.fileCoverageFor(file).toSummary()
299
+ ) : [coverageMap.getCoverageSummary()];
300
+ const thresholdsToUpdate = [];
301
+ for (const key of THRESHOLD_KEYS) {
302
+ const threshold = thresholds[key] ?? 100;
303
+ const actual = Math.min(
304
+ ...summaries.map((summary) => summary[key].pct)
305
+ );
306
+ if (actual > threshold) {
307
+ thresholdsToUpdate.push([key, actual]);
308
+ }
309
+ }
310
+ if (thresholdsToUpdate.length === 0) {
311
+ continue;
312
+ }
313
+ updatedThresholds = true;
314
+ for (const [threshold, newValue] of thresholdsToUpdate) {
315
+ if (name === GLOBAL_THRESHOLDS_KEY) {
316
+ config.test.coverage.thresholds[threshold] = newValue;
317
+ } else {
318
+ const glob = config.test.coverage.thresholds[name];
319
+ glob[threshold] = newValue;
320
+ }
321
+ }
322
+ }
323
+ if (updatedThresholds) {
324
+ this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds.");
325
+ onUpdate();
326
+ }
327
+ }
328
+ async mergeReports(coverageMaps) {
329
+ const coverageMap = this.createCoverageMap();
330
+ for (const coverage of coverageMaps) {
331
+ coverageMap.merge(coverage);
332
+ }
333
+ await this.generateReports(coverageMap, true);
196
334
  }
197
335
  hasTerminalReporter(reporters) {
198
336
  return reporters.some(