vitest 4.0.0-beta.8 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +86 -102
- package/browser/context.d.ts +7 -0
- package/browser/context.js +20 -0
- package/dist/browser.d.ts +26 -9
- package/dist/browser.js +17 -7
- package/dist/chunks/base.CtHM3ryk.js +128 -0
- package/dist/chunks/{benchmark.UW6Ezvxy.js → benchmark.DHKMYAts.js} +2 -2
- package/dist/chunks/{browser.d.DOMmqJQx.d.ts → browser.d.B9iJzZyn.d.ts} +3 -3
- package/dist/chunks/{cac.By1HvRIk.js → cac.B99MQg-w.js} +47 -91
- package/dist/chunks/{cli-api.C-JHgQgp.js → cli-api.PwHwIMss.js} +1544 -310
- package/dist/chunks/{config.d._GBBbReY.d.ts → config.d.u2CUDWwS.d.ts} +6 -19
- package/dist/chunks/{console.B0quX7yH.js → console.CTJL2nuH.js} +4 -6
- package/dist/chunks/{coverage.DarITf6U.js → coverage.FU3w4IrQ.js} +128 -1142
- package/dist/chunks/{creator.KEg6n5IC.js → creator.DucAaYBz.js} +10 -37
- package/dist/chunks/{defaults.CXFFjsi8.js → defaults.BOqNVLsY.js} +0 -1
- package/dist/chunks/environment.d.CrsxCzP1.d.ts +29 -0
- package/dist/chunks/evaluatedModules.Dg1zASAC.js +17 -0
- package/dist/chunks/{global.d.K6uBQHzY.d.ts → global.d.BgJSTpgQ.d.ts} +2 -17
- package/dist/chunks/{globals.lgsmH00r.js → globals.BGT_RUsD.js} +12 -9
- package/dist/chunks/{index.BuwjkI-q.js → index.BdSLhLDZ.js} +3 -3
- package/dist/chunks/{index.DfviD7lX.js → index.CbWINfS7.js} +49 -21
- package/dist/chunks/{index.AzwzFtyi.js → index.CcRZ6fUh.js} +1493 -114
- package/dist/chunks/{index.X0nbfr6-.js → index.Dc3xnDvT.js} +48 -289
- package/dist/chunks/{index.AR8aAkCC.js → index.RwjEGCQ0.js} +7 -8
- package/dist/chunks/init-forks.DSafeltJ.js +54 -0
- package/dist/chunks/init-threads.SUtZ-067.js +17 -0
- package/dist/chunks/init.B2EESLQM.js +213 -0
- package/dist/chunks/{inspector.CvQD-Nie.js → inspector.DLZxSeU3.js} +2 -6
- package/dist/chunks/{moduleRunner.d.CX4DuqOx.d.ts → moduleRunner.d.YtNsMIoJ.d.ts} +12 -14
- package/dist/chunks/{node.BOqcT2jW.js → node.BwAWWjHZ.js} +3 -4
- package/dist/chunks/{plugin.d.CHe6slQs.d.ts → plugin.d.DQU1R5px.d.ts} +1 -1
- package/dist/chunks/{reporters.d.37tJQ2uV.d.ts → reporters.d.BMKt7f6I.d.ts} +1066 -1030
- package/dist/chunks/{index.CsFXYRkW.js → resolveSnapshotEnvironment.DJJKMKxb.js} +18 -24
- package/dist/chunks/{rpc.RpPylpp0.js → rpc.cD77ENhU.js} +13 -14
- package/dist/chunks/{setup-common.hLGRxhC8.js → setup-common.DR1sucx6.js} +8 -8
- package/dist/chunks/{startModuleRunner.C8TW8zTN.js → startModuleRunner.C2tTvmF9.js} +131 -110
- package/dist/chunks/test.C3RPt8JR.js +214 -0
- package/dist/chunks/{utils.C7__0Iv5.js → utils.CG9h5ccR.js} +3 -15
- package/dist/chunks/{vi.BfdOiD4j.js → vi.BZvkKVkM.js} +73 -176
- package/dist/chunks/{vm.BHBje7cC.js → vm.DBeOXrP9.js} +29 -33
- package/dist/chunks/{worker.d.DYlqbejz.d.ts → worker.d.BFk-vvBU.d.ts} +42 -6
- package/dist/cli.js +12 -11
- package/dist/config.cjs +0 -1
- package/dist/config.d.ts +12 -14
- package/dist/config.js +1 -1
- package/dist/coverage.d.ts +8 -7
- package/dist/coverage.js +3 -14
- package/dist/environments.d.ts +3 -6
- package/dist/environments.js +1 -1
- package/dist/index.d.ts +24 -30
- package/dist/index.js +12 -11
- package/dist/module-evaluator.d.ts +6 -4
- package/dist/module-evaluator.js +14 -16
- package/dist/module-runner.js +5 -5
- package/dist/node.d.ts +83 -27
- package/dist/node.js +23 -20
- package/dist/reporters.d.ts +11 -10
- package/dist/reporters.js +12 -11
- package/dist/runners.d.ts +1 -1
- package/dist/runners.js +14 -216
- package/dist/snapshot.js +3 -3
- package/dist/suite.js +4 -3
- package/dist/worker.d.ts +26 -0
- package/dist/worker.js +45 -166
- package/dist/workers/forks.js +41 -35
- package/dist/workers/runVmTests.js +25 -22
- package/dist/workers/threads.js +41 -23
- package/dist/workers/vmForks.js +26 -39
- package/dist/workers/vmThreads.js +26 -29
- package/package.json +48 -35
- package/worker.d.ts +1 -0
- package/browser.d.ts +0 -1
- package/dist/chunks/base.BXI97p6t.js +0 -39
- package/dist/chunks/environment.d.2fYMoz3o.d.ts +0 -66
- package/dist/chunks/moduleTransport.I-bgQy0S.js +0 -19
- package/dist/chunks/resolver.Bx6lE0iq.js +0 -119
- package/dist/chunks/runBaseTests.D6sfuWBM.js +0 -99
- package/dist/chunks/typechecker.DSo_maXz.js +0 -762
- package/dist/chunks/utils.C2YI6McM.js +0 -52
- package/dist/chunks/worker.d.BKu8cnnX.d.ts +0 -8
- package/dist/workers.d.ts +0 -38
- package/dist/workers.js +0 -31
- package/execute.d.ts +0 -1
- package/utils.d.ts +0 -1
- package/workers.d.ts +0 -1
|
@@ -1,58 +1,26 @@
|
|
|
1
|
-
import fs, { statSync, realpathSync,
|
|
2
|
-
import path, { win32, dirname, join
|
|
3
|
-
import {
|
|
4
|
-
import { isAbsolute,
|
|
1
|
+
import fs, { statSync, realpathSync, existsSync, promises, readdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path, { win32, dirname, join } from 'node:path';
|
|
3
|
+
import { slash, shuffle, toArray, cleanUrl } from '@vitest/utils/helpers';
|
|
4
|
+
import { isAbsolute, resolve, relative, normalize } from 'pathe';
|
|
5
5
|
import pm from 'picomatch';
|
|
6
6
|
import { glob } from 'tinyglobby';
|
|
7
7
|
import c from 'tinyrainbow';
|
|
8
|
-
import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.
|
|
8
|
+
import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.BOqNVLsY.js';
|
|
9
9
|
import crypto from 'node:crypto';
|
|
10
10
|
import { builtinModules, createRequire } from 'node:module';
|
|
11
11
|
import process$1 from 'node:process';
|
|
12
|
-
import fs$1
|
|
12
|
+
import fs$1 from 'node:fs/promises';
|
|
13
13
|
import { fileURLToPath as fileURLToPath$1, pathToFileURL as pathToFileURL$1, URL as URL$1 } from 'node:url';
|
|
14
14
|
import assert from 'node:assert';
|
|
15
15
|
import v8 from 'node:v8';
|
|
16
16
|
import { format, inspect } from 'node:util';
|
|
17
|
-
import {
|
|
17
|
+
import { mergeConfig } from 'vite';
|
|
18
18
|
import { c as configFiles, d as defaultBrowserPort, b as defaultInspectPort, a as defaultPort } from './constants.D_Q9UYh-.js';
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import nodeos__default, { tmpdir } from 'node:os';
|
|
22
|
-
import { isatty } from 'node:tty';
|
|
23
|
-
import EventEmitter from 'node:events';
|
|
24
|
-
import { c as createBirpc } from './index.Bgo3tNWt.js';
|
|
25
|
-
import Tinypool$1, { Tinypool } from 'tinypool';
|
|
26
|
-
import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.DSo_maXz.js';
|
|
27
|
-
import { MessageChannel } from 'node:worker_threads';
|
|
28
|
-
import { hasFailed } from '@vitest/runner/utils';
|
|
29
|
-
import { rootDir } from '../path.js';
|
|
19
|
+
import './env.D4Lgay0q.js';
|
|
20
|
+
import nodeos__default from 'node:os';
|
|
30
21
|
import { isCI, provider } from 'std-env';
|
|
31
22
|
import { r as resolveCoverageProviderModule } from './coverage.D_JHT54q.js';
|
|
32
23
|
|
|
33
|
-
function groupBy(collection, iteratee) {
|
|
34
|
-
return collection.reduce((acc, item) => {
|
|
35
|
-
const key = iteratee(item);
|
|
36
|
-
return acc[key] ||= [], acc[key].push(item), acc;
|
|
37
|
-
}, {});
|
|
38
|
-
}
|
|
39
|
-
function stdout() {
|
|
40
|
-
// @ts-expect-error Node.js maps process.stdout to console._stdout
|
|
41
|
-
// eslint-disable-next-line no-console
|
|
42
|
-
return console._stdout || process.stdout;
|
|
43
|
-
}
|
|
44
|
-
function escapeRegExp(s) {
|
|
45
|
-
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
|
46
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
47
|
-
}
|
|
48
|
-
function wildcardPatternToRegExp(pattern) {
|
|
49
|
-
const negated = pattern.startsWith("!");
|
|
50
|
-
if (negated) pattern = pattern.slice(1);
|
|
51
|
-
let regexp = `${pattern.split("*").map(escapeRegExp).join(".*")}$`;
|
|
52
|
-
if (negated) regexp = `(?!${regexp})`;
|
|
53
|
-
return new RegExp(`^${regexp}`, "i");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
24
|
const hash = crypto.hash ?? ((algorithm, data, outputEncoding) => crypto.createHash(algorithm).update(data).digest(outputEncoding));
|
|
57
25
|
|
|
58
26
|
const JOIN_LEADING_SLASH_RE = /^\.?\//;
|
|
@@ -2014,7 +1982,7 @@ function normalizeid(id) {
|
|
|
2014
1982
|
if (typeof id !== "string") {
|
|
2015
1983
|
id = id.toString();
|
|
2016
1984
|
}
|
|
2017
|
-
if (/(node|data|http|https|file):/.test(id)) {
|
|
1985
|
+
if (/(?:node|data|http|https|file):/.test(id)) {
|
|
2018
1986
|
return id;
|
|
2019
1987
|
}
|
|
2020
1988
|
if (BUILTIN_MODULES.has(id)) {
|
|
@@ -2048,7 +2016,7 @@ function _resolve$1(id, options = {}) {
|
|
|
2048
2016
|
throw new TypeError("input must be a `string` or `URL`");
|
|
2049
2017
|
}
|
|
2050
2018
|
}
|
|
2051
|
-
if (/(node|data|http|https):/.test(id)) {
|
|
2019
|
+
if (/(?:node|data|http|https):/.test(id)) {
|
|
2052
2020
|
return id;
|
|
2053
2021
|
}
|
|
2054
2022
|
if (BUILTIN_MODULES.has(id)) {
|
|
@@ -2386,970 +2354,6 @@ function getWorkersCountByPercentage(percent) {
|
|
|
2386
2354
|
return Math.max(1, Math.min(maxWorkersCount, workersCountByPercentage));
|
|
2387
2355
|
}
|
|
2388
2356
|
|
|
2389
|
-
const envsOrder = [
|
|
2390
|
-
"node",
|
|
2391
|
-
"jsdom",
|
|
2392
|
-
"happy-dom",
|
|
2393
|
-
"edge-runtime"
|
|
2394
|
-
];
|
|
2395
|
-
async function groupFilesByEnv(files) {
|
|
2396
|
-
const filesWithEnv = await Promise.all(files.map(async ({ moduleId: filepath, project, testLines }) => {
|
|
2397
|
-
const code = await promises$1.readFile(filepath, "utf-8");
|
|
2398
|
-
// 1. Check for control comments in the file
|
|
2399
|
-
let env = code.match(/@(?:vitest|jest)-environment\s+([\w-]+)\b/)?.[1];
|
|
2400
|
-
// 2. Fallback to global env
|
|
2401
|
-
env ||= project.config.environment || "node";
|
|
2402
|
-
let envOptionsJson = code.match(/@(?:vitest|jest)-environment-options\s+(.+)/)?.[1];
|
|
2403
|
-
if (envOptionsJson?.endsWith("*/"))
|
|
2404
|
-
// Trim closing Docblock characters the above regex might have captured
|
|
2405
|
-
envOptionsJson = envOptionsJson.slice(0, -2);
|
|
2406
|
-
const envOptions = JSON.parse(envOptionsJson || "null"), envKey = env === "happy-dom" ? "happyDOM" : env, environment = {
|
|
2407
|
-
name: env,
|
|
2408
|
-
options: envOptions ? { [envKey]: envOptions } : null
|
|
2409
|
-
};
|
|
2410
|
-
return {
|
|
2411
|
-
file: {
|
|
2412
|
-
filepath,
|
|
2413
|
-
testLocations: testLines
|
|
2414
|
-
},
|
|
2415
|
-
project,
|
|
2416
|
-
environment
|
|
2417
|
-
};
|
|
2418
|
-
}));
|
|
2419
|
-
return groupBy(filesWithEnv, ({ environment }) => environment.name);
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
const created = /* @__PURE__ */ new Set(), promises = /* @__PURE__ */ new Map();
|
|
2423
|
-
function createFetchModuleFunction(resolver, cacheFs = false, tmpDir = join$1(tmpdir(), nanoid())) {
|
|
2424
|
-
const cachedFsResults = /* @__PURE__ */ new Map();
|
|
2425
|
-
return async (url, importer, environment, options) => {
|
|
2426
|
-
// We are copy pasting Vite's externalization logic from `fetchModule` because
|
|
2427
|
-
// we instead rely on our own `shouldExternalize` method because Vite
|
|
2428
|
-
// doesn't support `resolve.external` in non SSR environments (jsdom/happy-dom)
|
|
2429
|
-
if (url.startsWith("data:")) return {
|
|
2430
|
-
externalize: url,
|
|
2431
|
-
type: "builtin"
|
|
2432
|
-
};
|
|
2433
|
-
if (url === "/@vite/client" || url === "@vite/client")
|
|
2434
|
-
// this will be stubbed
|
|
2435
|
-
return {
|
|
2436
|
-
externalize: "/@vite/client",
|
|
2437
|
-
type: "module"
|
|
2438
|
-
};
|
|
2439
|
-
const isFileUrl = url.startsWith("file://");
|
|
2440
|
-
if (isExternalUrl(url) && !isFileUrl) return {
|
|
2441
|
-
externalize: url,
|
|
2442
|
-
type: "network"
|
|
2443
|
-
};
|
|
2444
|
-
// Vite does the same in `fetchModule`, but we want to externalize modules ourselves,
|
|
2445
|
-
// so we do this first to resolve the module and check its `id`. The next call of
|
|
2446
|
-
// `ensureEntryFromUrl` inside `fetchModule` is cached and should take no time
|
|
2447
|
-
// This also makes it so externalized modules are inside the module graph.
|
|
2448
|
-
const moduleGraphModule = await environment.moduleGraph.ensureEntryFromUrl(unwrapId(url)), cached = !!moduleGraphModule.transformResult;
|
|
2449
|
-
// if url is already cached, we can just confirm it's also cached on the server
|
|
2450
|
-
if (options?.cached && cached) return { cache: true };
|
|
2451
|
-
if (moduleGraphModule.id) {
|
|
2452
|
-
const externalize = await resolver.shouldExternalize(moduleGraphModule.id);
|
|
2453
|
-
if (externalize) return {
|
|
2454
|
-
externalize,
|
|
2455
|
-
type: "module"
|
|
2456
|
-
};
|
|
2457
|
-
}
|
|
2458
|
-
const moduleRunnerModule = await fetchModule(environment, url, importer, {
|
|
2459
|
-
...options,
|
|
2460
|
-
inlineSourceMap: false
|
|
2461
|
-
}).catch(handleRollupError), result = processResultSource(environment, moduleRunnerModule);
|
|
2462
|
-
if (!cacheFs || !("code" in result)) return result;
|
|
2463
|
-
const code = result.code;
|
|
2464
|
-
// to avoid serialising large chunks of code,
|
|
2465
|
-
// we store them in a tmp file and read in the test thread
|
|
2466
|
-
if (cachedFsResults.has(result.id)) return getCachedResult(result, cachedFsResults);
|
|
2467
|
-
const dir = join$1(tmpDir, environment.name), name = hash("sha1", result.id, "hex"), tmp = join$1(dir, name);
|
|
2468
|
-
if (!created.has(dir)) mkdirSync(dir, { recursive: true }), created.add(dir);
|
|
2469
|
-
return promises.has(tmp) ? (await promises.get(tmp), cachedFsResults.set(result.id, tmp), getCachedResult(result, cachedFsResults)) : (promises.set(tmp, atomicWriteFile(tmp, code).catch(() => writeFile(tmp, code, "utf-8")).finally(() => promises.delete(tmp))), await promises.get(tmp), cachedFsResults.set(result.id, tmp), getCachedResult(result, cachedFsResults));
|
|
2470
|
-
};
|
|
2471
|
-
}
|
|
2472
|
-
let SOURCEMAPPING_URL = "sourceMa";
|
|
2473
|
-
SOURCEMAPPING_URL += "ppingURL";
|
|
2474
|
-
const MODULE_RUNNER_SOURCEMAPPING_SOURCE = "//# sourceMappingSource=vite-generated";
|
|
2475
|
-
function processResultSource(environment, result) {
|
|
2476
|
-
if (!("code" in result)) return result;
|
|
2477
|
-
const node = environment.moduleGraph.getModuleById(result.id);
|
|
2478
|
-
if (node?.transformResult)
|
|
2479
|
-
// this also overrides node.transformResult.code which is also what the module
|
|
2480
|
-
// runner does under the hood by default (we disable source maps inlining)
|
|
2481
|
-
inlineSourceMap(node.transformResult);
|
|
2482
|
-
return {
|
|
2483
|
-
...result,
|
|
2484
|
-
code: node?.transformResult?.code || result.code
|
|
2485
|
-
};
|
|
2486
|
-
}
|
|
2487
|
-
const OTHER_SOURCE_MAP_REGEXP = new RegExp(`//# ${SOURCEMAPPING_URL}=data:application/json[^,]+base64,([A-Za-z0-9+/=]+)$`, "gm");
|
|
2488
|
-
// we have to inline the source map ourselves, because
|
|
2489
|
-
// - we don't need //# sourceURL since we are running code in VM
|
|
2490
|
-
// - important in stack traces and the V8 coverage
|
|
2491
|
-
// - we need to inject an empty line for --inspect-brk
|
|
2492
|
-
function inlineSourceMap(result) {
|
|
2493
|
-
const map = result.map;
|
|
2494
|
-
let code = result.code;
|
|
2495
|
-
if (!map || !("version" in map) || code.includes(MODULE_RUNNER_SOURCEMAPPING_SOURCE)) return result;
|
|
2496
|
-
if (OTHER_SOURCE_MAP_REGEXP.lastIndex = 0, OTHER_SOURCE_MAP_REGEXP.test(code)) code = code.replace(OTHER_SOURCE_MAP_REGEXP, "");
|
|
2497
|
-
const sourceMap = { ...map };
|
|
2498
|
-
// If the first line is not present on source maps, add simple 1:1 mapping ([0,0,0,0], [1,0,0,0])
|
|
2499
|
-
// so that debuggers can be set to break on first line
|
|
2500
|
-
if (sourceMap.mappings.startsWith(";")) sourceMap.mappings = `AAAA,CAAA${sourceMap.mappings}`;
|
|
2501
|
-
return result.code = `${code.trimEnd()}\n${MODULE_RUNNER_SOURCEMAPPING_SOURCE}\n//# ${SOURCEMAPPING_URL}=${genSourceMapUrl(sourceMap)}\n`, result;
|
|
2502
|
-
}
|
|
2503
|
-
function genSourceMapUrl(map) {
|
|
2504
|
-
if (typeof map !== "string") map = JSON.stringify(map);
|
|
2505
|
-
return `data:application/json;base64,${Buffer.from(map).toString("base64")}`;
|
|
2506
|
-
}
|
|
2507
|
-
function getCachedResult(result, cachedFsResults) {
|
|
2508
|
-
const tmp = cachedFsResults.get(result.id);
|
|
2509
|
-
if (!tmp) throw new Error(`The cached result was returned too early for ${result.id}.`);
|
|
2510
|
-
return {
|
|
2511
|
-
cached: true,
|
|
2512
|
-
file: result.file,
|
|
2513
|
-
id: result.id,
|
|
2514
|
-
tmp,
|
|
2515
|
-
url: result.url,
|
|
2516
|
-
invalidate: result.invalidate
|
|
2517
|
-
};
|
|
2518
|
-
}
|
|
2519
|
-
// serialize rollup error on server to preserve details as a test error
|
|
2520
|
-
function handleRollupError(e) {
|
|
2521
|
-
throw e instanceof Error && ("plugin" in e || "frame" in e || "id" in e) ? {
|
|
2522
|
-
name: e.name,
|
|
2523
|
-
message: e.message,
|
|
2524
|
-
stack: e.stack,
|
|
2525
|
-
cause: e.cause,
|
|
2526
|
-
__vitest_rollup_error__: {
|
|
2527
|
-
plugin: e.plugin,
|
|
2528
|
-
id: e.id,
|
|
2529
|
-
loc: e.loc,
|
|
2530
|
-
frame: e.frame
|
|
2531
|
-
}
|
|
2532
|
-
} : e;
|
|
2533
|
-
}
|
|
2534
|
-
/**
|
|
2535
|
-
* Performs an atomic write operation using the write-then-rename pattern.
|
|
2536
|
-
*
|
|
2537
|
-
* Why we need this:
|
|
2538
|
-
* - Ensures file integrity by never leaving partially written files on disk
|
|
2539
|
-
* - Prevents other processes from reading incomplete data during writes
|
|
2540
|
-
* - Particularly important for test files where incomplete writes could cause test failures
|
|
2541
|
-
*
|
|
2542
|
-
* The implementation writes to a temporary file first, then renames it to the target path.
|
|
2543
|
-
* This rename operation is atomic on most filesystems (including POSIX-compliant ones),
|
|
2544
|
-
* guaranteeing that other processes will only ever see the complete file.
|
|
2545
|
-
*
|
|
2546
|
-
* Added in https://github.com/vitest-dev/vitest/pull/7531
|
|
2547
|
-
*/
|
|
2548
|
-
async function atomicWriteFile(realFilePath, data) {
|
|
2549
|
-
const dir = dirname$1(realFilePath), tmpFilePath = join$1(dir, `.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
2550
|
-
try {
|
|
2551
|
-
await writeFile(tmpFilePath, data, "utf-8"), await rename(tmpFilePath, realFilePath);
|
|
2552
|
-
} finally {
|
|
2553
|
-
try {
|
|
2554
|
-
if (await stat(tmpFilePath)) await unlink(tmpFilePath);
|
|
2555
|
-
} catch {}
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
// this is copy pasted from vite
|
|
2560
|
-
function normalizeResolvedIdToUrl(environment, resolvedId) {
|
|
2561
|
-
const root = environment.config.root, depsOptimizer = environment.depsOptimizer;
|
|
2562
|
-
let url;
|
|
2563
|
-
// normalize all imports into resolved URLs
|
|
2564
|
-
// e.g. `import 'foo'` -> `import '/@fs/.../node_modules/foo/index.js'`
|
|
2565
|
-
if (resolvedId.startsWith(withTrailingSlash$1(root)))
|
|
2566
|
-
// in root: infer short absolute path from root
|
|
2567
|
-
url = resolvedId.slice(root.length);
|
|
2568
|
-
else if (depsOptimizer?.isOptimizedDepFile(resolvedId) || resolvedId !== "/@react-refresh" && path.isAbsolute(resolvedId) && existsSync(cleanUrl(resolvedId)))
|
|
2569
|
-
// an optimized deps may not yet exists in the filesystem, or
|
|
2570
|
-
// a regular file exists but is out of root: rewrite to absolute /@fs/ paths
|
|
2571
|
-
url = path.posix.join("/@fs/", resolvedId);
|
|
2572
|
-
else url = resolvedId;
|
|
2573
|
-
// if the resolved id is not a valid browser import specifier,
|
|
2574
|
-
// prefix it to make it valid. We will strip this before feeding it
|
|
2575
|
-
// back into the transform pipeline
|
|
2576
|
-
if (url[0] !== "." && url[0] !== "/") url = wrapId(resolvedId);
|
|
2577
|
-
return url;
|
|
2578
|
-
}
|
|
2579
|
-
|
|
2580
|
-
function createMethodsRPC(project, options = {}) {
|
|
2581
|
-
const ctx = project.vitest, cacheFs = options.cacheFs ?? false, fetch = createFetchModuleFunction(project._resolver, cacheFs, project.tmpDir);
|
|
2582
|
-
return {
|
|
2583
|
-
async fetch(url, importer, environmentName, options) {
|
|
2584
|
-
const environment = project.vite.environments[environmentName];
|
|
2585
|
-
if (!environment) throw new Error(`The environment ${environmentName} was not defined in the Vite config.`);
|
|
2586
|
-
const start = performance.now();
|
|
2587
|
-
try {
|
|
2588
|
-
return await fetch(url, importer, environment, options);
|
|
2589
|
-
} finally {
|
|
2590
|
-
project.vitest.state.transformTime += performance.now() - start;
|
|
2591
|
-
}
|
|
2592
|
-
},
|
|
2593
|
-
async resolve(id, importer, environmentName) {
|
|
2594
|
-
const environment = project.vite.environments[environmentName];
|
|
2595
|
-
if (!environment) throw new Error(`The environment ${environmentName} was not defined in the Vite config.`);
|
|
2596
|
-
const resolved = await environment.pluginContainer.resolveId(id, importer);
|
|
2597
|
-
return resolved ? {
|
|
2598
|
-
file: cleanUrl(resolved.id),
|
|
2599
|
-
url: normalizeResolvedIdToUrl(environment, resolved.id),
|
|
2600
|
-
id: resolved.id
|
|
2601
|
-
} : null;
|
|
2602
|
-
},
|
|
2603
|
-
snapshotSaved(snapshot) {
|
|
2604
|
-
ctx.snapshot.add(snapshot);
|
|
2605
|
-
},
|
|
2606
|
-
resolveSnapshotPath(testPath) {
|
|
2607
|
-
return ctx.snapshot.resolvePath(testPath, { config: project.serializedConfig });
|
|
2608
|
-
},
|
|
2609
|
-
async transform(id) {
|
|
2610
|
-
const environment = project.vite.environments.__vitest_vm__;
|
|
2611
|
-
if (!environment) throw new Error(`The VM environment was not defined in the Vite config. This is a bug in Vitest. Please, open a new issue with reproduction.`);
|
|
2612
|
-
const url = normalizeResolvedIdToUrl(environment, fileURLToPath$1(id)), result = await environment.transformRequest(url).catch(handleRollupError);
|
|
2613
|
-
return { code: result?.code };
|
|
2614
|
-
},
|
|
2615
|
-
async onQueued(file) {
|
|
2616
|
-
if (options.collect) ctx.state.collectFiles(project, [file]);
|
|
2617
|
-
else await ctx._testRun.enqueued(project, file);
|
|
2618
|
-
},
|
|
2619
|
-
async onCollected(files) {
|
|
2620
|
-
if (options.collect) ctx.state.collectFiles(project, files);
|
|
2621
|
-
else await ctx._testRun.collected(project, files);
|
|
2622
|
-
},
|
|
2623
|
-
onAfterSuiteRun(meta) {
|
|
2624
|
-
ctx.coverageProvider?.onAfterSuiteRun(meta);
|
|
2625
|
-
},
|
|
2626
|
-
async onTaskAnnotate(testId, annotation) {
|
|
2627
|
-
return ctx._testRun.annotate(testId, annotation);
|
|
2628
|
-
},
|
|
2629
|
-
async onTaskUpdate(packs, events) {
|
|
2630
|
-
if (options.collect) ctx.state.updateTasks(packs);
|
|
2631
|
-
else await ctx._testRun.updated(packs, events);
|
|
2632
|
-
},
|
|
2633
|
-
async onUserConsoleLog(log) {
|
|
2634
|
-
if (options.collect) ctx.state.updateUserLog(log);
|
|
2635
|
-
else await ctx._testRun.log(log);
|
|
2636
|
-
},
|
|
2637
|
-
onUnhandledError(err, type) {
|
|
2638
|
-
ctx.state.catchError(err, type);
|
|
2639
|
-
},
|
|
2640
|
-
onCancel(reason) {
|
|
2641
|
-
ctx.cancelCurrentRun(reason);
|
|
2642
|
-
},
|
|
2643
|
-
getCountOfFailedTests() {
|
|
2644
|
-
return ctx.state.getCountOfFailedTests();
|
|
2645
|
-
}
|
|
2646
|
-
};
|
|
2647
|
-
}
|
|
2648
|
-
|
|
2649
|
-
function createChildProcessChannel$1(project, collect = false) {
|
|
2650
|
-
const emitter = new EventEmitter(), events = {
|
|
2651
|
-
message: "message",
|
|
2652
|
-
response: "response"
|
|
2653
|
-
}, rpc = createBirpc(createMethodsRPC(project, {
|
|
2654
|
-
cacheFs: true,
|
|
2655
|
-
collect
|
|
2656
|
-
}), {
|
|
2657
|
-
eventNames: ["onCancel"],
|
|
2658
|
-
serialize: v8.serialize,
|
|
2659
|
-
deserialize: (v) => {
|
|
2660
|
-
try {
|
|
2661
|
-
return v8.deserialize(Buffer.from(v));
|
|
2662
|
-
} catch (error) {
|
|
2663
|
-
let stringified = "";
|
|
2664
|
-
try {
|
|
2665
|
-
stringified = `\nReceived value: ${JSON.stringify(v)}`;
|
|
2666
|
-
} catch {}
|
|
2667
|
-
throw new Error(`[vitest-pool]: Unexpected call to process.send(). Make sure your test cases are not interfering with process's channel.${stringified}`, { cause: error });
|
|
2668
|
-
}
|
|
2669
|
-
},
|
|
2670
|
-
post(v) {
|
|
2671
|
-
emitter.emit(events.message, v);
|
|
2672
|
-
},
|
|
2673
|
-
on(fn) {
|
|
2674
|
-
emitter.on(events.response, fn);
|
|
2675
|
-
},
|
|
2676
|
-
timeout: -1
|
|
2677
|
-
});
|
|
2678
|
-
project.vitest.onCancel((reason) => rpc.onCancel(reason));
|
|
2679
|
-
const channel = {
|
|
2680
|
-
onMessage: (callback) => emitter.on(events.message, callback),
|
|
2681
|
-
postMessage: (message) => emitter.emit(events.response, message),
|
|
2682
|
-
onClose: () => {
|
|
2683
|
-
emitter.removeAllListeners(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
|
|
2684
|
-
}
|
|
2685
|
-
};
|
|
2686
|
-
return channel;
|
|
2687
|
-
}
|
|
2688
|
-
function createForksPool(vitest, { execArgv, env }) {
|
|
2689
|
-
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.forks ?? {}, maxThreads = poolOptions.maxForks ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.minForks ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/forks.js"), options = {
|
|
2690
|
-
runtime: "child_process",
|
|
2691
|
-
filename: resolve(vitest.distPath, "worker.js"),
|
|
2692
|
-
teardown: "teardown",
|
|
2693
|
-
maxThreads,
|
|
2694
|
-
minThreads,
|
|
2695
|
-
env,
|
|
2696
|
-
execArgv: [...poolOptions.execArgv ?? [], ...execArgv],
|
|
2697
|
-
terminateTimeout: vitest.config.teardownTimeout,
|
|
2698
|
-
concurrentTasksPerWorker: 1
|
|
2699
|
-
}, isolated = poolOptions.isolate ?? true;
|
|
2700
|
-
if (isolated) options.isolateWorkers = true;
|
|
2701
|
-
if (poolOptions.singleFork || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
|
|
2702
|
-
const pool = new Tinypool(options), runWithFiles = (name) => {
|
|
2703
|
-
let id = 0;
|
|
2704
|
-
async function runFiles(project, config, files, environment, invalidates = []) {
|
|
2705
|
-
const paths = files.map((f) => f.filepath);
|
|
2706
|
-
vitest.state.clearFiles(project, paths);
|
|
2707
|
-
const channel = createChildProcessChannel$1(project, name === "collect"), workerId = ++id, data = {
|
|
2708
|
-
pool: "forks",
|
|
2709
|
-
worker,
|
|
2710
|
-
config,
|
|
2711
|
-
files,
|
|
2712
|
-
invalidates,
|
|
2713
|
-
environment,
|
|
2714
|
-
workerId,
|
|
2715
|
-
projectName: project.name,
|
|
2716
|
-
providedContext: project.getProvidedContext()
|
|
2717
|
-
};
|
|
2718
|
-
try {
|
|
2719
|
-
await pool.run(data, {
|
|
2720
|
-
name,
|
|
2721
|
-
channel
|
|
2722
|
-
});
|
|
2723
|
-
} catch (error) {
|
|
2724
|
-
// Worker got stuck and won't terminate - this may cause process to hang
|
|
2725
|
-
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) vitest.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}.`);
|
|
2726
|
-
else if (vitest.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) vitest.state.cancelFiles(paths, project);
|
|
2727
|
-
else throw error;
|
|
2728
|
-
}
|
|
2729
|
-
}
|
|
2730
|
-
return async (specs, invalidates) => {
|
|
2731
|
-
// Cancel pending tasks from pool when possible
|
|
2732
|
-
vitest.onCancel(() => pool.cancelPendingTasks());
|
|
2733
|
-
const configs = /* @__PURE__ */ new WeakMap(), getConfig = (project) => {
|
|
2734
|
-
if (configs.has(project)) return configs.get(project);
|
|
2735
|
-
const _config = project.serializedConfig, config = wrapSerializableConfig(_config);
|
|
2736
|
-
return configs.set(project, config), config;
|
|
2737
|
-
}, singleFork = specs.filter((spec) => spec.project.config.poolOptions?.forks?.singleFork), multipleForks = specs.filter((spec) => !spec.project.config.poolOptions?.forks?.singleFork);
|
|
2738
|
-
if (multipleForks.length) {
|
|
2739
|
-
const filesByEnv = await groupFilesByEnv(multipleForks), files = Object.values(filesByEnv).flat(), results = [];
|
|
2740
|
-
if (isolated) results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
|
|
2741
|
-
else {
|
|
2742
|
-
// When isolation is disabled, we still need to isolate environments and workspace projects from each other.
|
|
2743
|
-
// Tasks are still running parallel but environments are isolated between tasks.
|
|
2744
|
-
const grouped = groupBy(files, ({ project, environment }) => project.name + environment.name + JSON.stringify(environment.options));
|
|
2745
|
-
for (const group of Object.values(grouped)) results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)))), await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve)), await pool.recycleWorkers();
|
|
2746
|
-
}
|
|
2747
|
-
const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
2748
|
-
if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
|
|
2749
|
-
}
|
|
2750
|
-
if (singleFork.length) {
|
|
2751
|
-
const filesByEnv = await groupFilesByEnv(singleFork), envs = envsOrder.concat(Object.keys(filesByEnv).filter((env) => !envsOrder.includes(env)));
|
|
2752
|
-
for (const env of envs) {
|
|
2753
|
-
const files = filesByEnv[env];
|
|
2754
|
-
if (!files?.length) continue;
|
|
2755
|
-
const filesByOptions = groupBy(files, ({ project, environment }) => project.name + JSON.stringify(environment.options));
|
|
2756
|
-
for (const files of Object.values(filesByOptions)) {
|
|
2757
|
-
// Always run environments isolated between each other
|
|
2758
|
-
await pool.recycleWorkers();
|
|
2759
|
-
const filenames = files.map((f) => f.file);
|
|
2760
|
-
await runFiles(files[0].project, getConfig(files[0].project), filenames, files[0].environment, invalidates);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
}
|
|
2764
|
-
};
|
|
2765
|
-
};
|
|
2766
|
-
return {
|
|
2767
|
-
name: "forks",
|
|
2768
|
-
runTests: runWithFiles("run"),
|
|
2769
|
-
collectTests: runWithFiles("collect"),
|
|
2770
|
-
close: () => pool.destroy()
|
|
2771
|
-
};
|
|
2772
|
-
}
|
|
2773
|
-
|
|
2774
|
-
function createWorkerChannel$1(project, collect) {
|
|
2775
|
-
const channel = new MessageChannel(), port = channel.port2, workerPort = channel.port1, rpc = createBirpc(createMethodsRPC(project, { collect }), {
|
|
2776
|
-
eventNames: ["onCancel"],
|
|
2777
|
-
post(v) {
|
|
2778
|
-
port.postMessage(v);
|
|
2779
|
-
},
|
|
2780
|
-
on(fn) {
|
|
2781
|
-
port.on("message", fn);
|
|
2782
|
-
},
|
|
2783
|
-
timeout: -1
|
|
2784
|
-
});
|
|
2785
|
-
project.vitest.onCancel((reason) => rpc.onCancel(reason));
|
|
2786
|
-
const onClose = () => {
|
|
2787
|
-
port.close(), workerPort.close(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
|
|
2788
|
-
};
|
|
2789
|
-
return {
|
|
2790
|
-
workerPort,
|
|
2791
|
-
port,
|
|
2792
|
-
onClose
|
|
2793
|
-
};
|
|
2794
|
-
}
|
|
2795
|
-
function createThreadsPool(vitest, { execArgv, env }) {
|
|
2796
|
-
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.threads ?? {}, maxThreads = poolOptions.maxThreads ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.minThreads ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/threads.js"), options = {
|
|
2797
|
-
filename: resolve(vitest.distPath, "worker.js"),
|
|
2798
|
-
teardown: "teardown",
|
|
2799
|
-
useAtomics: poolOptions.useAtomics ?? false,
|
|
2800
|
-
maxThreads,
|
|
2801
|
-
minThreads,
|
|
2802
|
-
env,
|
|
2803
|
-
execArgv: [...poolOptions.execArgv ?? [], ...execArgv],
|
|
2804
|
-
terminateTimeout: vitest.config.teardownTimeout,
|
|
2805
|
-
concurrentTasksPerWorker: 1
|
|
2806
|
-
}, isolated = poolOptions.isolate ?? true;
|
|
2807
|
-
if (isolated) options.isolateWorkers = true;
|
|
2808
|
-
if (poolOptions.singleThread || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
|
|
2809
|
-
const pool = new Tinypool$1(options), runWithFiles = (name) => {
|
|
2810
|
-
let id = 0;
|
|
2811
|
-
async function runFiles(project, config, files, environment, invalidates = []) {
|
|
2812
|
-
const paths = files.map((f) => f.filepath);
|
|
2813
|
-
vitest.state.clearFiles(project, paths);
|
|
2814
|
-
const { workerPort, onClose } = createWorkerChannel$1(project, name === "collect"), workerId = ++id, data = {
|
|
2815
|
-
pool: "threads",
|
|
2816
|
-
worker,
|
|
2817
|
-
port: workerPort,
|
|
2818
|
-
config,
|
|
2819
|
-
files,
|
|
2820
|
-
invalidates,
|
|
2821
|
-
environment,
|
|
2822
|
-
workerId,
|
|
2823
|
-
projectName: project.name,
|
|
2824
|
-
providedContext: project.getProvidedContext()
|
|
2825
|
-
};
|
|
2826
|
-
try {
|
|
2827
|
-
await pool.run(data, {
|
|
2828
|
-
transferList: [workerPort],
|
|
2829
|
-
name,
|
|
2830
|
-
channel: { onClose }
|
|
2831
|
-
});
|
|
2832
|
-
} catch (error) {
|
|
2833
|
-
// Worker got stuck and won't terminate - this may cause process to hang
|
|
2834
|
-
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) vitest.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}. \nSee https://vitest.dev/guide/common-errors.html#failed-to-terminate-worker for troubleshooting.`);
|
|
2835
|
-
else if (vitest.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) vitest.state.cancelFiles(paths, project);
|
|
2836
|
-
else throw error;
|
|
2837
|
-
}
|
|
2838
|
-
}
|
|
2839
|
-
return async (specs, invalidates) => {
|
|
2840
|
-
// Cancel pending tasks from pool when possible
|
|
2841
|
-
vitest.onCancel(() => pool.cancelPendingTasks());
|
|
2842
|
-
const configs = /* @__PURE__ */ new WeakMap(), getConfig = (project) => {
|
|
2843
|
-
if (configs.has(project)) return configs.get(project);
|
|
2844
|
-
const config = project.serializedConfig;
|
|
2845
|
-
return configs.set(project, config), config;
|
|
2846
|
-
}, singleThreads = specs.filter((spec) => spec.project.config.poolOptions?.threads?.singleThread), multipleThreads = specs.filter((spec) => !spec.project.config.poolOptions?.threads?.singleThread);
|
|
2847
|
-
if (multipleThreads.length) {
|
|
2848
|
-
const filesByEnv = await groupFilesByEnv(multipleThreads), files = Object.values(filesByEnv).flat(), results = [];
|
|
2849
|
-
if (isolated) results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))));
|
|
2850
|
-
else {
|
|
2851
|
-
// When isolation is disabled, we still need to isolate environments and workspace projects from each other.
|
|
2852
|
-
// Tasks are still running parallel but environments are isolated between tasks.
|
|
2853
|
-
const grouped = groupBy(files, ({ project, environment }) => project.name + environment.name + JSON.stringify(environment.options));
|
|
2854
|
-
for (const group of Object.values(grouped)) results.push(...await Promise.allSettled(group.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)))), await new Promise((resolve) => pool.queueSize === 0 ? resolve() : pool.once("drain", resolve)), await pool.recycleWorkers();
|
|
2855
|
-
}
|
|
2856
|
-
const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
2857
|
-
if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
|
|
2858
|
-
}
|
|
2859
|
-
if (singleThreads.length) {
|
|
2860
|
-
const filesByEnv = await groupFilesByEnv(singleThreads), envs = envsOrder.concat(Object.keys(filesByEnv).filter((env) => !envsOrder.includes(env)));
|
|
2861
|
-
for (const env of envs) {
|
|
2862
|
-
const files = filesByEnv[env];
|
|
2863
|
-
if (!files?.length) continue;
|
|
2864
|
-
const filesByOptions = groupBy(files, ({ project, environment }) => project.name + JSON.stringify(environment.options));
|
|
2865
|
-
for (const files of Object.values(filesByOptions)) {
|
|
2866
|
-
// Always run environments isolated between each other
|
|
2867
|
-
await pool.recycleWorkers();
|
|
2868
|
-
const filenames = files.map((f) => f.file);
|
|
2869
|
-
await runFiles(files[0].project, getConfig(files[0].project), filenames, files[0].environment, invalidates);
|
|
2870
|
-
}
|
|
2871
|
-
}
|
|
2872
|
-
}
|
|
2873
|
-
};
|
|
2874
|
-
};
|
|
2875
|
-
return {
|
|
2876
|
-
name: "threads",
|
|
2877
|
-
runTests: runWithFiles("run"),
|
|
2878
|
-
collectTests: runWithFiles("collect"),
|
|
2879
|
-
close: () => pool.destroy()
|
|
2880
|
-
};
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
function createTypecheckPool(vitest) {
|
|
2884
|
-
const promisesMap = /* @__PURE__ */ new WeakMap(), rerunTriggered = /* @__PURE__ */ new WeakSet();
|
|
2885
|
-
async function onParseEnd(project, { files, sourceErrors }) {
|
|
2886
|
-
const checker = project.typechecker, { packs, events } = checker.getTestPacksAndEvents();
|
|
2887
|
-
if (await vitest._testRun.updated(packs, events), !project.config.typecheck.ignoreSourceErrors) sourceErrors.forEach((error) => vitest.state.catchError(error, "Unhandled Source Error"));
|
|
2888
|
-
const processError = !hasFailed(files) && !sourceErrors.length && checker.getExitCode();
|
|
2889
|
-
if (processError) {
|
|
2890
|
-
const error = new Error(checker.getOutput());
|
|
2891
|
-
error.stack = "", vitest.state.catchError(error, "Typecheck Error");
|
|
2892
|
-
}
|
|
2893
|
-
// triggered by TSC watcher, not Vitest watcher, so we need to emulate what Vitest does in this case
|
|
2894
|
-
if (promisesMap.get(project)?.resolve(), rerunTriggered.delete(project), vitest.config.watch && !vitest.runningPromise) {
|
|
2895
|
-
const modules = files.map((file) => vitest.state.getReportedEntity(file)).filter((e) => e?.type === "module"), state = vitest.isCancelling ? "interrupted" : modules.some((m) => !m.ok()) ? "failed" : "passed";
|
|
2896
|
-
await vitest.report("onTestRunEnd", modules, [], state), await vitest.report("onWatcherStart", files, [...project.config.typecheck.ignoreSourceErrors ? [] : sourceErrors, ...vitest.state.getUnhandledErrors()]);
|
|
2897
|
-
}
|
|
2898
|
-
}
|
|
2899
|
-
async function createWorkspaceTypechecker(project, files) {
|
|
2900
|
-
const checker = project.typechecker ?? new Typechecker(project);
|
|
2901
|
-
return project.typechecker ? checker : (project.typechecker = checker, checker.setFiles(files), checker.onParseStart(async () => {
|
|
2902
|
-
const files = checker.getTestFiles();
|
|
2903
|
-
for (const file of files) await vitest._testRun.enqueued(project, file);
|
|
2904
|
-
await vitest._testRun.collected(project, files);
|
|
2905
|
-
}), checker.onParseEnd((result) => onParseEnd(project, result)), checker.onWatcherRerun(async () => {
|
|
2906
|
-
if (rerunTriggered.add(project), !vitest.runningPromise) vitest.state.clearErrors(), await vitest.report("onWatcherRerun", files, "File change detected. Triggering rerun.");
|
|
2907
|
-
await checker.collectTests();
|
|
2908
|
-
const testFiles = checker.getTestFiles();
|
|
2909
|
-
for (const file of testFiles) await vitest._testRun.enqueued(project, file);
|
|
2910
|
-
await vitest._testRun.collected(project, testFiles);
|
|
2911
|
-
const { packs, events } = checker.getTestPacksAndEvents();
|
|
2912
|
-
await vitest._testRun.updated(packs, events);
|
|
2913
|
-
}), checker);
|
|
2914
|
-
}
|
|
2915
|
-
async function startTypechecker(project, files) {
|
|
2916
|
-
if (project.typechecker) return;
|
|
2917
|
-
const checker = await createWorkspaceTypechecker(project, files);
|
|
2918
|
-
await checker.collectTests(), await checker.start();
|
|
2919
|
-
}
|
|
2920
|
-
async function collectTests(specs) {
|
|
2921
|
-
const specsByProject = groupBy(specs, (spec) => spec.project.name);
|
|
2922
|
-
for (const name in specsByProject) {
|
|
2923
|
-
const project = specsByProject[name][0].project, files = specsByProject[name].map((spec) => spec.moduleId), checker = await createWorkspaceTypechecker(project, files);
|
|
2924
|
-
checker.setFiles(files), await checker.collectTests();
|
|
2925
|
-
const testFiles = checker.getTestFiles();
|
|
2926
|
-
vitest.state.collectFiles(project, testFiles);
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
2929
|
-
async function runTests(specs) {
|
|
2930
|
-
const specsByProject = groupBy(specs, (spec) => spec.project.name), promises = [];
|
|
2931
|
-
for (const name in specsByProject) {
|
|
2932
|
-
const project = specsByProject[name][0].project, files = specsByProject[name].map((spec) => spec.moduleId), promise = createDefer(), _p = new Promise((resolve) => {
|
|
2933
|
-
const _i = setInterval(() => {
|
|
2934
|
-
if (!project.typechecker || rerunTriggered.has(project)) resolve(true), clearInterval(_i);
|
|
2935
|
-
});
|
|
2936
|
-
setTimeout(() => {
|
|
2937
|
-
resolve(false), clearInterval(_i);
|
|
2938
|
-
}, 500).unref();
|
|
2939
|
-
}), triggered = await _p;
|
|
2940
|
-
if (project.typechecker && !triggered) {
|
|
2941
|
-
const testFiles = project.typechecker.getTestFiles();
|
|
2942
|
-
for (const file of testFiles) await vitest._testRun.enqueued(project, file);
|
|
2943
|
-
await vitest._testRun.collected(project, testFiles), await onParseEnd(project, project.typechecker.getResult());
|
|
2944
|
-
continue;
|
|
2945
|
-
}
|
|
2946
|
-
promises.push(promise), promisesMap.set(project, promise), promises.push(startTypechecker(project, files));
|
|
2947
|
-
}
|
|
2948
|
-
await Promise.all(promises);
|
|
2949
|
-
}
|
|
2950
|
-
return {
|
|
2951
|
-
name: "typescript",
|
|
2952
|
-
runTests,
|
|
2953
|
-
collectTests,
|
|
2954
|
-
async close() {
|
|
2955
|
-
const promises = vitest.projects.map((project) => project.typechecker?.stop());
|
|
2956
|
-
await Promise.all(promises);
|
|
2957
|
-
}
|
|
2958
|
-
};
|
|
2959
|
-
}
|
|
2960
|
-
|
|
2961
|
-
function getDefaultThreadsCount(config) {
|
|
2962
|
-
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
|
|
2963
|
-
return config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
|
|
2964
|
-
}
|
|
2965
|
-
function getWorkerMemoryLimit(config, pool) {
|
|
2966
|
-
if (pool === "vmForks") {
|
|
2967
|
-
const opts = config.poolOptions?.vmForks ?? {};
|
|
2968
|
-
if (opts.memoryLimit) return opts.memoryLimit;
|
|
2969
|
-
const workers = opts.maxForks ?? getDefaultThreadsCount(config);
|
|
2970
|
-
return 1 / workers;
|
|
2971
|
-
} else {
|
|
2972
|
-
const opts = config.poolOptions?.vmThreads ?? {};
|
|
2973
|
-
if (opts.memoryLimit) return opts.memoryLimit;
|
|
2974
|
-
const workers = opts.maxThreads ?? getDefaultThreadsCount(config);
|
|
2975
|
-
return 1 / workers;
|
|
2976
|
-
}
|
|
2977
|
-
}
|
|
2978
|
-
/**
|
|
2979
|
-
* Converts a string representing an amount of memory to bytes.
|
|
2980
|
-
*
|
|
2981
|
-
* @param input The value to convert to bytes.
|
|
2982
|
-
* @param percentageReference The reference value to use when a '%' value is supplied.
|
|
2983
|
-
*/
|
|
2984
|
-
function stringToBytes(input, percentageReference) {
|
|
2985
|
-
if (input === null || input === void 0) return input;
|
|
2986
|
-
if (typeof input === "string") if (Number.isNaN(Number.parseFloat(input.slice(-1)))) {
|
|
2987
|
-
let [, numericString, trailingChars] = input.match(/(.*?)([^0-9.-]+)$/) || [];
|
|
2988
|
-
if (trailingChars && numericString) {
|
|
2989
|
-
const numericValue = Number.parseFloat(numericString);
|
|
2990
|
-
switch (trailingChars = trailingChars.toLowerCase(), trailingChars) {
|
|
2991
|
-
case "%":
|
|
2992
|
-
input = numericValue / 100;
|
|
2993
|
-
break;
|
|
2994
|
-
case "kb":
|
|
2995
|
-
case "k": return numericValue * 1e3;
|
|
2996
|
-
case "kib": return numericValue * 1024;
|
|
2997
|
-
case "mb":
|
|
2998
|
-
case "m": return numericValue * 1e3 * 1e3;
|
|
2999
|
-
case "mib": return numericValue * 1024 * 1024;
|
|
3000
|
-
case "gb":
|
|
3001
|
-
case "g": return numericValue * 1e3 * 1e3 * 1e3;
|
|
3002
|
-
case "gib": return numericValue * 1024 * 1024 * 1024;
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
3005
|
-
} else input = Number.parseFloat(input);
|
|
3006
|
-
if (typeof input === "number") if (input <= 1 && input > 0) {
|
|
3007
|
-
if (percentageReference) return Math.floor(input * percentageReference);
|
|
3008
|
-
throw new Error("For a percentage based memory limit a percentageReference must be supplied");
|
|
3009
|
-
} else if (input > 1) return Math.floor(input);
|
|
3010
|
-
else throw new Error("Unexpected numerical input for \"memoryLimit\"");
|
|
3011
|
-
return null;
|
|
3012
|
-
}
|
|
3013
|
-
|
|
3014
|
-
const suppressWarningsPath$1 = resolve(rootDir, "./suppress-warnings.cjs");
|
|
3015
|
-
function createChildProcessChannel(project, collect) {
|
|
3016
|
-
const emitter = new EventEmitter(), events = {
|
|
3017
|
-
message: "message",
|
|
3018
|
-
response: "response"
|
|
3019
|
-
}, rpc = createBirpc(createMethodsRPC(project, {
|
|
3020
|
-
cacheFs: true,
|
|
3021
|
-
collect
|
|
3022
|
-
}), {
|
|
3023
|
-
eventNames: ["onCancel"],
|
|
3024
|
-
serialize: v8.serialize,
|
|
3025
|
-
deserialize: (v) => {
|
|
3026
|
-
try {
|
|
3027
|
-
return v8.deserialize(Buffer.from(v));
|
|
3028
|
-
} catch (error) {
|
|
3029
|
-
let stringified = "";
|
|
3030
|
-
try {
|
|
3031
|
-
stringified = `\nReceived value: ${JSON.stringify(v)}`;
|
|
3032
|
-
} catch {}
|
|
3033
|
-
throw new Error(`[vitest-pool]: Unexpected call to process.send(). Make sure your test cases are not interfering with process's channel.${stringified}`, { cause: error });
|
|
3034
|
-
}
|
|
3035
|
-
},
|
|
3036
|
-
post(v) {
|
|
3037
|
-
emitter.emit(events.message, v);
|
|
3038
|
-
},
|
|
3039
|
-
on(fn) {
|
|
3040
|
-
emitter.on(events.response, fn);
|
|
3041
|
-
},
|
|
3042
|
-
timeout: -1
|
|
3043
|
-
});
|
|
3044
|
-
project.vitest.onCancel((reason) => rpc.onCancel(reason));
|
|
3045
|
-
const channel = {
|
|
3046
|
-
onMessage: (callback) => emitter.on(events.message, callback),
|
|
3047
|
-
postMessage: (message) => emitter.emit(events.response, message),
|
|
3048
|
-
onClose: () => {
|
|
3049
|
-
emitter.removeAllListeners(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
|
|
3050
|
-
}
|
|
3051
|
-
};
|
|
3052
|
-
return { channel };
|
|
3053
|
-
}
|
|
3054
|
-
function createVmForksPool(vitest, { execArgv, env }) {
|
|
3055
|
-
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.vmForks ?? {}, maxThreads = poolOptions.maxForks ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.maxForks ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/vmForks.js"), options = {
|
|
3056
|
-
runtime: "child_process",
|
|
3057
|
-
filename: resolve(vitest.distPath, "worker.js"),
|
|
3058
|
-
maxThreads,
|
|
3059
|
-
minThreads,
|
|
3060
|
-
env,
|
|
3061
|
-
execArgv: [
|
|
3062
|
-
"--experimental-import-meta-resolve",
|
|
3063
|
-
"--experimental-vm-modules",
|
|
3064
|
-
"--require",
|
|
3065
|
-
suppressWarningsPath$1,
|
|
3066
|
-
...poolOptions.execArgv ?? [],
|
|
3067
|
-
...execArgv
|
|
3068
|
-
],
|
|
3069
|
-
terminateTimeout: vitest.config.teardownTimeout,
|
|
3070
|
-
concurrentTasksPerWorker: 1,
|
|
3071
|
-
maxMemoryLimitBeforeRecycle: getMemoryLimit$1(vitest.config) || void 0
|
|
3072
|
-
};
|
|
3073
|
-
if (poolOptions.singleFork || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
|
|
3074
|
-
const pool = new Tinypool$1(options), runWithFiles = (name) => {
|
|
3075
|
-
let id = 0;
|
|
3076
|
-
async function runFiles(project, config, files, environment, invalidates = []) {
|
|
3077
|
-
const paths = files.map((f) => f.filepath);
|
|
3078
|
-
vitest.state.clearFiles(project, paths);
|
|
3079
|
-
const { channel } = createChildProcessChannel(project, name === "collect"), workerId = ++id, data = {
|
|
3080
|
-
pool: "forks",
|
|
3081
|
-
worker,
|
|
3082
|
-
config,
|
|
3083
|
-
files,
|
|
3084
|
-
invalidates,
|
|
3085
|
-
environment,
|
|
3086
|
-
workerId,
|
|
3087
|
-
projectName: project.name,
|
|
3088
|
-
providedContext: project.getProvidedContext()
|
|
3089
|
-
};
|
|
3090
|
-
try {
|
|
3091
|
-
await pool.run(data, {
|
|
3092
|
-
name,
|
|
3093
|
-
channel
|
|
3094
|
-
});
|
|
3095
|
-
} catch (error) {
|
|
3096
|
-
// Worker got stuck and won't terminate - this may cause process to hang
|
|
3097
|
-
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) vitest.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}.`);
|
|
3098
|
-
else if (vitest.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) vitest.state.cancelFiles(paths, project);
|
|
3099
|
-
else throw error;
|
|
3100
|
-
} finally {
|
|
3101
|
-
channel.onClose();
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
return async (specs, invalidates) => {
|
|
3105
|
-
// Cancel pending tasks from pool when possible
|
|
3106
|
-
vitest.onCancel(() => pool.cancelPendingTasks());
|
|
3107
|
-
const configs = /* @__PURE__ */ new Map(), getConfig = (project) => {
|
|
3108
|
-
if (configs.has(project)) return configs.get(project);
|
|
3109
|
-
const _config = project.serializedConfig, config = wrapSerializableConfig(_config);
|
|
3110
|
-
return configs.set(project, config), config;
|
|
3111
|
-
}, filesByEnv = await groupFilesByEnv(specs), promises = Object.values(filesByEnv).flat(), results = await Promise.allSettled(promises.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))), errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
3112
|
-
if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
|
|
3113
|
-
};
|
|
3114
|
-
};
|
|
3115
|
-
return {
|
|
3116
|
-
name: "vmForks",
|
|
3117
|
-
runTests: runWithFiles("run"),
|
|
3118
|
-
collectTests: runWithFiles("collect"),
|
|
3119
|
-
close: () => pool.destroy()
|
|
3120
|
-
};
|
|
3121
|
-
}
|
|
3122
|
-
function getMemoryLimit$1(config) {
|
|
3123
|
-
const memory = nodeos.totalmem(), limit = getWorkerMemoryLimit(config, "vmForks");
|
|
3124
|
-
// just ignore "memoryLimit" value because we cannot detect memory limit
|
|
3125
|
-
return typeof memory === "number" ? stringToBytes(limit, config.watch ? memory / 2 : memory) : typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%" ? stringToBytes(limit) : null;
|
|
3126
|
-
}
|
|
3127
|
-
|
|
3128
|
-
const suppressWarningsPath = resolve(rootDir, "./suppress-warnings.cjs");
|
|
3129
|
-
function createWorkerChannel(project, collect) {
|
|
3130
|
-
const channel = new MessageChannel(), port = channel.port2, workerPort = channel.port1, rpc = createBirpc(createMethodsRPC(project, { collect }), {
|
|
3131
|
-
eventNames: ["onCancel"],
|
|
3132
|
-
post(v) {
|
|
3133
|
-
port.postMessage(v);
|
|
3134
|
-
},
|
|
3135
|
-
on(fn) {
|
|
3136
|
-
port.on("message", fn);
|
|
3137
|
-
},
|
|
3138
|
-
timeout: -1
|
|
3139
|
-
});
|
|
3140
|
-
project.vitest.onCancel((reason) => rpc.onCancel(reason));
|
|
3141
|
-
function onClose() {
|
|
3142
|
-
workerPort.close(), port.close(), rpc.$close(/* @__PURE__ */ new Error("[vitest-pool]: Pending methods while closing rpc"));
|
|
3143
|
-
}
|
|
3144
|
-
return {
|
|
3145
|
-
workerPort,
|
|
3146
|
-
onClose
|
|
3147
|
-
};
|
|
3148
|
-
}
|
|
3149
|
-
function createVmThreadsPool(vitest, { execArgv, env }) {
|
|
3150
|
-
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), poolOptions = vitest.config.poolOptions?.vmThreads ?? {}, maxThreads = poolOptions.maxThreads ?? vitest.config.maxWorkers ?? threadsCount, minThreads = poolOptions.minThreads ?? vitest.config.minWorkers ?? Math.min(threadsCount, maxThreads), worker = resolve(vitest.distPath, "workers/vmThreads.js"), options = {
|
|
3151
|
-
filename: resolve(vitest.distPath, "worker.js"),
|
|
3152
|
-
useAtomics: poolOptions.useAtomics ?? false,
|
|
3153
|
-
maxThreads,
|
|
3154
|
-
minThreads,
|
|
3155
|
-
env,
|
|
3156
|
-
execArgv: [
|
|
3157
|
-
"--experimental-import-meta-resolve",
|
|
3158
|
-
"--experimental-vm-modules",
|
|
3159
|
-
"--require",
|
|
3160
|
-
suppressWarningsPath,
|
|
3161
|
-
...poolOptions.execArgv ?? [],
|
|
3162
|
-
...execArgv
|
|
3163
|
-
],
|
|
3164
|
-
terminateTimeout: vitest.config.teardownTimeout,
|
|
3165
|
-
concurrentTasksPerWorker: 1,
|
|
3166
|
-
maxMemoryLimitBeforeRecycle: getMemoryLimit(vitest.config) || void 0
|
|
3167
|
-
};
|
|
3168
|
-
if (poolOptions.singleThread || !vitest.config.fileParallelism) options.maxThreads = 1, options.minThreads = 1;
|
|
3169
|
-
const pool = new Tinypool$1(options), runWithFiles = (name) => {
|
|
3170
|
-
let id = 0;
|
|
3171
|
-
async function runFiles(project, config, files, environment, invalidates = []) {
|
|
3172
|
-
const paths = files.map((f) => f.filepath);
|
|
3173
|
-
vitest.state.clearFiles(project, paths);
|
|
3174
|
-
const { workerPort, onClose } = createWorkerChannel(project, name === "collect"), workerId = ++id, data = {
|
|
3175
|
-
pool: "vmThreads",
|
|
3176
|
-
worker,
|
|
3177
|
-
port: workerPort,
|
|
3178
|
-
config,
|
|
3179
|
-
files: paths,
|
|
3180
|
-
invalidates,
|
|
3181
|
-
environment,
|
|
3182
|
-
workerId,
|
|
3183
|
-
projectName: project.name,
|
|
3184
|
-
providedContext: project.getProvidedContext()
|
|
3185
|
-
};
|
|
3186
|
-
try {
|
|
3187
|
-
await pool.run(data, {
|
|
3188
|
-
transferList: [workerPort],
|
|
3189
|
-
name
|
|
3190
|
-
});
|
|
3191
|
-
} catch (error) {
|
|
3192
|
-
// Worker got stuck and won't terminate - this may cause process to hang
|
|
3193
|
-
if (error instanceof Error && /Failed to terminate worker/.test(error.message)) vitest.state.addProcessTimeoutCause(`Failed to terminate worker while running ${paths.join(", ")}. \nSee https://vitest.dev/guide/common-errors.html#failed-to-terminate-worker for troubleshooting.`);
|
|
3194
|
-
else if (vitest.isCancelling && error instanceof Error && /The task has been cancelled/.test(error.message)) vitest.state.cancelFiles(paths, project);
|
|
3195
|
-
else throw error;
|
|
3196
|
-
} finally {
|
|
3197
|
-
onClose();
|
|
3198
|
-
}
|
|
3199
|
-
}
|
|
3200
|
-
return async (specs, invalidates) => {
|
|
3201
|
-
// Cancel pending tasks from pool when possible
|
|
3202
|
-
vitest.onCancel(() => pool.cancelPendingTasks());
|
|
3203
|
-
const configs = /* @__PURE__ */ new Map(), getConfig = (project) => {
|
|
3204
|
-
if (configs.has(project)) return configs.get(project);
|
|
3205
|
-
const config = project.serializedConfig;
|
|
3206
|
-
return configs.set(project, config), config;
|
|
3207
|
-
}, filesByEnv = await groupFilesByEnv(specs), promises = Object.values(filesByEnv).flat(), results = await Promise.allSettled(promises.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates))), errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
|
|
3208
|
-
if (errors.length > 0) throw new AggregateError(errors, "Errors occurred while running tests. For more information, see serialized error.");
|
|
3209
|
-
};
|
|
3210
|
-
};
|
|
3211
|
-
return {
|
|
3212
|
-
name: "vmThreads",
|
|
3213
|
-
runTests: runWithFiles("run"),
|
|
3214
|
-
collectTests: runWithFiles("collect"),
|
|
3215
|
-
close: () => pool.destroy()
|
|
3216
|
-
};
|
|
3217
|
-
}
|
|
3218
|
-
function getMemoryLimit(config) {
|
|
3219
|
-
const memory = nodeos.totalmem(), limit = getWorkerMemoryLimit(config, "vmThreads");
|
|
3220
|
-
// just ignore "memoryLimit" value because we cannot detect memory limit
|
|
3221
|
-
return typeof memory === "number" ? stringToBytes(limit, config.watch ? memory / 2 : memory) : typeof limit === "number" && limit > 1 || typeof limit === "string" && limit.at(-1) !== "%" ? stringToBytes(limit) : null;
|
|
3222
|
-
}
|
|
3223
|
-
|
|
3224
|
-
const builtinPools = [
|
|
3225
|
-
"forks",
|
|
3226
|
-
"threads",
|
|
3227
|
-
"browser",
|
|
3228
|
-
"vmThreads",
|
|
3229
|
-
"vmForks",
|
|
3230
|
-
"typescript"
|
|
3231
|
-
];
|
|
3232
|
-
function getDefaultPoolName(project) {
|
|
3233
|
-
return project.config.browser.enabled ? "browser" : project.config.pool;
|
|
3234
|
-
}
|
|
3235
|
-
function getFilePoolName(project) {
|
|
3236
|
-
return getDefaultPoolName(project);
|
|
3237
|
-
}
|
|
3238
|
-
function createPool(ctx) {
|
|
3239
|
-
const pools = {
|
|
3240
|
-
forks: null,
|
|
3241
|
-
threads: null,
|
|
3242
|
-
browser: null,
|
|
3243
|
-
vmThreads: null,
|
|
3244
|
-
vmForks: null,
|
|
3245
|
-
typescript: null
|
|
3246
|
-
}, viteMajor = Number(version.split(".")[0]), potentialConditions = new Set(viteMajor >= 6 ? ctx.vite.config.ssr.resolve?.conditions ?? [] : [
|
|
3247
|
-
"production",
|
|
3248
|
-
"development",
|
|
3249
|
-
...ctx.vite.config.resolve.conditions
|
|
3250
|
-
]), conditions = [...potentialConditions].filter((condition) => {
|
|
3251
|
-
return condition === "production" ? ctx.vite.config.isProduction : condition === "development" ? !ctx.vite.config.isProduction : true;
|
|
3252
|
-
}).map((condition) => {
|
|
3253
|
-
return viteMajor >= 6 && condition === "development|production" ? ctx.vite.config.isProduction ? "production" : "development" : condition;
|
|
3254
|
-
}).flatMap((c) => ["--conditions", c]), execArgv = process.execArgv.filter((execArg) => execArg.startsWith("--cpu-prof") || execArg.startsWith("--heap-prof") || execArg.startsWith("--diagnostic-dir"));
|
|
3255
|
-
async function executeTests(method, files, invalidate) {
|
|
3256
|
-
const options = {
|
|
3257
|
-
execArgv: [...execArgv, ...conditions],
|
|
3258
|
-
env: {
|
|
3259
|
-
TEST: "true",
|
|
3260
|
-
VITEST: "true",
|
|
3261
|
-
NODE_ENV: process.env.NODE_ENV || "test",
|
|
3262
|
-
VITEST_MODE: ctx.config.watch ? "WATCH" : "RUN",
|
|
3263
|
-
FORCE_TTY: isatty(1) ? "true" : "",
|
|
3264
|
-
...process.env,
|
|
3265
|
-
...ctx.config.env
|
|
3266
|
-
}
|
|
3267
|
-
};
|
|
3268
|
-
// env are case-insensitive on Windows, but spawned processes don't support it
|
|
3269
|
-
if (isWindows) for (const name in options.env) options.env[name.toUpperCase()] = options.env[name];
|
|
3270
|
-
const poolConcurrentPromises = /* @__PURE__ */ new Map(), customPools = /* @__PURE__ */ new Map();
|
|
3271
|
-
async function resolveCustomPool(filepath) {
|
|
3272
|
-
if (customPools.has(filepath)) return customPools.get(filepath);
|
|
3273
|
-
const pool = await ctx.runner.import(filepath);
|
|
3274
|
-
if (typeof pool.default !== "function") throw new TypeError(`Custom pool "${filepath}" must export a function as default export`);
|
|
3275
|
-
const poolInstance = await pool.default(ctx, options);
|
|
3276
|
-
if (typeof poolInstance?.name !== "string") throw new TypeError(`Custom pool "${filepath}" should return an object with "name" property`);
|
|
3277
|
-
if (typeof poolInstance?.[method] !== "function") throw new TypeError(`Custom pool "${filepath}" should return an object with "${method}" method`);
|
|
3278
|
-
return customPools.set(filepath, poolInstance), poolInstance;
|
|
3279
|
-
}
|
|
3280
|
-
function getConcurrentPool(pool, fn) {
|
|
3281
|
-
if (poolConcurrentPromises.has(pool)) return poolConcurrentPromises.get(pool);
|
|
3282
|
-
const promise = fn().finally(() => {
|
|
3283
|
-
poolConcurrentPromises.delete(pool);
|
|
3284
|
-
});
|
|
3285
|
-
return poolConcurrentPromises.set(pool, promise), promise;
|
|
3286
|
-
}
|
|
3287
|
-
function getCustomPool(pool) {
|
|
3288
|
-
return getConcurrentPool(pool, () => resolveCustomPool(pool));
|
|
3289
|
-
}
|
|
3290
|
-
function getBrowserPool() {
|
|
3291
|
-
return getConcurrentPool("browser", async () => {
|
|
3292
|
-
const { createBrowserPool } = await import('@vitest/browser');
|
|
3293
|
-
return createBrowserPool(ctx);
|
|
3294
|
-
});
|
|
3295
|
-
}
|
|
3296
|
-
const groupedSpecifications = {}, groups = /* @__PURE__ */ new Set(), factories = {
|
|
3297
|
-
vmThreads: () => createVmThreadsPool(ctx, options),
|
|
3298
|
-
vmForks: () => createVmForksPool(ctx, options),
|
|
3299
|
-
threads: () => createThreadsPool(ctx, options),
|
|
3300
|
-
forks: () => createForksPool(ctx, options),
|
|
3301
|
-
typescript: () => createTypecheckPool(ctx)
|
|
3302
|
-
};
|
|
3303
|
-
for (const spec of files) {
|
|
3304
|
-
const group = spec.project.config.sequence.groupOrder ?? 0;
|
|
3305
|
-
groups.add(group), groupedSpecifications[group] ??= [], groupedSpecifications[group].push(spec);
|
|
3306
|
-
}
|
|
3307
|
-
const Sequencer = ctx.config.sequence.sequencer, sequencer = new Sequencer(ctx);
|
|
3308
|
-
async function sortSpecs(specs) {
|
|
3309
|
-
if (ctx.config.shard) {
|
|
3310
|
-
if (!ctx.config.passWithNoTests && ctx.config.shard.count > specs.length) throw new Error(`--shard <count> must be a smaller than count of test files. Resolved ${specs.length} test files for --shard=${ctx.config.shard.index}/${ctx.config.shard.count}.`);
|
|
3311
|
-
specs = await sequencer.shard(specs);
|
|
3312
|
-
}
|
|
3313
|
-
return sequencer.sort(specs);
|
|
3314
|
-
}
|
|
3315
|
-
const sortedGroups = Array.from(groups).sort();
|
|
3316
|
-
for (const group of sortedGroups) {
|
|
3317
|
-
const specifications = groupedSpecifications[group];
|
|
3318
|
-
if (!specifications?.length) continue;
|
|
3319
|
-
const filesByPool = {
|
|
3320
|
-
forks: [],
|
|
3321
|
-
threads: [],
|
|
3322
|
-
vmThreads: [],
|
|
3323
|
-
vmForks: [],
|
|
3324
|
-
typescript: []
|
|
3325
|
-
};
|
|
3326
|
-
specifications.forEach((specification) => {
|
|
3327
|
-
const pool = specification.pool;
|
|
3328
|
-
filesByPool[pool] ??= [], filesByPool[pool].push(specification);
|
|
3329
|
-
}), await Promise.all(Object.entries(filesByPool).map(async (entry) => {
|
|
3330
|
-
const [pool, files] = entry;
|
|
3331
|
-
if (!files.length) return null;
|
|
3332
|
-
const specs = await sortSpecs(files);
|
|
3333
|
-
if (pool in factories) {
|
|
3334
|
-
const factory = factories[pool];
|
|
3335
|
-
return pools[pool] ??= factory(), pools[pool][method](specs, invalidate);
|
|
3336
|
-
}
|
|
3337
|
-
if (pool === "browser") return pools.browser ??= await getBrowserPool(), pools.browser[method](specs, invalidate);
|
|
3338
|
-
const poolHandler = await getCustomPool(pool);
|
|
3339
|
-
return pools[poolHandler.name] ??= poolHandler, poolHandler[method](specs, invalidate);
|
|
3340
|
-
}));
|
|
3341
|
-
}
|
|
3342
|
-
}
|
|
3343
|
-
return {
|
|
3344
|
-
name: "default",
|
|
3345
|
-
runTests: (files, invalidates) => executeTests("runTests", files, invalidates),
|
|
3346
|
-
collectTests: (files, invalidates) => executeTests("collectTests", files, invalidates),
|
|
3347
|
-
async close() {
|
|
3348
|
-
await Promise.all(Object.values(pools).map((p) => p?.close?.()));
|
|
3349
|
-
}
|
|
3350
|
-
};
|
|
3351
|
-
}
|
|
3352
|
-
|
|
3353
2357
|
class BaseSequencer {
|
|
3354
2358
|
ctx;
|
|
3355
2359
|
constructor(ctx) {
|
|
@@ -3359,7 +2363,7 @@ class BaseSequencer {
|
|
|
3359
2363
|
async shard(files) {
|
|
3360
2364
|
const { config } = this.ctx, { index, count } = config.shard, [shardStart, shardEnd] = this.calculateShardRange(files.length, index, count);
|
|
3361
2365
|
return [...files].map((spec) => {
|
|
3362
|
-
const
|
|
2366
|
+
const specPath = resolve(slash(config.root), slash(spec.moduleId))?.slice(config.root.length);
|
|
3363
2367
|
return {
|
|
3364
2368
|
spec,
|
|
3365
2369
|
hash: hash("sha1", specPath, "hex")
|
|
@@ -3400,7 +2404,7 @@ class RandomSequencer extends BaseSequencer {
|
|
|
3400
2404
|
}
|
|
3401
2405
|
|
|
3402
2406
|
function resolvePath(path, root) {
|
|
3403
|
-
return normalize(/* @__PURE__ */ resolveModule(path, { paths: [root] }) ?? resolve
|
|
2407
|
+
return normalize(/* @__PURE__ */ resolveModule(path, { paths: [root] }) ?? resolve(root, path));
|
|
3404
2408
|
}
|
|
3405
2409
|
function parseInspector(inspect) {
|
|
3406
2410
|
if (typeof inspect === "boolean" || inspect === void 0) return {};
|
|
@@ -3442,7 +2446,8 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3442
2446
|
root: viteConfig.root,
|
|
3443
2447
|
mode
|
|
3444
2448
|
};
|
|
3445
|
-
if (
|
|
2449
|
+
if (options.pool && typeof options.pool !== "string") resolved.pool = options.pool.name, resolved.poolRunner = options.pool;
|
|
2450
|
+
if (resolved.pool ??= "forks", resolved.project = toArray(resolved.project), resolved.provide ??= {}, resolved.name = typeof options.name === "string" ? options.name : options.name?.label || "", resolved.color = typeof options.name !== "string" ? options.name?.color : void 0, resolved.environment === "browser") throw new Error(`Looks like you set "test.environment" to "browser". To enabled Browser Mode, use "test.browser.enabled" instead.`);
|
|
3446
2451
|
const inspector = resolved.inspect || resolved.inspectBrk;
|
|
3447
2452
|
if (resolved.inspector = {
|
|
3448
2453
|
...resolved.inspector,
|
|
@@ -3463,14 +2468,14 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3463
2468
|
if (resolved.standalone && !resolved.watch) throw new Error(`Vitest standalone mode requires --watch`);
|
|
3464
2469
|
if (resolved.mergeReports && resolved.watch) throw new Error(`Cannot merge reports with --watch enabled`);
|
|
3465
2470
|
if (resolved.maxWorkers) resolved.maxWorkers = resolveInlineWorkerOption(resolved.maxWorkers);
|
|
3466
|
-
if (resolved.
|
|
3467
|
-
|
|
2471
|
+
if (resolved.fileParallelism ??= mode !== "benchmark", !resolved.fileParallelism)
|
|
2472
|
+
// ignore user config, parallelism cannot be implemented without limiting workers
|
|
2473
|
+
resolved.maxWorkers = 1;
|
|
3468
2474
|
if (resolved.maxConcurrency === 0) logger.console.warn(c.yellow(`The option "maxConcurrency" cannot be set to 0. Using default value ${configDefaults.maxConcurrency} instead.`)), resolved.maxConcurrency = configDefaults.maxConcurrency;
|
|
3469
2475
|
if (resolved.inspect || resolved.inspectBrk) {
|
|
3470
|
-
|
|
3471
|
-
if (resolved.fileParallelism && !isSingleThread && !isSingleFork) {
|
|
2476
|
+
if (resolved.fileParallelism) {
|
|
3472
2477
|
const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
|
|
3473
|
-
throw new Error(`You cannot use ${inspectOption} without "--no-file-parallelism"
|
|
2478
|
+
throw new Error(`You cannot use ${inspectOption} without "--no-file-parallelism"`);
|
|
3474
2479
|
}
|
|
3475
2480
|
}
|
|
3476
2481
|
// apply browser CLI options only if the config already has the browser config and not disabled manually
|
|
@@ -3478,35 +2483,59 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3478
2483
|
resolved.browser ??= {};
|
|
3479
2484
|
const browser = resolved.browser;
|
|
3480
2485
|
if (browser.enabled) {
|
|
3481
|
-
if (!browser.name && !browser.instances) throw new Error(`Vitest Browser Mode requires "browser.name" (deprecated) or "browser.instances" options, none were set.`);
|
|
3482
2486
|
const instances = browser.instances;
|
|
3483
|
-
if (browser.
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
2487
|
+
if (!browser.instances) browser.instances = [];
|
|
2488
|
+
// use `chromium` by default when the preview provider is specified
|
|
2489
|
+
// for a smoother experience. if chromium is not available, it will
|
|
2490
|
+
// open the default browser anyway
|
|
2491
|
+
if (!browser.instances.length && browser.provider?.name === "preview") browser.instances = [{ browser: "chromium" }];
|
|
2492
|
+
if (browser.name && instances?.length) {
|
|
2493
|
+
// if `instances` were defined, but now they are empty,
|
|
2494
|
+
// let's throw an error because the filter is invalid
|
|
2495
|
+
if (browser.instances = browser.instances.filter((instance) => instance.browser === browser.name), !browser.instances.length) throw new Error([`"browser.instances" was set in the config, but the array is empty. Define at least one browser config.`, ` The "browser.name" was set to "${browser.name}" which filtered all configs (${instances.map((c) => c.browser).join(", ")}). Did you mean to use another name?`].join(""));
|
|
2496
|
+
}
|
|
3487
2497
|
}
|
|
3488
|
-
const
|
|
3489
|
-
// Browser-mode "
|
|
3490
|
-
if (browser.enabled && !
|
|
3491
|
-
const browserConfig =
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
2498
|
+
const containsChromium = hasBrowserChromium(vitest, resolved), hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
|
|
2499
|
+
// Browser-mode "Chromium" only features:
|
|
2500
|
+
if (browser.enabled && (!containsChromium || !hasOnlyChromium)) {
|
|
2501
|
+
const browserConfig = `
|
|
2502
|
+
{
|
|
2503
|
+
browser: {
|
|
2504
|
+
provider: ${browser.provider?.name || "preview"}(),
|
|
2505
|
+
instances: [
|
|
2506
|
+
${(browser.instances || []).map((i) => `{ browser: '${i.browser}' }`).join(",\n ")}
|
|
2507
|
+
],
|
|
2508
|
+
},
|
|
2509
|
+
}
|
|
2510
|
+
`.trim(), preferredProvider = !browser.provider?.name || browser.provider.name === "preview" ? "playwright" : browser.provider.name, correctExample = `
|
|
2511
|
+
{
|
|
2512
|
+
browser: {
|
|
2513
|
+
provider: ${preferredProvider}(),
|
|
2514
|
+
instances: [
|
|
2515
|
+
{ browser: '${preferredProvider === "playwright" ? "chromium" : "chrome"}' }
|
|
2516
|
+
],
|
|
2517
|
+
},
|
|
2518
|
+
}
|
|
2519
|
+
`.trim();
|
|
2520
|
+
// requires all projects to be chromium
|
|
2521
|
+
if (!hasOnlyChromium && resolved.coverage.enabled && resolved.coverage.provider === "v8") {
|
|
2522
|
+
const coverageExample = `
|
|
2523
|
+
{
|
|
2524
|
+
coverage: {
|
|
2525
|
+
provider: 'istanbul',
|
|
2526
|
+
},
|
|
2527
|
+
}
|
|
2528
|
+
`.trim();
|
|
2529
|
+
throw new Error(`@vitest/coverage-v8 does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or change your coverage provider to:\n${coverageExample}\n`);
|
|
2530
|
+
}
|
|
2531
|
+
// ignores non-chromium browsers when there is at least one chromium project
|
|
2532
|
+
if (!containsChromium && (resolved.inspect || resolved.inspectBrk)) {
|
|
3501
2533
|
const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
|
|
3502
|
-
throw new Error(`${inspectOption} does not work with\n${
|
|
3503
|
-
provider: "playwright",
|
|
3504
|
-
instances: [{ browser: "chromium" }]
|
|
3505
|
-
} }, null, 2)}\n\n...or disable ${inspectOption}\n`);
|
|
2534
|
+
throw new Error(`${inspectOption} does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or disable ${inspectOption}\n`);
|
|
3506
2535
|
}
|
|
3507
2536
|
}
|
|
3508
2537
|
if (resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter), resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
|
|
3509
|
-
const reportsDirectory = resolve
|
|
2538
|
+
const reportsDirectory = resolve(resolved.root, resolved.coverage.reportsDirectory);
|
|
3510
2539
|
if (reportsDirectory === resolved.root || reportsDirectory === process.cwd()) throw new Error(`You cannot set "coverage.reportsDirectory" as ${reportsDirectory}. Vitest needs to be able to remove this directory before test run`);
|
|
3511
2540
|
}
|
|
3512
2541
|
if (resolved.coverage.enabled && resolved.coverage.provider === "custom" && resolved.coverage.customProviderModule) resolved.coverage.customProviderModule = resolvePath(resolved.coverage.customProviderModule, resolved.root);
|
|
@@ -3514,7 +2543,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3514
2543
|
const envModuleDirectories = process.env.VITEST_MODULE_DIRECTORIES || process.env.npm_config_VITEST_MODULE_DIRECTORIES;
|
|
3515
2544
|
if (envModuleDirectories) resolved.deps.moduleDirectories.push(...envModuleDirectories.split(","));
|
|
3516
2545
|
if (resolved.deps.moduleDirectories = resolved.deps.moduleDirectories.map((dir) => {
|
|
3517
|
-
if (
|
|
2546
|
+
if (dir[0] !== "/") dir = `/${dir}`;
|
|
3518
2547
|
if (!dir.endsWith("/")) dir += "/";
|
|
3519
2548
|
return normalize(dir);
|
|
3520
2549
|
}), !resolved.deps.moduleDirectories.includes("/node_modules/")) resolved.deps.moduleDirectories.push("/node_modules/");
|
|
@@ -3527,9 +2556,9 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3527
2556
|
"**/virtual:*",
|
|
3528
2557
|
"**/__x00__*",
|
|
3529
2558
|
"**/node_modules/**"
|
|
3530
|
-
].filter((pattern) => pattern
|
|
2559
|
+
].filter((pattern) => typeof pattern === "string"), resolved.forceRerunTriggers = [...resolved.forceRerunTriggers, ...resolved.setupFiles], resolved.cliExclude) resolved.exclude.push(...resolved.cliExclude);
|
|
3531
2560
|
if (resolved.runner) resolved.runner = resolvePath(resolved.runner, resolved.root);
|
|
3532
|
-
if (resolved.attachmentsDir = resolve
|
|
2561
|
+
if (resolved.attachmentsDir = resolve(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments"), resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
|
|
3533
2562
|
if (resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0, resolved.snapshotFormat && "plugins" in resolved.snapshotFormat) {
|
|
3534
2563
|
// TODO: support it via separate config (like DiffOptions) or via `Function.toString()`
|
|
3535
2564
|
if (resolved.snapshotFormat.plugins = [], typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
|
|
@@ -3542,65 +2571,8 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3542
2571
|
resolveSnapshotPath: options.resolveSnapshotPath,
|
|
3543
2572
|
snapshotEnvironment: null
|
|
3544
2573
|
}, resolved.snapshotSerializers ??= [], resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root)), resolved.forceRerunTriggers.push(...resolved.snapshotSerializers), options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
|
|
3545
|
-
if (resolved.pool ??= "threads",
|
|
3546
|
-
|
|
3547
|
-
threads: {
|
|
3548
|
-
...resolved.poolOptions?.threads,
|
|
3549
|
-
maxThreads: Number.parseInt(process.env.VITEST_MAX_THREADS)
|
|
3550
|
-
},
|
|
3551
|
-
vmThreads: {
|
|
3552
|
-
...resolved.poolOptions?.vmThreads,
|
|
3553
|
-
maxThreads: Number.parseInt(process.env.VITEST_MAX_THREADS)
|
|
3554
|
-
}
|
|
3555
|
-
};
|
|
3556
|
-
if (process.env.VITEST_MIN_THREADS) resolved.poolOptions = {
|
|
3557
|
-
...resolved.poolOptions,
|
|
3558
|
-
threads: {
|
|
3559
|
-
...resolved.poolOptions?.threads,
|
|
3560
|
-
minThreads: Number.parseInt(process.env.VITEST_MIN_THREADS)
|
|
3561
|
-
},
|
|
3562
|
-
vmThreads: {
|
|
3563
|
-
...resolved.poolOptions?.vmThreads,
|
|
3564
|
-
minThreads: Number.parseInt(process.env.VITEST_MIN_THREADS)
|
|
3565
|
-
}
|
|
3566
|
-
};
|
|
3567
|
-
if (process.env.VITEST_MAX_FORKS) resolved.poolOptions = {
|
|
3568
|
-
...resolved.poolOptions,
|
|
3569
|
-
forks: {
|
|
3570
|
-
...resolved.poolOptions?.forks,
|
|
3571
|
-
maxForks: Number.parseInt(process.env.VITEST_MAX_FORKS)
|
|
3572
|
-
},
|
|
3573
|
-
vmForks: {
|
|
3574
|
-
...resolved.poolOptions?.vmForks,
|
|
3575
|
-
maxForks: Number.parseInt(process.env.VITEST_MAX_FORKS)
|
|
3576
|
-
}
|
|
3577
|
-
};
|
|
3578
|
-
if (process.env.VITEST_MIN_FORKS) resolved.poolOptions = {
|
|
3579
|
-
...resolved.poolOptions,
|
|
3580
|
-
forks: {
|
|
3581
|
-
...resolved.poolOptions?.forks,
|
|
3582
|
-
minForks: Number.parseInt(process.env.VITEST_MIN_FORKS)
|
|
3583
|
-
},
|
|
3584
|
-
vmForks: {
|
|
3585
|
-
...resolved.poolOptions?.vmForks,
|
|
3586
|
-
minForks: Number.parseInt(process.env.VITEST_MIN_FORKS)
|
|
3587
|
-
}
|
|
3588
|
-
};
|
|
3589
|
-
const poolThreadsOptions = [
|
|
3590
|
-
["threads", "minThreads"],
|
|
3591
|
-
["threads", "maxThreads"],
|
|
3592
|
-
["vmThreads", "minThreads"],
|
|
3593
|
-
["vmThreads", "maxThreads"]
|
|
3594
|
-
];
|
|
3595
|
-
for (const [poolOptionKey, workerOptionKey] of poolThreadsOptions) if (resolved.poolOptions?.[poolOptionKey]?.[workerOptionKey]) resolved.poolOptions[poolOptionKey][workerOptionKey] = resolveInlineWorkerOption(resolved.poolOptions[poolOptionKey][workerOptionKey]);
|
|
3596
|
-
const poolForksOptions = [
|
|
3597
|
-
["forks", "minForks"],
|
|
3598
|
-
["forks", "maxForks"],
|
|
3599
|
-
["vmForks", "minForks"],
|
|
3600
|
-
["vmForks", "maxForks"]
|
|
3601
|
-
];
|
|
3602
|
-
for (const [poolOptionKey, workerOptionKey] of poolForksOptions) if (resolved.poolOptions?.[poolOptionKey]?.[workerOptionKey]) resolved.poolOptions[poolOptionKey][workerOptionKey] = resolveInlineWorkerOption(resolved.poolOptions[poolOptionKey][workerOptionKey]);
|
|
3603
|
-
if (!builtinPools.includes(resolved.pool)) resolved.pool = resolvePath(resolved.pool, resolved.root);
|
|
2574
|
+
if (resolved.execArgv ??= [], resolved.pool ??= "threads", resolved.pool === "vmForks" || resolved.pool === "vmThreads" || resolved.pool === "typescript") resolved.isolate = false;
|
|
2575
|
+
if (process.env.VITEST_MAX_WORKERS) resolved.maxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS);
|
|
3604
2576
|
if (mode === "benchmark") {
|
|
3605
2577
|
resolved.benchmark = {
|
|
3606
2578
|
...benchmarkConfigDefaults,
|
|
@@ -3615,12 +2587,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3615
2587
|
if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
|
|
3616
2588
|
}
|
|
3617
2589
|
if (typeof resolved.diff === "string") resolved.diff = resolvePath(resolved.diff, resolved.root), resolved.forceRerunTriggers.push(resolved.diff);
|
|
3618
|
-
// the server has been created, we don't need to override vite.server options
|
|
3619
|
-
const api = resolveApiServerConfig(options, defaultPort);
|
|
3620
2590
|
if (resolved.api = {
|
|
3621
|
-
...
|
|
2591
|
+
...resolveApiServerConfig(options, defaultPort),
|
|
3622
2592
|
token: crypto.randomUUID()
|
|
3623
|
-
}, options.related) resolved.related = toArray(options.related).map((file) => resolve
|
|
2593
|
+
}, options.related) resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
|
|
3624
2594
|
/*
|
|
3625
2595
|
* Reporters can be defined in many different ways:
|
|
3626
2596
|
* { reporter: 'json' }
|
|
@@ -3650,7 +2620,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3650
2620
|
// @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
|
|
3651
2621
|
// it is passed down as "vitest --reporter ../reporter.js"
|
|
3652
2622
|
const reportersFromCLI = resolved.reporter, cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
|
|
3653
|
-
return /^\.\.?\//.test(reporter) ? resolve
|
|
2623
|
+
return /^\.\.?\//.test(reporter) ? resolve(process.cwd(), reporter) : reporter;
|
|
3654
2624
|
});
|
|
3655
2625
|
if (cliReporters.length) resolved.reporters = Array.from(new Set(toArray(cliReporters))).filter(Boolean).map((reporter) => [reporter, {}]);
|
|
3656
2626
|
}
|
|
@@ -3676,24 +2646,35 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
3676
2646
|
...configDefaults.typecheck,
|
|
3677
2647
|
...resolved.typecheck
|
|
3678
2648
|
}, resolved.typecheck ??= {}, resolved.typecheck.enabled ??= false, resolved.typecheck.enabled) 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."));
|
|
3679
|
-
if (resolved.browser.enabled ??= false, resolved.browser.headless ??= isCI, resolved.browser.isolate ??= true, resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark", resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI, resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve
|
|
3680
|
-
|
|
2649
|
+
if (resolved.browser.enabled ??= false, resolved.browser.headless ??= isCI, resolved.browser.isolate ??= true, resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark", resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI, resolved.browser.commands ??= {}, resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve(resolved.root, resolved.browser.screenshotDirectory);
|
|
2650
|
+
if (resolved.inspector.enabled) resolved.browser.trackUnhandledErrors ??= false;
|
|
2651
|
+
if (resolved.browser.viewport ??= {}, resolved.browser.viewport.width ??= 414, resolved.browser.viewport.height ??= 896, resolved.browser.locators ??= {}, resolved.browser.locators.testIdAttribute ??= "data-testid", typeof resolved.browser.provider === "string") {
|
|
2652
|
+
const source = `@vitest/browser-${resolved.browser.provider}`;
|
|
2653
|
+
throw new TypeError(`The \`browser.provider\` configuration was changed to accept a factory instead of a string. Add an import of "${resolved.browser.provider}" from "${source}" instead. See: https://vitest.dev/guide/browser/config#provider`);
|
|
2654
|
+
}
|
|
2655
|
+
const isPreview = resolved.browser.provider?.name === "preview";
|
|
2656
|
+
if (!isPreview && resolved.browser.enabled && provider === "stackblitz") throw new Error(`stackblitz environment does not support the ${resolved.browser.provider?.name} provider. Please, use "@vitest/browser-preview" instead.`);
|
|
3681
2657
|
if (isPreview && resolved.browser.screenshotFailures === true) console.warn(c.yellow([
|
|
3682
2658
|
`Browser provider "preview" doesn't support screenshots, `,
|
|
3683
2659
|
`so "browser.screenshotFailures" option is forcefully disabled. `,
|
|
3684
2660
|
`Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
|
|
3685
2661
|
].join(""))), resolved.browser.screenshotFailures = false;
|
|
3686
2662
|
else resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui;
|
|
3687
|
-
if (resolved.browser.
|
|
2663
|
+
if (resolved.browser.provider && resolved.browser.provider.options == null) resolved.browser.provider.options = {};
|
|
3688
2664
|
// enable includeTaskLocation by default in UI mode
|
|
3689
2665
|
if (resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort }, resolved.browser.enabled) {
|
|
3690
2666
|
if (resolved.browser.ui) resolved.includeTaskLocation ??= true;
|
|
3691
2667
|
} else if (resolved.ui) resolved.includeTaskLocation ??= true;
|
|
3692
|
-
|
|
2668
|
+
if (typeof resolved.browser.trace === "string" || !resolved.browser.trace) resolved.browser.trace = { mode: resolved.browser.trace || "off" };
|
|
2669
|
+
if (resolved.browser.trace.tracesDir != null) resolved.browser.trace.tracesDir = resolvePath(resolved.browser.trace.tracesDir, resolved.root);
|
|
2670
|
+
if (toArray(resolved.reporters).some((reporter) => {
|
|
3693
2671
|
return Array.isArray(reporter) ? reporter[0] === "html" : false;
|
|
3694
|
-
});
|
|
3695
|
-
if (
|
|
3696
|
-
|
|
2672
|
+
})) resolved.includeTaskLocation ??= true;
|
|
2673
|
+
if (resolved.server ??= {}, resolved.server.deps ??= {}, resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP) {
|
|
2674
|
+
const userFolder = resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP;
|
|
2675
|
+
resolved.dumpDir = resolve(resolved.root, typeof userFolder === "string" && userFolder !== "true" ? userFolder : ".vitest-dump", resolved.name || "root");
|
|
2676
|
+
}
|
|
2677
|
+
return resolved.testTimeout ??= resolved.browser.enabled ? 3e4 : 5e3, resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4, resolved;
|
|
3697
2678
|
}
|
|
3698
2679
|
function isBrowserEnabled(config) {
|
|
3699
2680
|
return Boolean(config.browser?.enabled);
|
|
@@ -3710,18 +2691,22 @@ function resolveCoverageReporters(configReporters) {
|
|
|
3710
2691
|
resolvedReporters.push([reporter, {}]);
|
|
3711
2692
|
return resolvedReporters;
|
|
3712
2693
|
}
|
|
3713
|
-
function
|
|
2694
|
+
function isChromiumName(provider, name) {
|
|
2695
|
+
return provider === "playwright" ? name === "chromium" : name === "chrome" || name === "edge";
|
|
2696
|
+
}
|
|
2697
|
+
function hasBrowserChromium(vitest, config) {
|
|
3714
2698
|
const browser = config.browser;
|
|
3715
|
-
|
|
3716
|
-
if (browser.name) return browser.name === "chromium";
|
|
3717
|
-
if (!browser.instances) return false;
|
|
3718
|
-
for (const instance of browser.instances) {
|
|
2699
|
+
return !browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled ? false : browser.name ? isChromiumName(browser.provider.name, browser.name) : browser.instances ? browser.instances.some((instance) => {
|
|
3719
2700
|
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
2701
|
+
return vitest.matchesProjectFilter(name) ? isChromiumName(browser.provider.name, instance.browser) : false;
|
|
2702
|
+
}) : false;
|
|
2703
|
+
}
|
|
2704
|
+
function hasOnlyBrowserChromium(vitest, config) {
|
|
2705
|
+
const browser = config.browser;
|
|
2706
|
+
return !browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled ? false : browser.name ? isChromiumName(browser.provider.name, browser.name) : browser.instances ? browser.instances.every((instance) => {
|
|
2707
|
+
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
2708
|
+
return vitest.matchesProjectFilter(name) ? isChromiumName(browser.provider.name, instance.browser) : true;
|
|
2709
|
+
}) : false;
|
|
3725
2710
|
}
|
|
3726
2711
|
|
|
3727
2712
|
const THRESHOLD_KEYS = [
|
|
@@ -3754,7 +2739,7 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3754
2739
|
...coverageConfigDefaults,
|
|
3755
2740
|
...config,
|
|
3756
2741
|
provider: this.name,
|
|
3757
|
-
reportsDirectory: resolve
|
|
2742
|
+
reportsDirectory: resolve(ctx.config.root, config.reportsDirectory || coverageConfigDefaults.reportsDirectory),
|
|
3758
2743
|
reporter: resolveCoverageReporters(config.reporter || coverageConfigDefaults.reporter),
|
|
3759
2744
|
thresholds: config.thresholds && {
|
|
3760
2745
|
...config.thresholds,
|
|
@@ -3766,7 +2751,7 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3766
2751
|
};
|
|
3767
2752
|
const shard = this.ctx.config.shard, tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
|
|
3768
2753
|
// If --project filter is set pick only roots of resolved projects
|
|
3769
|
-
this.coverageFilesDirectory = resolve
|
|
2754
|
+
this.coverageFilesDirectory = resolve(this.options.reportsDirectory, tempDirectory), this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
|
|
3770
2755
|
}
|
|
3771
2756
|
/**
|
|
3772
2757
|
* Check if file matches `coverage.include` but not `coverage.exclude`
|
|
@@ -3802,8 +2787,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3802
2787
|
}
|
|
3803
2788
|
async getUntestedFiles(testedFiles) {
|
|
3804
2789
|
if (this.options.include == null) return [];
|
|
3805
|
-
const rootMapper = this.getUntestedFilesByRoot.bind(this, testedFiles, this.options.include)
|
|
3806
|
-
return
|
|
2790
|
+
const rootMapper = this.getUntestedFilesByRoot.bind(this, testedFiles, this.options.include);
|
|
2791
|
+
return (await Promise.all(this.roots.map(rootMapper))).flatMap((files) => files);
|
|
3807
2792
|
}
|
|
3808
2793
|
createCoverageMap() {
|
|
3809
2794
|
throw new Error("BaseReporter's createCoverageMap was not overwritten");
|
|
@@ -3818,26 +2803,26 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3818
2803
|
return this.options;
|
|
3819
2804
|
}
|
|
3820
2805
|
async clean(clean = true) {
|
|
3821
|
-
if (clean && existsSync(this.options.reportsDirectory)) await promises
|
|
2806
|
+
if (clean && existsSync(this.options.reportsDirectory)) await promises.rm(this.options.reportsDirectory, {
|
|
3822
2807
|
recursive: true,
|
|
3823
2808
|
force: true,
|
|
3824
2809
|
maxRetries: 10
|
|
3825
2810
|
});
|
|
3826
|
-
if (existsSync(this.coverageFilesDirectory)) await promises
|
|
2811
|
+
if (existsSync(this.coverageFilesDirectory)) await promises.rm(this.coverageFilesDirectory, {
|
|
3827
2812
|
recursive: true,
|
|
3828
2813
|
force: true,
|
|
3829
2814
|
maxRetries: 10
|
|
3830
2815
|
});
|
|
3831
|
-
await promises
|
|
2816
|
+
await promises.mkdir(this.coverageFilesDirectory, { recursive: true }), this.coverageFiles = /* @__PURE__ */ new Map(), this.pendingPromises = [];
|
|
3832
2817
|
}
|
|
3833
2818
|
onAfterSuiteRun({ coverage, environment, projectName, testFiles }) {
|
|
3834
2819
|
if (!coverage) return;
|
|
3835
2820
|
let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
|
|
3836
2821
|
if (!entry) entry = {}, this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
|
|
3837
|
-
const testFilenames = testFiles.join(), filename = resolve
|
|
2822
|
+
const testFilenames = testFiles.join(), filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
|
|
3838
2823
|
// If there's a result from previous run, overwrite it
|
|
3839
2824
|
entry[environment] ??= {}, entry[environment][testFilenames] = filename;
|
|
3840
|
-
const promise = promises
|
|
2825
|
+
const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
|
|
3841
2826
|
this.pendingPromises.push(promise);
|
|
3842
2827
|
}
|
|
3843
2828
|
async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
|
|
@@ -3849,7 +2834,7 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3849
2834
|
for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
|
|
3850
2835
|
if (onDebug.enabled) index += chunk.length, onDebug(`Reading coverage results ${index}/${total}`);
|
|
3851
2836
|
await Promise.all(chunk.map(async (filename) => {
|
|
3852
|
-
const contents = await promises
|
|
2837
|
+
const contents = await promises.readFile(filename, "utf-8"), coverage = JSON.parse(contents);
|
|
3853
2838
|
onFileRead(coverage);
|
|
3854
2839
|
}));
|
|
3855
2840
|
}
|
|
@@ -3858,16 +2843,13 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3858
2843
|
}
|
|
3859
2844
|
async cleanAfterRun() {
|
|
3860
2845
|
// Remove empty reports directory, e.g. when only text-reporter is used
|
|
3861
|
-
if (this.coverageFiles = /* @__PURE__ */ new Map(), await promises
|
|
2846
|
+
if (this.coverageFiles = /* @__PURE__ */ new Map(), await promises.rm(this.coverageFilesDirectory, { recursive: true }), readdirSync(this.options.reportsDirectory).length === 0) await promises.rm(this.options.reportsDirectory, { recursive: true });
|
|
3862
2847
|
}
|
|
3863
2848
|
async onTestFailure() {
|
|
3864
2849
|
if (!this.options.reportOnFailure) await this.cleanAfterRun();
|
|
3865
2850
|
}
|
|
3866
2851
|
async reportCoverage(coverageMap, { allTestsRun }) {
|
|
3867
|
-
await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun);
|
|
3868
|
-
// In watch mode we need to preserve the previous results if cleanOnRerun is disabled
|
|
3869
|
-
const keepResults = !this.options.cleanOnRerun && this.ctx.config.watch;
|
|
3870
|
-
if (!keepResults) await this.cleanAfterRun();
|
|
2852
|
+
if (await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun), !(!this.options.cleanOnRerun && this.ctx.config.watch)) await this.cleanAfterRun();
|
|
3871
2853
|
}
|
|
3872
2854
|
async reportThresholds(coverageMap, allTestsRun) {
|
|
3873
2855
|
const resolvedThresholds = this.resolveThresholds(coverageMap);
|
|
@@ -3998,10 +2980,14 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3998
2980
|
}
|
|
3999
2981
|
if (thresholdsToUpdate.length === 0) continue;
|
|
4000
2982
|
updatedThresholds = true;
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
const
|
|
4004
|
-
|
|
2983
|
+
const thresholdFormatter = typeof this.options.thresholds?.autoUpdate === "function" ? this.options.thresholds?.autoUpdate : (value) => value;
|
|
2984
|
+
for (const [threshold, newValue] of thresholdsToUpdate) {
|
|
2985
|
+
const formattedValue = thresholdFormatter(newValue);
|
|
2986
|
+
if (name === GLOBAL_THRESHOLDS_KEY) config.test.coverage.thresholds[threshold] = formattedValue;
|
|
2987
|
+
else {
|
|
2988
|
+
const glob = config.test.coverage.thresholds[name];
|
|
2989
|
+
glob[threshold] = formattedValue;
|
|
2990
|
+
}
|
|
4005
2991
|
}
|
|
4006
2992
|
}
|
|
4007
2993
|
if (updatedThresholds) this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds."), onUpdate();
|
|
@@ -4118,4 +3104,4 @@ function resolveMergeConfig(mod) {
|
|
|
4118
3104
|
}
|
|
4119
3105
|
}
|
|
4120
3106
|
|
|
4121
|
-
export { BaseCoverageProvider as B, RandomSequencer as R, resolveApiServerConfig as a, BaseSequencer as b,
|
|
3107
|
+
export { BaseCoverageProvider as B, RandomSequencer as R, resolveApiServerConfig as a, BaseSequencer as b, isBrowserEnabled as c, getCoverageProvider as g, hash as h, isPackageExists as i, resolveConfig$1 as r };
|