vitest 3.2.0-beta.2 → 3.2.0-beta.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.
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +1 -1
- package/dist/chunks/{base.DwtwORaC.js → base.D4119yLM.js} +4 -3
- package/dist/chunks/{benchmark.BoF7jW0Q.js → benchmark.Cf_PACH1.js} +1 -1
- package/dist/chunks/{cac.I9MLYfT-.js → cac.DWaWHIIE.js} +18 -15
- package/dist/chunks/{cli-api.d6IK1pnk.js → cli-api.CnmEXkxs.js} +250 -49
- package/dist/chunks/{config.d.UqE-KR0o.d.ts → config.d.D2ROskhv.d.ts} +2 -0
- package/dist/chunks/{console.K1NMVOSc.js → console.Cwr-MFPV.js} +3 -2
- package/dist/chunks/{constants.BZZyIeIE.js → constants.DnKduX2e.js} +1 -0
- package/dist/chunks/{coverage.OGU09Jbh.js → coverage.C73DaDgS.js} +116 -12
- package/dist/chunks/{creator.DGAdZ4Hj.js → creator.C8WKy2eW.js} +10 -7
- package/dist/chunks/{date.CDOsz-HY.js → date.ByMsSlOr.js} +25 -0
- package/dist/chunks/{defaults.DSxsTG0h.js → defaults.DpVH7vbg.js} +1 -0
- package/dist/chunks/{environment.d.D8YDy2v5.d.ts → environment.d.cL3nLXbE.d.ts} +1 -0
- package/dist/chunks/{execute.JlGHLJZT.js → execute.B3q-2LPV.js} +25 -0
- package/dist/chunks/{global.d.BPa1eL3O.d.ts → global.d.BNLIi6yo.d.ts} +3 -1
- package/dist/chunks/{globals.CpxW8ccg.js → globals.CI21aWXF.js} +7 -6
- package/dist/chunks/{index.DFXFpH3w.js → index.2jgTs_Q5.js} +19 -1
- package/dist/chunks/{index.CV36oG_L.js → index.Bter3jj9.js} +83 -16
- package/dist/chunks/{index.DswW_LEs.js → index.CbT4iuwc.js} +7 -4
- package/dist/chunks/index.D3XRDfWc.js +213 -0
- package/dist/chunks/{index.CfXMNXHg.js → index.DNgLEKsQ.js} +4 -2
- package/dist/chunks/{index.CmC5OK9L.js → index.JOzufsrU.js} +2 -1
- package/dist/chunks/{inspector.DbDkSkFn.js → inspector.BFsh5KO0.js} +3 -0
- package/dist/chunks/{node.3xsWotC9.js → node.Be-ntJnD.js} +1 -1
- package/dist/chunks/{reporters.d.CLC9rhKy.d.ts → reporters.d.Bt4IGtsa.d.ts} +24 -6
- package/dist/chunks/{rpc.D9_013TY.js → rpc.BKExFSRG.js} +2 -1
- package/dist/chunks/{runBaseTests.Dn2vyej_.js → runBaseTests.B_M1TTsK.js} +19 -10
- package/dist/chunks/{setup-common.CYo3Y0dD.js → setup-common.CF-O-dZX.js} +2 -1
- package/dist/chunks/{typechecker.DnTrplSJ.js → typechecker.BgzF-6iO.js} +78 -21
- package/dist/chunks/{utils.CgTj3MsC.js → utils.BlI4TC7Y.js} +1 -0
- package/dist/chunks/{utils.BfxieIyZ.js → utils.DPCq3gzW.js} +3 -0
- package/dist/chunks/{vi.BFR5YIgu.js → vi.pkoYCV6A.js} +25 -2
- package/dist/chunks/{vite.d.CBZ3M_ru.d.ts → vite.d.B-Kx3KCF.d.ts} +3 -1
- package/dist/chunks/{vm.C1HHjtNS.js → vm.DPYem2so.js} +72 -4
- package/dist/chunks/{worker.d.CoCI7hzP.d.ts → worker.d.BKbBp2ga.d.ts} +2 -2
- package/dist/chunks/{worker.d.D5Xdi-Zr.d.ts → worker.d.Bl1O4kuf.d.ts} +1 -1
- package/dist/cli.js +4 -4
- package/dist/config.cjs +2 -0
- package/dist/config.d.ts +7 -6
- package/dist/config.js +2 -2
- package/dist/coverage.d.ts +4 -4
- package/dist/coverage.js +5 -5
- package/dist/environments.d.ts +6 -2
- package/dist/environments.js +1 -1
- package/dist/execute.d.ts +9 -3
- package/dist/execute.js +1 -1
- package/dist/index.d.ts +24 -12
- package/dist/index.js +5 -5
- package/dist/node.d.ts +18 -10
- package/dist/node.js +14 -12
- package/dist/reporters.d.ts +4 -4
- package/dist/reporters.js +3 -3
- package/dist/runners.d.ts +1 -1
- package/dist/runners.js +13 -5
- package/dist/snapshot.js +2 -2
- package/dist/suite.js +2 -2
- package/dist/worker.js +9 -5
- package/dist/workers/forks.js +6 -4
- package/dist/workers/runVmTests.js +14 -9
- package/dist/workers/threads.js +4 -4
- package/dist/workers/vmForks.js +6 -6
- package/dist/workers/vmThreads.js +6 -6
- package/dist/workers.d.ts +4 -4
- package/dist/workers.js +10 -10
- package/package.json +18 -18
- package/dist/chunks/index.CK1YOQaa.js +0 -143
|
@@ -2,27 +2,27 @@ import fs, { statSync, realpathSync, promises as promises$1, mkdirSync, existsSy
|
|
|
2
2
|
import { relative, resolve, dirname, isAbsolute, join as join$1, normalize } from 'pathe';
|
|
3
3
|
import pm from 'picomatch';
|
|
4
4
|
import c from 'tinyrainbow';
|
|
5
|
-
import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.
|
|
5
|
+
import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.DpVH7vbg.js';
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
7
|
import { slash, createDefer, shuffle, toArray } from '@vitest/utils';
|
|
8
8
|
import { builtinModules, createRequire } from 'node:module';
|
|
9
9
|
import path, { win32, dirname as dirname$1, join, resolve as resolve$1 } from 'node:path';
|
|
10
10
|
import process$1 from 'node:process';
|
|
11
|
-
import
|
|
11
|
+
import fs$1, { writeFile, rename, stat, unlink } from 'node:fs/promises';
|
|
12
12
|
import { fileURLToPath as fileURLToPath$1, pathToFileURL as pathToFileURL$1, URL as URL$1 } from 'node:url';
|
|
13
13
|
import assert from 'node:assert';
|
|
14
14
|
import v8 from 'node:v8';
|
|
15
15
|
import { format, inspect } from 'node:util';
|
|
16
|
-
import {
|
|
16
|
+
import { version, mergeConfig } from 'vite';
|
|
17
|
+
import { e as extraInlineDeps, d as defaultBrowserPort, b as defaultInspectPort, a as defaultPort } from './constants.DnKduX2e.js';
|
|
17
18
|
import { a as isWindows } from './env.Dq0hM4Xv.js';
|
|
18
19
|
import * as nodeos from 'node:os';
|
|
19
20
|
import nodeos__default from 'node:os';
|
|
20
21
|
import { isatty } from 'node:tty';
|
|
21
|
-
import { version } from 'vite';
|
|
22
22
|
import EventEmitter from 'node:events';
|
|
23
23
|
import { c as createBirpc } from './index.CJ0plNrh.js';
|
|
24
24
|
import Tinypool$1, { Tinypool } from 'tinypool';
|
|
25
|
-
import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.
|
|
25
|
+
import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.BgzF-6iO.js';
|
|
26
26
|
import { MessageChannel } from 'node:worker_threads';
|
|
27
27
|
import { hasFailed } from '@vitest/runner/utils';
|
|
28
28
|
import { rootDir } from '../path.js';
|
|
@@ -39,9 +39,12 @@ function groupBy(collection, iteratee) {
|
|
|
39
39
|
}, {});
|
|
40
40
|
}
|
|
41
41
|
function stdout() {
|
|
42
|
+
// @ts-expect-error Node.js maps process.stdout to console._stdout
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
42
44
|
return console._stdout || process.stdout;
|
|
43
45
|
}
|
|
44
46
|
function escapeRegExp(s) {
|
|
47
|
+
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
|
45
48
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
46
49
|
}
|
|
47
50
|
function wildcardPatternToRegExp(pattern) {
|
|
@@ -116,7 +119,9 @@ class ResultsCache {
|
|
|
116
119
|
}
|
|
117
120
|
const resultsCache = await fs.promises.readFile(this.cachePath, "utf8");
|
|
118
121
|
const { results, version } = JSON.parse(resultsCache || "[]");
|
|
119
|
-
|
|
122
|
+
const [major, minor] = version.split(".");
|
|
123
|
+
// handling changed in 0.30.0
|
|
124
|
+
if (major > 0 || Number(minor) >= 30) {
|
|
120
125
|
this.cache = new Map(results);
|
|
121
126
|
this.version = version;
|
|
122
127
|
results.forEach(([spec]) => {
|
|
@@ -134,6 +139,7 @@ class ResultsCache {
|
|
|
134
139
|
return;
|
|
135
140
|
}
|
|
136
141
|
const duration = result.duration || 0;
|
|
142
|
+
// store as relative, so cache would be the same in CI and locally
|
|
137
143
|
const relativePath = relative(this.root, file.filepath);
|
|
138
144
|
this.cache.set(`${file.projectName || ""}:${relativePath}`, {
|
|
139
145
|
duration: duration >= 0 ? duration : 0,
|
|
@@ -2363,7 +2369,7 @@ async function findUp$1(name, {
|
|
|
2363
2369
|
while (directory) {
|
|
2364
2370
|
const filePath = isAbsoluteName ? name : path.join(directory, name);
|
|
2365
2371
|
try {
|
|
2366
|
-
const stats = await
|
|
2372
|
+
const stats = await fs$1.stat(filePath); // eslint-disable-line no-await-in-loop
|
|
2367
2373
|
if ((type === 'file' && stats.isFile()) || (type === 'directory' && stats.isDirectory())) {
|
|
2368
2374
|
return filePath;
|
|
2369
2375
|
}
|
|
@@ -2528,7 +2534,9 @@ function getTransformMode(patterns, filename) {
|
|
|
2528
2534
|
async function groupFilesByEnv(files) {
|
|
2529
2535
|
const filesWithEnv = await Promise.all(files.map(async ({ moduleId: filepath, project, testLines }) => {
|
|
2530
2536
|
const code = await promises$1.readFile(filepath, "utf-8");
|
|
2537
|
+
// 1. Check for control comments in the file
|
|
2531
2538
|
let env = code.match(/@(?:vitest|jest)-environment\s+([\w-]+)\b/)?.[1];
|
|
2539
|
+
// 2. Check for globals
|
|
2532
2540
|
if (!env) {
|
|
2533
2541
|
for (const [glob, target] of project.config.environmentMatchGlobs || []) {
|
|
2534
2542
|
if (pm.isMatch(filepath, glob, { cwd: project.config.root })) {
|
|
@@ -2537,10 +2545,12 @@ async function groupFilesByEnv(files) {
|
|
|
2537
2545
|
}
|
|
2538
2546
|
}
|
|
2539
2547
|
}
|
|
2548
|
+
// 3. Fallback to global env
|
|
2540
2549
|
env ||= project.config.environment || "node";
|
|
2541
2550
|
const transformMode = getTransformMode(project.config.testTransformMode, filepath);
|
|
2542
2551
|
let envOptionsJson = code.match(/@(?:vitest|jest)-environment-options\s+(.+)/)?.[1];
|
|
2543
2552
|
if (envOptionsJson?.endsWith("*/")) {
|
|
2553
|
+
// Trim closing Docblock characters the above regex might have captured
|
|
2544
2554
|
envOptionsJson = envOptionsJson.slice(0, -2);
|
|
2545
2555
|
}
|
|
2546
2556
|
const envOptions = JSON.parse(envOptionsJson || "null");
|
|
@@ -2660,8 +2670,10 @@ function createMethodsRPC(project, options = {}) {
|
|
|
2660
2670
|
}
|
|
2661
2671
|
};
|
|
2662
2672
|
}
|
|
2673
|
+
// serialize rollup error on server to preserve details as a test error
|
|
2663
2674
|
function handleRollupError(e) {
|
|
2664
2675
|
if (e instanceof Error && ("plugin" in e || "frame" in e || "id" in e)) {
|
|
2676
|
+
// eslint-disable-next-line no-throw-literal
|
|
2665
2677
|
throw {
|
|
2666
2678
|
name: e.name,
|
|
2667
2679
|
message: e.message,
|
|
@@ -2790,6 +2802,7 @@ function createForksPool(ctx, { execArgv, env }) {
|
|
|
2790
2802
|
channel
|
|
2791
2803
|
});
|
|
2792
2804
|
} catch (error) {
|
|
2805
|
+
// Worker got stuck and won't terminate - this may cause process to hang
|
|
2793
2806
|
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) {
|
|
2794
2807
|
ctx.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}.`);
|
|
2795
2808
|
} else if (ctx.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) {
|
|
@@ -2802,6 +2815,7 @@ function createForksPool(ctx, { execArgv, env }) {
|
|
|
2802
2815
|
}
|
|
2803
2816
|
}
|
|
2804
2817
|
return async (specs, invalidates) => {
|
|
2818
|
+
// Cancel pending tasks from pool when possible
|
|
2805
2819
|
ctx.onCancel(() => pool.cancelPendingTasks());
|
|
2806
2820
|
const configs = new WeakMap();
|
|
2807
2821
|
const getConfig = (project) => {
|
|
@@ -2822,9 +2836,14 @@ function createForksPool(ctx, { execArgv, env }) {
|
|
|
2822
2836
|
if (isolated) {
|
|
2823
2837
|
results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
|
|
2824
2838
|
} else {
|
|
2839
|
+
// When isolation is disabled, we still need to isolate environments and workspace projects from each other.
|
|
2840
|
+
// Tasks are still running parallel but environments are isolated between tasks.
|
|
2825
2841
|
const grouped = groupBy(files, ({ project, environment }) => project.name + environment.name + JSON.stringify(environment.options));
|
|
2826
2842
|
for (const group of Object.values(grouped)) {
|
|
2843
|
+
// Push all files to pool's queue
|
|
2827
2844
|
results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
|
|
2845
|
+
// Once all tasks are running or finished, recycle worker for isolation.
|
|
2846
|
+
// On-going workers will run in the previous environment.
|
|
2828
2847
|
await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve));
|
|
2829
2848
|
await pool.recycleWorkers();
|
|
2830
2849
|
}
|
|
@@ -2844,6 +2863,7 @@ function createForksPool(ctx, { execArgv, env }) {
|
|
|
2844
2863
|
}
|
|
2845
2864
|
const filesByOptions = groupBy(files, ({ project, environment }) => project.name + JSON.stringify(environment.options));
|
|
2846
2865
|
for (const files of Object.values(filesByOptions)) {
|
|
2866
|
+
// Always run environments isolated between each other
|
|
2847
2867
|
await pool.recycleWorkers();
|
|
2848
2868
|
const filenames = files.map((f) => f.file);
|
|
2849
2869
|
await runFiles(files[0].project, getConfig(files[0].project), filenames, files[0].environment, invalidates);
|
|
@@ -2933,6 +2953,7 @@ function createThreadsPool(ctx, { execArgv, env }) {
|
|
|
2933
2953
|
name
|
|
2934
2954
|
});
|
|
2935
2955
|
} catch (error) {
|
|
2956
|
+
// Worker got stuck and won't terminate - this may cause process to hang
|
|
2936
2957
|
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) {
|
|
2937
2958
|
ctx.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}. \nSee https://vitest.dev/guide/common-errors.html#failed-to-terminate-worker for troubleshooting.`);
|
|
2938
2959
|
} else if (ctx.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) {
|
|
@@ -2946,6 +2967,7 @@ function createThreadsPool(ctx, { execArgv, env }) {
|
|
|
2946
2967
|
}
|
|
2947
2968
|
}
|
|
2948
2969
|
return async (specs, invalidates) => {
|
|
2970
|
+
// Cancel pending tasks from pool when possible
|
|
2949
2971
|
ctx.onCancel(() => pool.cancelPendingTasks());
|
|
2950
2972
|
const configs = new WeakMap();
|
|
2951
2973
|
const getConfig = (project) => {
|
|
@@ -2965,9 +2987,14 @@ function createThreadsPool(ctx, { execArgv, env }) {
|
|
|
2965
2987
|
if (isolated) {
|
|
2966
2988
|
results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
|
|
2967
2989
|
} else {
|
|
2990
|
+
// When isolation is disabled, we still need to isolate environments and workspace projects from each other.
|
|
2991
|
+
// Tasks are still running parallel but environments are isolated between tasks.
|
|
2968
2992
|
const grouped = groupBy(files, ({ project, environment }) => project.name + environment.name + JSON.stringify(environment.options));
|
|
2969
2993
|
for (const group of Object.values(grouped)) {
|
|
2994
|
+
// Push all files to pool's queue
|
|
2970
2995
|
results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
|
|
2996
|
+
// Once all tasks are running or finished, recycle worker for isolation.
|
|
2997
|
+
// On-going workers will run in the previous environment.
|
|
2971
2998
|
await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve));
|
|
2972
2999
|
await pool.recycleWorkers();
|
|
2973
3000
|
}
|
|
@@ -2987,6 +3014,7 @@ function createThreadsPool(ctx, { execArgv, env }) {
|
|
|
2987
3014
|
}
|
|
2988
3015
|
const filesByOptions = groupBy(files, ({ project, environment }) => project.name + JSON.stringify(environment.options));
|
|
2989
3016
|
for (const files of Object.values(filesByOptions)) {
|
|
3017
|
+
// Always run environments isolated between each other
|
|
2990
3018
|
await pool.recycleWorkers();
|
|
2991
3019
|
const filenames = files.map((f) => f.file);
|
|
2992
3020
|
await runFiles(files[0].project, getConfig(files[0].project), filenames, files[0].environment, invalidates);
|
|
@@ -3021,6 +3049,7 @@ function createTypecheckPool(vitest) {
|
|
|
3021
3049
|
}
|
|
3022
3050
|
promisesMap.get(project)?.resolve();
|
|
3023
3051
|
rerunTriggered.delete(project);
|
|
3052
|
+
// triggered by TSC watcher, not Vitest watcher, so we need to emulate what Vitest does in this case
|
|
3024
3053
|
if (vitest.config.watch && !vitest.runningPromise) {
|
|
3025
3054
|
await vitest.report("onFinished", files, []);
|
|
3026
3055
|
await vitest.report("onWatcherStart", files, [...project.config.typecheck.ignoreSourceErrors ? [] : sourceErrors, ...vitest.state.getUnhandledErrors()]);
|
|
@@ -3060,7 +3089,7 @@ function createTypecheckPool(vitest) {
|
|
|
3060
3089
|
}
|
|
3061
3090
|
async function startTypechecker(project, files) {
|
|
3062
3091
|
if (project.typechecker) {
|
|
3063
|
-
return
|
|
3092
|
+
return;
|
|
3064
3093
|
}
|
|
3065
3094
|
const checker = await createWorkspaceTypechecker(project, files);
|
|
3066
3095
|
await checker.collectTests();
|
|
@@ -3085,6 +3114,7 @@ function createTypecheckPool(vitest) {
|
|
|
3085
3114
|
const project = specsByProject[name][0].project;
|
|
3086
3115
|
const files = specsByProject[name].map((spec) => spec.moduleId);
|
|
3087
3116
|
const promise = createDefer();
|
|
3117
|
+
// check that watcher actually triggered rerun
|
|
3088
3118
|
const _p = new Promise((resolve) => {
|
|
3089
3119
|
const _i = setInterval(() => {
|
|
3090
3120
|
if (!project.typechecker || rerunTriggered.has(project)) {
|
|
@@ -3109,7 +3139,7 @@ function createTypecheckPool(vitest) {
|
|
|
3109
3139
|
}
|
|
3110
3140
|
promises.push(promise);
|
|
3111
3141
|
promisesMap.set(project, promise);
|
|
3112
|
-
startTypechecker(project, files);
|
|
3142
|
+
promises.push(startTypechecker(project, files));
|
|
3113
3143
|
}
|
|
3114
3144
|
await Promise.all(promises);
|
|
3115
3145
|
}
|
|
@@ -3285,6 +3315,7 @@ function createVmForksPool(ctx, { execArgv, env }) {
|
|
|
3285
3315
|
channel
|
|
3286
3316
|
});
|
|
3287
3317
|
} catch (error) {
|
|
3318
|
+
// Worker got stuck and won't terminate - this may cause process to hang
|
|
3288
3319
|
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) {
|
|
3289
3320
|
ctx.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}.`);
|
|
3290
3321
|
} else if (ctx.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) {
|
|
@@ -3297,6 +3328,7 @@ function createVmForksPool(ctx, { execArgv, env }) {
|
|
|
3297
3328
|
}
|
|
3298
3329
|
}
|
|
3299
3330
|
return async (specs, invalidates) => {
|
|
3331
|
+
// Cancel pending tasks from pool when possible
|
|
3300
3332
|
ctx.onCancel(() => pool.cancelPendingTasks());
|
|
3301
3333
|
const configs = new Map();
|
|
3302
3334
|
const getConfig = (project) => {
|
|
@@ -3330,9 +3362,11 @@ function getMemoryLimit$1(config) {
|
|
|
3330
3362
|
if (typeof memory === "number") {
|
|
3331
3363
|
return stringToBytes(limit, config.watch ? memory / 2 : memory);
|
|
3332
3364
|
}
|
|
3365
|
+
// If totalmem is not supported we cannot resolve percentage based values like 0.5, "50%"
|
|
3333
3366
|
if (typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%") {
|
|
3334
3367
|
return stringToBytes(limit);
|
|
3335
3368
|
}
|
|
3369
|
+
// just ignore "memoryLimit" value because we cannot detect memory limit
|
|
3336
3370
|
return null;
|
|
3337
3371
|
}
|
|
3338
3372
|
|
|
@@ -3414,6 +3448,7 @@ function createVmThreadsPool(ctx, { execArgv, env }) {
|
|
|
3414
3448
|
name
|
|
3415
3449
|
});
|
|
3416
3450
|
} catch (error) {
|
|
3451
|
+
// Worker got stuck and won't terminate - this may cause process to hang
|
|
3417
3452
|
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) {
|
|
3418
3453
|
ctx.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}. \nSee https://vitest.dev/guide/common-errors.html#failed-to-terminate-worker for troubleshooting.`);
|
|
3419
3454
|
} else if (ctx.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) {
|
|
@@ -3427,6 +3462,7 @@ function createVmThreadsPool(ctx, { execArgv, env }) {
|
|
|
3427
3462
|
}
|
|
3428
3463
|
}
|
|
3429
3464
|
return async (specs, invalidates) => {
|
|
3465
|
+
// Cancel pending tasks from pool when possible
|
|
3430
3466
|
ctx.onCancel(() => pool.cancelPendingTasks());
|
|
3431
3467
|
const configs = new Map();
|
|
3432
3468
|
const getConfig = (project) => {
|
|
@@ -3459,9 +3495,11 @@ function getMemoryLimit(config) {
|
|
|
3459
3495
|
if (typeof memory === "number") {
|
|
3460
3496
|
return stringToBytes(limit, config.watch ? memory / 2 : memory);
|
|
3461
3497
|
}
|
|
3498
|
+
// If totalmem is not supported we cannot resolve percentage based values like 0.5, "50%"
|
|
3462
3499
|
if (typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%") {
|
|
3463
3500
|
return stringToBytes(limit);
|
|
3464
3501
|
}
|
|
3502
|
+
// just ignore "memoryLimit" value because we cannot detect memory limit
|
|
3465
3503
|
return null;
|
|
3466
3504
|
}
|
|
3467
3505
|
|
|
@@ -3499,6 +3537,8 @@ function createPool(ctx) {
|
|
|
3499
3537
|
vmForks: null,
|
|
3500
3538
|
typescript: null
|
|
3501
3539
|
};
|
|
3540
|
+
// in addition to resolve.conditions Vite also adds production/development,
|
|
3541
|
+
// see: https://github.com/vitejs/vite/blob/af2aa09575229462635b7cbb6d248ca853057ba2/packages/vite/src/node/plugins/resolve.ts#L1056-L1080
|
|
3502
3542
|
const viteMajor = Number(version.split(".")[0]);
|
|
3503
3543
|
const potentialConditions = new Set(viteMajor >= 6 ? ctx.vite.config.ssr.resolve?.conditions ?? [] : [
|
|
3504
3544
|
"production",
|
|
@@ -3519,6 +3559,8 @@ function createPool(ctx) {
|
|
|
3519
3559
|
}
|
|
3520
3560
|
return condition;
|
|
3521
3561
|
}).flatMap((c) => ["--conditions", c]);
|
|
3562
|
+
// Instead of passing whole process.execArgv to the workers, pick allowed options.
|
|
3563
|
+
// Some options may crash worker, e.g. --prof, --title. nodejs/node#41103
|
|
3522
3564
|
const execArgv = process.execArgv.filter((execArg) => execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir"));
|
|
3523
3565
|
async function executeTests(method, files, invalidate) {
|
|
3524
3566
|
const options = {
|
|
@@ -3533,6 +3575,7 @@ function createPool(ctx) {
|
|
|
3533
3575
|
...ctx.config.env
|
|
3534
3576
|
}
|
|
3535
3577
|
};
|
|
3578
|
+
// env are case-insensitive on Windows, but spawned processes don't support it
|
|
3536
3579
|
if (isWindows) {
|
|
3537
3580
|
for (const name in options.env) {
|
|
3538
3581
|
options.env[name.toUpperCase()] = options.env[name];
|
|
@@ -3654,6 +3697,7 @@ class BaseSequencer {
|
|
|
3654
3697
|
constructor(ctx) {
|
|
3655
3698
|
this.ctx = ctx;
|
|
3656
3699
|
}
|
|
3700
|
+
// async so it can be extended by other sequelizers
|
|
3657
3701
|
async shard(files) {
|
|
3658
3702
|
const { config } = this.ctx;
|
|
3659
3703
|
const { index, count } = config.shard;
|
|
@@ -3669,6 +3713,7 @@ class BaseSequencer {
|
|
|
3669
3713
|
};
|
|
3670
3714
|
}).sort((a, b) => a.hash < b.hash ? -1 : a.hash > b.hash ? 1 : 0).slice(shardStart, shardEnd).map(({ spec }) => spec);
|
|
3671
3715
|
}
|
|
3716
|
+
// async so it can be extended by other sequelizers
|
|
3672
3717
|
async sort(files) {
|
|
3673
3718
|
const cache = this.ctx.cache;
|
|
3674
3719
|
return [...files].sort((a, b) => {
|
|
@@ -3679,17 +3724,21 @@ class BaseSequencer {
|
|
|
3679
3724
|
if (!aState || !bState) {
|
|
3680
3725
|
const statsA = cache.getFileStats(keyA);
|
|
3681
3726
|
const statsB = cache.getFileStats(keyB);
|
|
3727
|
+
// run unknown first
|
|
3682
3728
|
if (!statsA || !statsB) {
|
|
3683
3729
|
return !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0;
|
|
3684
3730
|
}
|
|
3731
|
+
// run larger files first
|
|
3685
3732
|
return statsB.size - statsA.size;
|
|
3686
3733
|
}
|
|
3734
|
+
// run failed first
|
|
3687
3735
|
if (aState.failed && !bState.failed) {
|
|
3688
3736
|
return -1;
|
|
3689
3737
|
}
|
|
3690
3738
|
if (!aState.failed && bState.failed) {
|
|
3691
3739
|
return 1;
|
|
3692
3740
|
}
|
|
3741
|
+
// run longer first
|
|
3693
3742
|
return bState.duration - aState.duration;
|
|
3694
3743
|
});
|
|
3695
3744
|
}
|
|
@@ -3824,9 +3873,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3824
3873
|
if (resolved.minWorkers) {
|
|
3825
3874
|
resolved.minWorkers = resolveInlineWorkerOption(resolved.minWorkers);
|
|
3826
3875
|
}
|
|
3827
|
-
|
|
3876
|
+
// run benchmark sequentially by default
|
|
3828
3877
|
resolved.fileParallelism ??= mode !== "benchmark";
|
|
3829
3878
|
if (!resolved.fileParallelism) {
|
|
3879
|
+
// ignore user config, parallelism cannot be implemented without limiting workers
|
|
3830
3880
|
resolved.maxWorkers = 1;
|
|
3831
3881
|
resolved.minWorkers = 1;
|
|
3832
3882
|
}
|
|
@@ -3842,13 +3892,19 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3842
3892
|
throw new Error(`You cannot use ${inspectOption} without "--no-file-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"`);
|
|
3843
3893
|
}
|
|
3844
3894
|
}
|
|
3895
|
+
// apply browser CLI options only if the config already has the browser config and not disabled manually
|
|
3896
|
+
if (vitest._cliOptions.browser && resolved.browser && (resolved.browser.enabled !== false || vitest._cliOptions.browser.enabled)) {
|
|
3897
|
+
resolved.browser = mergeConfig(resolved.browser, vitest._cliOptions.browser);
|
|
3898
|
+
}
|
|
3899
|
+
resolved.browser ??= {};
|
|
3845
3900
|
const browser = resolved.browser;
|
|
3846
|
-
if (browser.enabled
|
|
3901
|
+
if (browser.enabled) {
|
|
3847
3902
|
if (!browser.name && !browser.instances) {
|
|
3848
3903
|
throw new Error(`Vitest Browser Mode requires "browser.name" (deprecated) or "browser.instances" options, none were set.`);
|
|
3849
3904
|
}
|
|
3850
3905
|
const instances = browser.instances;
|
|
3851
3906
|
if (browser.name && browser.instances) {
|
|
3907
|
+
// --browser=chromium filters configs to a single one
|
|
3852
3908
|
browser.instances = browser.instances.filter((instance) => instance.browser === browser.name);
|
|
3853
3909
|
}
|
|
3854
3910
|
if (browser.instances && !browser.instances.length) {
|
|
@@ -3856,6 +3912,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3856
3912
|
}
|
|
3857
3913
|
}
|
|
3858
3914
|
const playwrightChromiumOnly = isPlaywrightChromiumOnly(vitest, resolved);
|
|
3915
|
+
// Browser-mode "Playwright + Chromium" only features:
|
|
3859
3916
|
if (browser.enabled && !playwrightChromiumOnly) {
|
|
3860
3917
|
const browserConfig = { browser: {
|
|
3861
3918
|
provider: browser.provider,
|
|
@@ -3912,6 +3969,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3912
3969
|
resolved.deps.web.transformGlobPattern ??= [];
|
|
3913
3970
|
resolved.setupFiles = toArray(resolved.setupFiles || []).map((file) => resolvePath(file, resolved.root));
|
|
3914
3971
|
resolved.globalSetup = toArray(resolved.globalSetup || []).map((file) => resolvePath(file, resolved.root));
|
|
3972
|
+
// override original exclude array for cases where user re-uses same object in test.exclude
|
|
3915
3973
|
resolved.coverage.exclude = [
|
|
3916
3974
|
...resolved.coverage.exclude,
|
|
3917
3975
|
...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
|
|
@@ -3942,6 +4000,8 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3942
4000
|
if (resolved.cliExclude) {
|
|
3943
4001
|
resolved.exclude.push(...resolved.cliExclude);
|
|
3944
4002
|
}
|
|
4003
|
+
// vitenode will try to import such file with native node,
|
|
4004
|
+
// but then our mocker will not work properly
|
|
3945
4005
|
if (resolved.server.deps.inline !== true) {
|
|
3946
4006
|
const ssrOptions = viteConfig.ssr;
|
|
3947
4007
|
if (ssrOptions?.noExternal === true && resolved.server.deps.inline == null) {
|
|
@@ -4055,6 +4115,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4055
4115
|
}
|
|
4056
4116
|
}
|
|
4057
4117
|
if (typeof resolved.workspace === "string") {
|
|
4118
|
+
// if passed down from the CLI and it's relative, resolve relative to CWD
|
|
4058
4119
|
resolved.workspace = typeof options.workspace === "string" && options.workspace[0] === "." ? resolve(process.cwd(), options.workspace) : resolvePath(resolved.workspace, resolved.root);
|
|
4059
4120
|
}
|
|
4060
4121
|
if (!builtinPools.includes(resolved.pool)) {
|
|
@@ -4074,6 +4135,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4074
4135
|
...benchmarkConfigDefaults,
|
|
4075
4136
|
...resolved.benchmark
|
|
4076
4137
|
};
|
|
4138
|
+
// override test config
|
|
4077
4139
|
resolved.coverage.enabled = false;
|
|
4078
4140
|
resolved.typecheck.enabled = false;
|
|
4079
4141
|
resolved.include = resolved.benchmark.include;
|
|
@@ -4088,6 +4150,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4088
4150
|
if (options.outputFile) {
|
|
4089
4151
|
resolved.benchmark.outputFile = options.outputFile;
|
|
4090
4152
|
}
|
|
4153
|
+
// --compare from cli
|
|
4091
4154
|
if (options.compare) {
|
|
4092
4155
|
resolved.benchmark.compare = options.compare;
|
|
4093
4156
|
}
|
|
@@ -4099,6 +4162,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4099
4162
|
resolved.diff = resolvePath(resolved.diff, resolved.root);
|
|
4100
4163
|
resolved.forceRerunTriggers.push(resolved.diff);
|
|
4101
4164
|
}
|
|
4165
|
+
// the server has been created, we don't need to override vite.server options
|
|
4102
4166
|
const api = resolveApiServerConfig(options, defaultPort);
|
|
4103
4167
|
resolved.api = {
|
|
4104
4168
|
...api,
|
|
@@ -4107,8 +4171,18 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4107
4171
|
if (options.related) {
|
|
4108
4172
|
resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
|
|
4109
4173
|
}
|
|
4174
|
+
/*
|
|
4175
|
+
* Reporters can be defined in many different ways:
|
|
4176
|
+
* { reporter: 'json' }
|
|
4177
|
+
* { reporter: { onFinish() { method() } } }
|
|
4178
|
+
* { reporter: ['json', { onFinish() { method() } }] }
|
|
4179
|
+
* { reporter: [[ 'json' ]] }
|
|
4180
|
+
* { reporter: [[ 'json' ], 'html'] }
|
|
4181
|
+
* { reporter: [[ 'json', { outputFile: 'test.json' } ], 'html'] }
|
|
4182
|
+
*/
|
|
4110
4183
|
if (options.reporters) {
|
|
4111
4184
|
if (!Array.isArray(options.reporters)) {
|
|
4185
|
+
// Reporter name, e.g. { reporters: 'json' }
|
|
4112
4186
|
if (typeof options.reporters === "string") {
|
|
4113
4187
|
resolved.reporters = [[options.reporters, {}]];
|
|
4114
4188
|
} else {
|
|
@@ -4118,18 +4192,24 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4118
4192
|
resolved.reporters = [];
|
|
4119
4193
|
for (const reporter of options.reporters) {
|
|
4120
4194
|
if (Array.isArray(reporter)) {
|
|
4195
|
+
// Reporter with options, e.g. { reporters: [ [ 'json', { outputFile: 'test.json' } ] ] }
|
|
4121
4196
|
resolved.reporters.push([reporter[0], reporter[1] || {}]);
|
|
4122
4197
|
} else if (typeof reporter === "string") {
|
|
4198
|
+
// Reporter name in array, e.g. { reporters: ["html", "json"]}
|
|
4123
4199
|
resolved.reporters.push([reporter, {}]);
|
|
4124
4200
|
} else {
|
|
4201
|
+
// Inline reporter, e.g. { reporter: [{ onFinish() { method() } }] }
|
|
4125
4202
|
resolved.reporters.push(reporter);
|
|
4126
4203
|
}
|
|
4127
4204
|
}
|
|
4128
4205
|
}
|
|
4129
4206
|
}
|
|
4130
4207
|
if (mode !== "benchmark") {
|
|
4208
|
+
// @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
|
|
4209
|
+
// it is passed down as "vitest --reporter ../reporter.js"
|
|
4131
4210
|
const reportersFromCLI = resolved.reporter;
|
|
4132
4211
|
const cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
|
|
4212
|
+
// ./reporter.js || ../reporter.js, but not .reporters/reporter.js
|
|
4133
4213
|
if (/^\.\.?\//.test(reporter)) {
|
|
4134
4214
|
return resolve(process.cwd(), reporter);
|
|
4135
4215
|
}
|
|
@@ -4141,6 +4221,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4141
4221
|
}
|
|
4142
4222
|
if (!resolved.reporters.length) {
|
|
4143
4223
|
resolved.reporters.push(["default", {}]);
|
|
4224
|
+
// also enable github-actions reporter as a default
|
|
4144
4225
|
if (process.env.GITHUB_ACTIONS === "true") {
|
|
4145
4226
|
resolved.reporters.push(["github-actions", {}]);
|
|
4146
4227
|
}
|
|
@@ -4168,6 +4249,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4168
4249
|
resolved.sequence.shuffle = tests;
|
|
4169
4250
|
}
|
|
4170
4251
|
if (!resolved.sequence?.sequencer) {
|
|
4252
|
+
// CLI flag has higher priority
|
|
4171
4253
|
resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
|
|
4172
4254
|
}
|
|
4173
4255
|
resolved.sequence.groupOrder ??= 0;
|
|
@@ -4188,11 +4270,11 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4188
4270
|
if (resolved.typecheck.enabled) {
|
|
4189
4271
|
logger.console.warn(c.yellow("Testing types with tsc and vue-tsc is an experimental feature.\nBreaking changes might not follow SemVer, please pin Vitest's version when using it."));
|
|
4190
4272
|
}
|
|
4191
|
-
resolved.browser ??= {};
|
|
4192
4273
|
resolved.browser.enabled ??= false;
|
|
4193
4274
|
resolved.browser.headless ??= isCI;
|
|
4194
4275
|
resolved.browser.isolate ??= true;
|
|
4195
4276
|
resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark";
|
|
4277
|
+
// disable in headless mode by default, and if CI is detected
|
|
4196
4278
|
resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI;
|
|
4197
4279
|
if (resolved.browser.screenshotDirectory) {
|
|
4198
4280
|
resolved.browser.screenshotDirectory = resolve(resolved.root, resolved.browser.screenshotDirectory);
|
|
@@ -4217,6 +4299,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
4217
4299
|
resolved.browser.provider = "preview";
|
|
4218
4300
|
}
|
|
4219
4301
|
resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort };
|
|
4302
|
+
// enable includeTaskLocation by default in UI mode
|
|
4220
4303
|
if (resolved.browser.enabled) {
|
|
4221
4304
|
if (resolved.browser.ui) {
|
|
4222
4305
|
resolved.includeTaskLocation ??= true;
|
|
@@ -4242,14 +4325,17 @@ function isBrowserEnabled(config) {
|
|
|
4242
4325
|
return Boolean(config.browser?.enabled);
|
|
4243
4326
|
}
|
|
4244
4327
|
function resolveCoverageReporters(configReporters) {
|
|
4328
|
+
// E.g. { reporter: "html" }
|
|
4245
4329
|
if (!Array.isArray(configReporters)) {
|
|
4246
4330
|
return [[configReporters, {}]];
|
|
4247
4331
|
}
|
|
4248
4332
|
const resolvedReporters = [];
|
|
4249
4333
|
for (const reporter of configReporters) {
|
|
4250
4334
|
if (Array.isArray(reporter)) {
|
|
4335
|
+
// E.g. { reporter: [ ["html", { skipEmpty: true }], ["lcov"], ["json", { file: "map.json" }] ]}
|
|
4251
4336
|
resolvedReporters.push([reporter[0], reporter[1] || {}]);
|
|
4252
4337
|
} else {
|
|
4338
|
+
// E.g. { reporter: ["html", "json"]}
|
|
4253
4339
|
resolvedReporters.push([reporter, {}]);
|
|
4254
4340
|
}
|
|
4255
4341
|
}
|
|
@@ -4268,6 +4354,7 @@ function isPlaywrightChromiumOnly(vitest, config) {
|
|
|
4268
4354
|
}
|
|
4269
4355
|
for (const instance of browser.instances) {
|
|
4270
4356
|
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
4357
|
+
// browser config is filtered out
|
|
4271
4358
|
if (!vitest.matchesProjectFilter(name)) {
|
|
4272
4359
|
continue;
|
|
4273
4360
|
}
|
|
@@ -4375,6 +4462,7 @@ class BaseCoverageProvider {
|
|
|
4375
4462
|
}
|
|
4376
4463
|
const testFilenames = testFiles.join();
|
|
4377
4464
|
const filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
|
|
4465
|
+
// If there's a result from previous run, overwrite it
|
|
4378
4466
|
entry[transformMode][testFilenames] = filename;
|
|
4379
4467
|
const promise = promises$1.writeFile(filename, JSON.stringify(coverage), "utf-8");
|
|
4380
4468
|
this.pendingPromises.push(promise);
|
|
@@ -4406,6 +4494,7 @@ class BaseCoverageProvider {
|
|
|
4406
4494
|
async cleanAfterRun() {
|
|
4407
4495
|
this.coverageFiles = new Map();
|
|
4408
4496
|
await promises$1.rm(this.coverageFilesDirectory, { recursive: true });
|
|
4497
|
+
// Remove empty reports directory, e.g. when only text-reporter is used
|
|
4409
4498
|
if (readdirSync(this.options.reportsDirectory).length === 0) {
|
|
4410
4499
|
await promises$1.rm(this.options.reportsDirectory, { recursive: true });
|
|
4411
4500
|
}
|
|
@@ -4417,6 +4506,7 @@ class BaseCoverageProvider {
|
|
|
4417
4506
|
}
|
|
4418
4507
|
async reportCoverage(coverageMap, { allTestsRun }) {
|
|
4419
4508
|
await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun);
|
|
4509
|
+
// In watch mode we need to preserve the previous results if cleanOnRerun is disabled
|
|
4420
4510
|
const keepResults = !this.options.cleanOnRerun && this.ctx.config.watch;
|
|
4421
4511
|
if (!keepResults) {
|
|
4422
4512
|
await this.cleanAfterRun();
|
|
@@ -4466,6 +4556,7 @@ class BaseCoverageProvider {
|
|
|
4466
4556
|
thresholds: globThresholds
|
|
4467
4557
|
});
|
|
4468
4558
|
}
|
|
4559
|
+
// Global threshold is for all files, even if they are included by glob patterns
|
|
4469
4560
|
for (const file of files) {
|
|
4470
4561
|
const fileCoverage = coverageMap.fileCoverageFor(file);
|
|
4471
4562
|
globalCoverageMap.addFileCoverage(fileCoverage);
|
|
@@ -4490,6 +4581,7 @@ class BaseCoverageProvider {
|
|
|
4490
4581
|
if (thresholds.branches === undefined && thresholds.functions === undefined && thresholds.lines === undefined && thresholds.statements === undefined) {
|
|
4491
4582
|
continue;
|
|
4492
4583
|
}
|
|
4584
|
+
// Construct list of coverage summaries where thresholds are compared against
|
|
4493
4585
|
const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => ({
|
|
4494
4586
|
file,
|
|
4495
4587
|
summary: coverageMap.fileCoverageFor(file).toSummary()
|
|
@@ -4497,6 +4589,7 @@ class BaseCoverageProvider {
|
|
|
4497
4589
|
file: null,
|
|
4498
4590
|
summary: coverageMap.getCoverageSummary()
|
|
4499
4591
|
}];
|
|
4592
|
+
// Check thresholds of each summary
|
|
4500
4593
|
for (const { summary, file } of summaries) {
|
|
4501
4594
|
for (const thresholdKey of THRESHOLD_KEYS) {
|
|
4502
4595
|
const threshold = thresholds[thresholdKey];
|
|
@@ -4568,6 +4661,7 @@ class BaseCoverageProvider {
|
|
|
4568
4661
|
const absoluteThreshold = threshold * -1;
|
|
4569
4662
|
const actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
|
|
4570
4663
|
if (actual < absoluteThreshold) {
|
|
4664
|
+
// If everything was covered, set new threshold to 100% (since a threshold of 0 would be considered as 0%)
|
|
4571
4665
|
const updatedThreshold = actual === 0 ? 100 : actual * -1;
|
|
4572
4666
|
thresholdsToUpdate.push([key, updatedThreshold]);
|
|
4573
4667
|
}
|
|
@@ -4627,6 +4721,7 @@ class BaseCoverageProvider {
|
|
|
4627
4721
|
return async function transformFile(filename) {
|
|
4628
4722
|
let lastError;
|
|
4629
4723
|
for (const { root, vitenode, isBrowserEnabled } of servers) {
|
|
4724
|
+
// On Windows root doesn't start with "/" while filenames do
|
|
4630
4725
|
if (!filename.startsWith(root) && !filename.startsWith(`/${root}`)) {
|
|
4631
4726
|
continue;
|
|
4632
4727
|
}
|
|
@@ -4642,6 +4737,7 @@ class BaseCoverageProvider {
|
|
|
4642
4737
|
lastError = error;
|
|
4643
4738
|
}
|
|
4644
4739
|
}
|
|
4740
|
+
// All vite-node servers failed to transform the file
|
|
4645
4741
|
throw lastError;
|
|
4646
4742
|
};
|
|
4647
4743
|
}
|
|
@@ -4670,6 +4766,7 @@ function resolveGlobThresholds(thresholds) {
|
|
|
4670
4766
|
}
|
|
4671
4767
|
function assertConfigurationModule(config) {
|
|
4672
4768
|
try {
|
|
4769
|
+
// @ts-expect-error -- Intentional unsafe null pointer check as wrapped in try-catch
|
|
4673
4770
|
if (typeof config.test.coverage.thresholds !== "object") {
|
|
4674
4771
|
throw new TypeError("Expected config.test.coverage.thresholds to be an object");
|
|
4675
4772
|
}
|
|
@@ -4681,13 +4778,16 @@ function assertConfigurationModule(config) {
|
|
|
4681
4778
|
function resolveConfig(configModule) {
|
|
4682
4779
|
const mod = configModule.exports.default;
|
|
4683
4780
|
try {
|
|
4781
|
+
// Check for "export default { test: {...} }"
|
|
4684
4782
|
if (mod.$type === "object") {
|
|
4685
4783
|
return mod;
|
|
4686
4784
|
}
|
|
4785
|
+
// "export default defineConfig(...)"
|
|
4687
4786
|
let config = resolveDefineConfig(mod);
|
|
4688
4787
|
if (config) {
|
|
4689
4788
|
return config;
|
|
4690
4789
|
}
|
|
4790
|
+
// "export default mergeConfig(..., defineConfig(...))"
|
|
4691
4791
|
if (mod.$type === "function-call" && mod.$callee === "mergeConfig") {
|
|
4692
4792
|
config = resolveMergeConfig(mod);
|
|
4693
4793
|
if (config) {
|
|
@@ -4695,19 +4795,23 @@ function resolveConfig(configModule) {
|
|
|
4695
4795
|
}
|
|
4696
4796
|
}
|
|
4697
4797
|
} catch (error) {
|
|
4798
|
+
// Reduce magicast's verbose errors to readable ones
|
|
4698
4799
|
throw new Error(error instanceof Error ? error.message : String(error));
|
|
4699
4800
|
}
|
|
4700
4801
|
throw new Error("Failed to update coverage thresholds. Configuration file is too complex.");
|
|
4701
4802
|
}
|
|
4702
4803
|
function resolveDefineConfig(mod) {
|
|
4703
4804
|
if (mod.$type === "function-call" && mod.$callee === "defineConfig") {
|
|
4805
|
+
// "export default defineConfig({ test: {...} })"
|
|
4704
4806
|
if (mod.$args[0].$type === "object") {
|
|
4705
4807
|
return mod.$args[0];
|
|
4706
4808
|
}
|
|
4707
4809
|
if (mod.$args[0].$type === "arrow-function-expression") {
|
|
4708
4810
|
if (mod.$args[0].$body.$type === "object") {
|
|
4811
|
+
// "export default defineConfig(() => ({ test: {...} }))"
|
|
4709
4812
|
return mod.$args[0].$body;
|
|
4710
4813
|
}
|
|
4814
|
+
// "export default defineConfig(() => mergeConfig({...}, ...))"
|
|
4711
4815
|
const config = resolveMergeConfig(mod.$args[0].$body);
|
|
4712
4816
|
if (config) {
|
|
4713
4817
|
return config;
|