vitest 4.0.6 → 4.0.8
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 +1 -1
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -2
- package/dist/chunks/base.BgTO2qAg.js +156 -0
- package/dist/chunks/{benchmark.DHKMYAts.js → benchmark.B3N2zMcH.js} +9 -4
- package/dist/chunks/{browser.d.ScGeWTou.d.ts → browser.d.DTTM2PTh.d.ts} +1 -1
- package/dist/chunks/{cac.BBqWH4nd.js → cac.CfkWq8Qy.js} +117 -43
- package/dist/chunks/{cli-api.UL3SwFUb.js → cli-api.BQ-bjcRi.js} +1870 -847
- package/dist/chunks/console.Cf-YriPC.js +146 -0
- package/dist/chunks/{coverage.DuCn_Tmx.js → coverage.NVjCOln1.js} +281 -103
- package/dist/chunks/{creator.cqqifzG7.js → creator.fzVyoMf3.js} +74 -30
- package/dist/chunks/{date.-jtEtIeV.js → date.Bq6ZW5rf.js} +17 -6
- package/dist/chunks/{git.BFNcloKD.js → git.Bm2pzPAa.js} +3 -3
- package/dist/chunks/{global.d.DdOkMiVb.d.ts → global.d.DVdCfKp5.d.ts} +1 -1
- package/dist/chunks/{globals.BGT_RUsD.js → globals.DOh96BiR.js} +5 -5
- package/dist/chunks/{resolveSnapshotEnvironment.BZzLjzkh.js → index.BY4-tcno.js} +42 -25
- package/dist/chunks/{index.Bgo3tNWt.js → index.DAL392Ss.js} +40 -15
- package/dist/chunks/{index.RwjEGCQ0.js → index.DIFZf73e.js} +2 -2
- package/dist/chunks/{index.DV0mQLEO.js → index.DfKyPFVi.js} +195 -64
- package/dist/chunks/{index.BL8Hg4Uk.js → index.kotH7DY7.js} +837 -380
- package/dist/chunks/{index.CpdwpN7L.js → index.op2Re5rn.js} +22 -12
- package/dist/chunks/{init-forks.CSGFj9zN.js → init-forks.2hx7cf78.js} +16 -5
- package/dist/chunks/{init-threads.CIJLeFO8.js → init-threads.Cm4OCIWA.js} +3 -2
- package/dist/chunks/{init.DUeOfNO9.js → init.DMDG-idf.js} +124 -54
- package/dist/chunks/{inspector.DLZxSeU3.js → inspector.CvyFGlXm.js} +25 -10
- package/dist/chunks/{moduleRunner.d.TP-w6tIQ.d.ts → moduleRunner.d.CzOZ_4wC.d.ts} +1 -1
- package/dist/chunks/{node.BwAWWjHZ.js → node.Ce0vMQM7.js} +1 -1
- package/dist/chunks/{plugin.d.lctzD3Wk.d.ts → plugin.d.D4RrtywJ.d.ts} +1 -1
- package/dist/chunks/{reporters.d.PEs0tXod.d.ts → reporters.d.Da1D1VbQ.d.ts} +19 -9
- package/dist/chunks/rpc.BUV7uWKJ.js +76 -0
- package/dist/chunks/{setup-common.DR1sucx6.js → setup-common.LGjNSzXp.js} +20 -8
- package/dist/chunks/{startModuleRunner.Di-EZqh0.js → startModuleRunner.BOmUtLIO.js} +228 -105
- package/dist/chunks/{test.CnspO-X4.js → test.ClrAtjMv.js} +48 -22
- package/dist/chunks/{utils.CG9h5ccR.js → utils.DvEY5TfP.js} +14 -5
- package/dist/chunks/{vi.BZvkKVkM.js → vi.Bgcdy3bQ.js} +261 -111
- package/dist/chunks/{vm.Co_lR2NL.js → vm.BIkCDs68.js} +177 -70
- package/dist/chunks/{worker.d.B4Hthdvt.d.ts → worker.d.DadbA89M.d.ts} +52 -6
- package/dist/cli.js +2 -2
- package/dist/config.d.ts +5 -5
- package/dist/coverage.d.ts +3 -3
- package/dist/coverage.js +1 -1
- package/dist/environments.js +2 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/module-evaluator.d.ts +2 -2
- package/dist/module-evaluator.js +85 -35
- package/dist/module-runner.js +2 -2
- package/dist/node.d.ts +7 -7
- package/dist/node.js +16 -12
- package/dist/reporters.d.ts +3 -3
- package/dist/reporters.js +2 -2
- package/dist/runners.js +7 -7
- package/dist/snapshot.js +2 -2
- package/dist/suite.js +2 -2
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +27 -27
- package/dist/workers/forks.js +34 -31
- package/dist/workers/runVmTests.js +41 -22
- package/dist/workers/threads.js +34 -31
- package/dist/workers/vmForks.js +14 -14
- package/dist/workers/vmThreads.js +14 -14
- package/package.json +20 -20
- package/dist/chunks/base.BAf_bYeI.js +0 -128
- package/dist/chunks/console.CTJL2nuH.js +0 -115
- package/dist/chunks/rpc.Dv1Jt3i2.js +0 -66
|
@@ -2350,7 +2350,8 @@ const isPackageListed = quansync(function* (name, cwd) {
|
|
|
2350
2350
|
isPackageListed.sync;
|
|
2351
2351
|
|
|
2352
2352
|
function getWorkersCountByPercentage(percent) {
|
|
2353
|
-
const maxWorkersCount = nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length
|
|
2353
|
+
const maxWorkersCount = nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length;
|
|
2354
|
+
const workersCountByPercentage = Math.round(Number.parseInt(percent) / 100 * maxWorkersCount);
|
|
2354
2355
|
return Math.max(1, Math.min(maxWorkersCount, workersCountByPercentage));
|
|
2355
2356
|
}
|
|
2356
2357
|
|
|
@@ -2361,7 +2362,9 @@ class BaseSequencer {
|
|
|
2361
2362
|
}
|
|
2362
2363
|
// async so it can be extended by other sequelizers
|
|
2363
2364
|
async shard(files) {
|
|
2364
|
-
const { config } = this.ctx
|
|
2365
|
+
const { config } = this.ctx;
|
|
2366
|
+
const { index, count } = config.shard;
|
|
2367
|
+
const [shardStart, shardEnd] = this.calculateShardRange(files.length, index, count);
|
|
2365
2368
|
return [...files].map((spec) => {
|
|
2366
2369
|
const specPath = resolve(slash(config.root), slash(spec.moduleId))?.slice(config.root.length);
|
|
2367
2370
|
return {
|
|
@@ -2374,25 +2377,43 @@ class BaseSequencer {
|
|
|
2374
2377
|
async sort(files) {
|
|
2375
2378
|
const cache = this.ctx.cache;
|
|
2376
2379
|
return [...files].sort((a, b) => {
|
|
2377
|
-
|
|
2380
|
+
// "sequence.groupOrder" is higher priority
|
|
2381
|
+
const groupOrderDiff = a.project.config.sequence.groupOrder - b.project.config.sequence.groupOrder;
|
|
2382
|
+
if (groupOrderDiff !== 0) return groupOrderDiff;
|
|
2383
|
+
// Projects run sequential
|
|
2384
|
+
if (a.project.name !== b.project.name) return a.project.name < b.project.name ? -1 : 1;
|
|
2385
|
+
// Isolated run first
|
|
2386
|
+
if (a.project.config.isolate && !b.project.config.isolate) return -1;
|
|
2387
|
+
if (!a.project.config.isolate && b.project.config.isolate) return 1;
|
|
2388
|
+
const keyA = `${a.project.name}:${relative(this.ctx.config.root, a.moduleId)}`;
|
|
2389
|
+
const keyB = `${b.project.name}:${relative(this.ctx.config.root, b.moduleId)}`;
|
|
2390
|
+
const aState = cache.getFileTestResults(keyA);
|
|
2391
|
+
const bState = cache.getFileTestResults(keyB);
|
|
2378
2392
|
if (!aState || !bState) {
|
|
2379
|
-
const statsA = cache.getFileStats(keyA)
|
|
2393
|
+
const statsA = cache.getFileStats(keyA);
|
|
2394
|
+
const statsB = cache.getFileStats(keyB);
|
|
2395
|
+
// run unknown first
|
|
2396
|
+
if (!statsA || !statsB) return !statsA && statsB ? -1 : !statsB && statsA ? 1 : 0;
|
|
2380
2397
|
// run larger files first
|
|
2381
|
-
return
|
|
2398
|
+
return statsB.size - statsA.size;
|
|
2382
2399
|
}
|
|
2400
|
+
// run failed first
|
|
2401
|
+
if (aState.failed && !bState.failed) return -1;
|
|
2402
|
+
if (!aState.failed && bState.failed) return 1;
|
|
2383
2403
|
// run longer first
|
|
2384
|
-
return
|
|
2404
|
+
return bState.duration - aState.duration;
|
|
2385
2405
|
});
|
|
2386
2406
|
}
|
|
2387
2407
|
// Calculate distributed shard range [start, end] distributed equally
|
|
2388
2408
|
calculateShardRange(filesCount, index, count) {
|
|
2389
|
-
const baseShardSize = Math.floor(filesCount / count)
|
|
2409
|
+
const baseShardSize = Math.floor(filesCount / count);
|
|
2410
|
+
const remainderTestFilesCount = filesCount % count;
|
|
2390
2411
|
if (remainderTestFilesCount >= index) {
|
|
2391
|
-
const shardSize = baseShardSize + 1
|
|
2392
|
-
return [
|
|
2412
|
+
const shardSize = baseShardSize + 1;
|
|
2413
|
+
return [shardSize * (index - 1), shardSize * index];
|
|
2393
2414
|
}
|
|
2394
|
-
const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize
|
|
2395
|
-
return [shardStart,
|
|
2415
|
+
const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize;
|
|
2416
|
+
return [shardStart, shardStart + baseShardSize];
|
|
2396
2417
|
}
|
|
2397
2418
|
}
|
|
2398
2419
|
|
|
@@ -2411,10 +2432,11 @@ function parseInspector(inspect) {
|
|
|
2411
2432
|
if (typeof inspect === "number") return { port: inspect };
|
|
2412
2433
|
if (inspect.match(/https?:\//)) throw new Error(`Inspector host cannot be a URL. Use "host:port" instead of "${inspect}"`);
|
|
2413
2434
|
const [host, port] = inspect.split(":");
|
|
2414
|
-
|
|
2435
|
+
if (!port) return { host };
|
|
2436
|
+
return {
|
|
2415
2437
|
host,
|
|
2416
2438
|
port: Number(port) || defaultInspectPort
|
|
2417
|
-
}
|
|
2439
|
+
};
|
|
2418
2440
|
}
|
|
2419
2441
|
function resolveApiServerConfig(options, defaultPort) {
|
|
2420
2442
|
let api;
|
|
@@ -2432,10 +2454,12 @@ function resolveApiServerConfig(options, defaultPort) {
|
|
|
2432
2454
|
return api;
|
|
2433
2455
|
}
|
|
2434
2456
|
function resolveInlineWorkerOption(value) {
|
|
2435
|
-
|
|
2457
|
+
if (typeof value === "string" && value.trim().endsWith("%")) return getWorkersCountByPercentage(value);
|
|
2458
|
+
else return Number(value);
|
|
2436
2459
|
}
|
|
2437
2460
|
function resolveConfig$1(vitest, options, viteConfig) {
|
|
2438
|
-
const mode = vitest.mode
|
|
2461
|
+
const mode = vitest.mode;
|
|
2462
|
+
const logger = vitest.logger;
|
|
2439
2463
|
if (options.dom) {
|
|
2440
2464
|
if (viteConfig.test?.environment != null && viteConfig.test.environment !== "happy-dom") logger.console.warn(c.yellow(`${c.inverse(c.yellow(" Vitest "))} Your config.test.environment ("${viteConfig.test.environment}") conflicts with --dom flag ("happy-dom"), ignoring "${viteConfig.test.environment}"`));
|
|
2441
2465
|
options.environment = "happy-dom";
|
|
@@ -2446,18 +2470,30 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2446
2470
|
root: viteConfig.root,
|
|
2447
2471
|
mode
|
|
2448
2472
|
};
|
|
2449
|
-
if (options.pool && typeof options.pool !== "string")
|
|
2450
|
-
|
|
2473
|
+
if (options.pool && typeof options.pool !== "string") {
|
|
2474
|
+
resolved.pool = options.pool.name;
|
|
2475
|
+
resolved.poolRunner = options.pool;
|
|
2476
|
+
}
|
|
2477
|
+
resolved.pool ??= "forks";
|
|
2478
|
+
resolved.project = toArray(resolved.project);
|
|
2479
|
+
resolved.provide ??= {};
|
|
2480
|
+
resolved.name = typeof options.name === "string" ? options.name : options.name?.label || "";
|
|
2481
|
+
resolved.color = typeof options.name !== "string" ? options.name?.color : void 0;
|
|
2482
|
+
if (resolved.environment === "browser") throw new Error(`Looks like you set "test.environment" to "browser". To enable Browser Mode, use "test.browser.enabled" instead.`);
|
|
2451
2483
|
const inspector = resolved.inspect || resolved.inspectBrk;
|
|
2452
|
-
|
|
2484
|
+
resolved.inspector = {
|
|
2453
2485
|
...resolved.inspector,
|
|
2454
2486
|
...parseInspector(inspector),
|
|
2455
2487
|
enabled: !!inspector,
|
|
2456
2488
|
waitForDebugger: options.inspector?.waitForDebugger ?? !!resolved.inspectBrk
|
|
2457
|
-
}
|
|
2458
|
-
if (
|
|
2489
|
+
};
|
|
2490
|
+
if (viteConfig.base !== "/") resolved.base = viteConfig.base;
|
|
2491
|
+
resolved.clearScreen = resolved.clearScreen ?? viteConfig.clearScreen ?? true;
|
|
2492
|
+
if (options.shard) {
|
|
2459
2493
|
if (resolved.watch) throw new Error("You cannot use --shard option with enabled watch");
|
|
2460
|
-
const [indexString, countString] = options.shard.split("/")
|
|
2494
|
+
const [indexString, countString] = options.shard.split("/");
|
|
2495
|
+
const index = Math.abs(Number.parseInt(indexString, 10));
|
|
2496
|
+
const count = Math.abs(Number.parseInt(countString, 10));
|
|
2461
2497
|
if (Number.isNaN(count) || count <= 0) throw new Error("--shard <count> must be a positive number");
|
|
2462
2498
|
if (Number.isNaN(index) || index <= 0 || index > count) throw new Error("--shard <index> must be a positive number less then <count>");
|
|
2463
2499
|
resolved.shard = {
|
|
@@ -2471,7 +2507,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2471
2507
|
if (!(options.fileParallelism ?? mode !== "benchmark"))
|
|
2472
2508
|
// ignore user config, parallelism cannot be implemented without limiting workers
|
|
2473
2509
|
resolved.maxWorkers = 1;
|
|
2474
|
-
if (resolved.maxConcurrency === 0)
|
|
2510
|
+
if (resolved.maxConcurrency === 0) {
|
|
2511
|
+
logger.console.warn(c.yellow(`The option "maxConcurrency" cannot be set to 0. Using default value ${configDefaults.maxConcurrency} instead.`));
|
|
2512
|
+
resolved.maxConcurrency = configDefaults.maxConcurrency;
|
|
2513
|
+
}
|
|
2475
2514
|
if (resolved.inspect || resolved.inspectBrk) {
|
|
2476
2515
|
if (resolved.maxWorkers !== 1) {
|
|
2477
2516
|
const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
|
|
@@ -2490,12 +2529,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2490
2529
|
// open the default browser anyway
|
|
2491
2530
|
if (!browser.instances.length && browser.provider?.name === "preview") browser.instances = [{ browser: "chromium" }];
|
|
2492
2531
|
if (browser.name && instances?.length) {
|
|
2532
|
+
// --browser=chromium filters configs to a single one
|
|
2533
|
+
browser.instances = browser.instances.filter((instance) => instance.browser === browser.name);
|
|
2493
2534
|
// if `instances` were defined, but now they are empty,
|
|
2494
2535
|
// let's throw an error because the filter is invalid
|
|
2495
|
-
if (
|
|
2536
|
+
if (!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
2537
|
}
|
|
2497
2538
|
}
|
|
2498
|
-
const containsChromium = hasBrowserChromium(vitest, resolved)
|
|
2539
|
+
const containsChromium = hasBrowserChromium(vitest, resolved);
|
|
2540
|
+
const hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
|
|
2499
2541
|
// Browser-mode "Chromium" only features:
|
|
2500
2542
|
if (browser.enabled && (!containsChromium || !hasOnlyChromium)) {
|
|
2501
2543
|
const browserConfig = `
|
|
@@ -2507,7 +2549,9 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2507
2549
|
],
|
|
2508
2550
|
},
|
|
2509
2551
|
}
|
|
2510
|
-
`.trim()
|
|
2552
|
+
`.trim();
|
|
2553
|
+
const preferredProvider = !browser.provider?.name || browser.provider.name === "preview" ? "playwright" : browser.provider.name;
|
|
2554
|
+
const correctExample = `
|
|
2511
2555
|
{
|
|
2512
2556
|
browser: {
|
|
2513
2557
|
provider: ${preferredProvider}(),
|
|
@@ -2534,12 +2578,29 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2534
2578
|
throw new Error(`${inspectOption} does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or disable ${inspectOption}\n`);
|
|
2535
2579
|
}
|
|
2536
2580
|
}
|
|
2537
|
-
|
|
2581
|
+
resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter);
|
|
2582
|
+
if (resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
|
|
2538
2583
|
const reportsDirectory = resolve(resolved.root, resolved.coverage.reportsDirectory);
|
|
2539
2584
|
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`);
|
|
2540
2585
|
}
|
|
2541
2586
|
if (resolved.coverage.enabled && resolved.coverage.provider === "custom" && resolved.coverage.customProviderModule) resolved.coverage.customProviderModule = resolvePath(resolved.coverage.customProviderModule, resolved.root);
|
|
2542
|
-
|
|
2587
|
+
resolved.expect ??= {};
|
|
2588
|
+
resolved.deps ??= {};
|
|
2589
|
+
resolved.deps.moduleDirectories ??= [];
|
|
2590
|
+
resolved.deps.optimizer ??= {};
|
|
2591
|
+
resolved.deps.optimizer.ssr ??= {};
|
|
2592
|
+
resolved.deps.optimizer.ssr.enabled ??= false;
|
|
2593
|
+
resolved.deps.optimizer.client ??= {};
|
|
2594
|
+
resolved.deps.optimizer.client.enabled ??= false;
|
|
2595
|
+
resolved.deps.web ??= {};
|
|
2596
|
+
resolved.deps.web.transformAssets ??= true;
|
|
2597
|
+
resolved.deps.web.transformCss ??= true;
|
|
2598
|
+
resolved.deps.web.transformGlobPattern ??= [];
|
|
2599
|
+
resolved.setupFiles = toArray(resolved.setupFiles || []).map((file) => resolvePath(file, resolved.root));
|
|
2600
|
+
resolved.globalSetup = toArray(resolved.globalSetup || []).map((file) => resolvePath(file, resolved.root));
|
|
2601
|
+
// Add hard-coded default coverage exclusions. These cannot be overidden by user config.
|
|
2602
|
+
// Override original exclude array for cases where user re-uses same object in test.exclude.
|
|
2603
|
+
resolved.coverage.exclude = [
|
|
2543
2604
|
...resolved.coverage.exclude,
|
|
2544
2605
|
...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
|
|
2545
2606
|
...resolved.include,
|
|
@@ -2548,28 +2609,45 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2548
2609
|
"**/virtual:*",
|
|
2549
2610
|
"**/__x00__*",
|
|
2550
2611
|
"**/node_modules/**"
|
|
2551
|
-
].filter((pattern) => typeof pattern === "string")
|
|
2612
|
+
].filter((pattern) => typeof pattern === "string");
|
|
2613
|
+
resolved.forceRerunTriggers = [...resolved.forceRerunTriggers, ...resolved.setupFiles];
|
|
2614
|
+
if (resolved.cliExclude) resolved.exclude.push(...resolved.cliExclude);
|
|
2552
2615
|
if (resolved.runner) resolved.runner = resolvePath(resolved.runner, resolved.root);
|
|
2553
|
-
|
|
2554
|
-
if (resolved.
|
|
2616
|
+
resolved.attachmentsDir = resolve(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments");
|
|
2617
|
+
if (resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
|
|
2618
|
+
resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0;
|
|
2619
|
+
if (resolved.snapshotFormat && "plugins" in resolved.snapshotFormat) {
|
|
2620
|
+
resolved.snapshotFormat.plugins = [];
|
|
2555
2621
|
// TODO: support it via separate config (like DiffOptions) or via `Function.toString()`
|
|
2556
|
-
if (
|
|
2622
|
+
if (typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
|
|
2557
2623
|
}
|
|
2558
2624
|
const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT;
|
|
2559
|
-
|
|
2625
|
+
resolved.snapshotOptions = {
|
|
2560
2626
|
expand: resolved.expandSnapshotDiff ?? false,
|
|
2561
2627
|
snapshotFormat: resolved.snapshotFormat || {},
|
|
2562
2628
|
updateSnapshot: isCI && !UPDATE_SNAPSHOT ? "none" : UPDATE_SNAPSHOT ? "all" : "new",
|
|
2563
2629
|
resolveSnapshotPath: options.resolveSnapshotPath,
|
|
2564
2630
|
snapshotEnvironment: null
|
|
2565
|
-
}
|
|
2566
|
-
|
|
2631
|
+
};
|
|
2632
|
+
resolved.snapshotSerializers ??= [];
|
|
2633
|
+
resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root));
|
|
2634
|
+
resolved.forceRerunTriggers.push(...resolved.snapshotSerializers);
|
|
2635
|
+
if (options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
|
|
2636
|
+
resolved.execArgv ??= [];
|
|
2637
|
+
resolved.pool ??= "threads";
|
|
2638
|
+
if (resolved.pool === "vmForks" || resolved.pool === "vmThreads" || resolved.pool === "typescript") resolved.isolate = false;
|
|
2567
2639
|
if (process.env.VITEST_MAX_WORKERS) resolved.maxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS);
|
|
2568
2640
|
if (mode === "benchmark") {
|
|
2569
2641
|
resolved.benchmark = {
|
|
2570
2642
|
...benchmarkConfigDefaults,
|
|
2571
2643
|
...resolved.benchmark
|
|
2572
|
-
}
|
|
2644
|
+
};
|
|
2645
|
+
// override test config
|
|
2646
|
+
resolved.coverage.enabled = false;
|
|
2647
|
+
resolved.typecheck.enabled = false;
|
|
2648
|
+
resolved.include = resolved.benchmark.include;
|
|
2649
|
+
resolved.exclude = resolved.benchmark.exclude;
|
|
2650
|
+
resolved.includeSource = resolved.benchmark.includeSource;
|
|
2573
2651
|
const reporters = Array.from(new Set([...toArray(resolved.benchmark.reporters), ...toArray(options.reporter)])).filter(Boolean);
|
|
2574
2652
|
if (reporters.length) resolved.benchmark.reporters = reporters;
|
|
2575
2653
|
else resolved.benchmark.reporters = ["default"];
|
|
@@ -2578,11 +2656,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2578
2656
|
if (options.compare) resolved.benchmark.compare = options.compare;
|
|
2579
2657
|
if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
|
|
2580
2658
|
}
|
|
2581
|
-
if (typeof resolved.diff === "string")
|
|
2582
|
-
|
|
2659
|
+
if (typeof resolved.diff === "string") {
|
|
2660
|
+
resolved.diff = resolvePath(resolved.diff, resolved.root);
|
|
2661
|
+
resolved.forceRerunTriggers.push(resolved.diff);
|
|
2662
|
+
}
|
|
2663
|
+
resolved.api = {
|
|
2583
2664
|
...resolveApiServerConfig(options, defaultPort),
|
|
2584
2665
|
token: crypto.randomUUID()
|
|
2585
|
-
}
|
|
2666
|
+
};
|
|
2667
|
+
if (options.related) resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
|
|
2586
2668
|
/*
|
|
2587
2669
|
* Reporters can be defined in many different ways:
|
|
2588
2670
|
* { reporter: 'json' }
|
|
@@ -2611,8 +2693,11 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2611
2693
|
if (mode !== "benchmark") {
|
|
2612
2694
|
// @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
|
|
2613
2695
|
// it is passed down as "vitest --reporter ../reporter.js"
|
|
2614
|
-
const reportersFromCLI = resolved.reporter
|
|
2615
|
-
|
|
2696
|
+
const reportersFromCLI = resolved.reporter;
|
|
2697
|
+
const cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
|
|
2698
|
+
// ./reporter.js || ../reporter.js, but not .reporters/reporter.js
|
|
2699
|
+
if (/^\.\.?\//.test(reporter)) return resolve(process.cwd(), reporter);
|
|
2700
|
+
return reporter;
|
|
2616
2701
|
});
|
|
2617
2702
|
if (cliReporters.length) {
|
|
2618
2703
|
// When CLI reporters are specified, preserve options from config file
|
|
@@ -2626,56 +2711,88 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2626
2711
|
}
|
|
2627
2712
|
}
|
|
2628
2713
|
if (!resolved.reporters.length) {
|
|
2714
|
+
resolved.reporters.push(["default", {}]);
|
|
2629
2715
|
// also enable github-actions reporter as a default
|
|
2630
|
-
if (
|
|
2716
|
+
if (process.env.GITHUB_ACTIONS === "true") resolved.reporters.push(["github-actions", {}]);
|
|
2631
2717
|
}
|
|
2632
2718
|
if (resolved.changed) resolved.passWithNoTests ??= true;
|
|
2633
|
-
|
|
2719
|
+
resolved.css ??= {};
|
|
2720
|
+
if (typeof resolved.css === "object") {
|
|
2721
|
+
resolved.css.modules ??= {};
|
|
2722
|
+
resolved.css.modules.classNameStrategy ??= "stable";
|
|
2723
|
+
}
|
|
2634
2724
|
if (resolved.cache !== false) {
|
|
2635
2725
|
if (resolved.cache && typeof resolved.cache.dir === "string") vitest.logger.deprecate(`"cache.dir" is deprecated, use Vite's "cacheDir" instead if you want to change the cache director. Note caches will be written to "cacheDir\/vitest"`);
|
|
2636
2726
|
resolved.cache = { dir: viteConfig.cacheDir };
|
|
2637
2727
|
}
|
|
2638
|
-
|
|
2728
|
+
resolved.sequence ??= {};
|
|
2729
|
+
if (resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
|
|
2639
2730
|
const { files, tests } = resolved.sequence.shuffle;
|
|
2640
|
-
resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer
|
|
2731
|
+
resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer;
|
|
2732
|
+
resolved.sequence.shuffle = tests;
|
|
2641
2733
|
}
|
|
2642
2734
|
if (!resolved.sequence?.sequencer)
|
|
2643
2735
|
// CLI flag has higher priority
|
|
2644
2736
|
resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
|
|
2645
|
-
|
|
2646
|
-
|
|
2737
|
+
resolved.sequence.groupOrder ??= 0;
|
|
2738
|
+
resolved.sequence.hooks ??= "stack";
|
|
2739
|
+
if (resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
|
|
2740
|
+
resolved.typecheck = {
|
|
2647
2741
|
...configDefaults.typecheck,
|
|
2648
2742
|
...resolved.typecheck
|
|
2649
|
-
}
|
|
2650
|
-
|
|
2743
|
+
};
|
|
2744
|
+
resolved.typecheck ??= {};
|
|
2745
|
+
resolved.typecheck.enabled ??= false;
|
|
2746
|
+
if (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."));
|
|
2747
|
+
resolved.browser.enabled ??= false;
|
|
2748
|
+
resolved.browser.headless ??= isCI;
|
|
2749
|
+
resolved.browser.isolate ??= resolved.isolate ?? true;
|
|
2750
|
+
resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark";
|
|
2751
|
+
// disable in headless mode by default, and if CI is detected
|
|
2752
|
+
resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI;
|
|
2753
|
+
resolved.browser.commands ??= {};
|
|
2754
|
+
if (resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve(resolved.root, resolved.browser.screenshotDirectory);
|
|
2651
2755
|
if (resolved.inspector.enabled) resolved.browser.trackUnhandledErrors ??= false;
|
|
2652
|
-
|
|
2756
|
+
resolved.browser.viewport ??= {};
|
|
2757
|
+
resolved.browser.viewport.width ??= 414;
|
|
2758
|
+
resolved.browser.viewport.height ??= 896;
|
|
2759
|
+
resolved.browser.locators ??= {};
|
|
2760
|
+
resolved.browser.locators.testIdAttribute ??= "data-testid";
|
|
2761
|
+
if (typeof resolved.browser.provider === "string") {
|
|
2653
2762
|
const source = `@vitest/browser-${resolved.browser.provider}`;
|
|
2654
|
-
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/
|
|
2763
|
+
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/config/browser/provider`);
|
|
2655
2764
|
}
|
|
2656
2765
|
const isPreview = resolved.browser.provider?.name === "preview";
|
|
2657
2766
|
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.`);
|
|
2658
|
-
if (isPreview && resolved.browser.screenshotFailures === true)
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2767
|
+
if (isPreview && resolved.browser.screenshotFailures === true) {
|
|
2768
|
+
console.warn(c.yellow([
|
|
2769
|
+
`Browser provider "preview" doesn't support screenshots, `,
|
|
2770
|
+
`so "browser.screenshotFailures" option is forcefully disabled. `,
|
|
2771
|
+
`Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
|
|
2772
|
+
].join("")));
|
|
2773
|
+
resolved.browser.screenshotFailures = false;
|
|
2774
|
+
} else resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui;
|
|
2664
2775
|
if (resolved.browser.provider && resolved.browser.provider.options == null) resolved.browser.provider.options = {};
|
|
2776
|
+
resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort };
|
|
2665
2777
|
// enable includeTaskLocation by default in UI mode
|
|
2666
|
-
if (resolved.browser.
|
|
2778
|
+
if (resolved.browser.enabled) {
|
|
2667
2779
|
if (resolved.browser.ui) resolved.includeTaskLocation ??= true;
|
|
2668
2780
|
} else if (resolved.ui) resolved.includeTaskLocation ??= true;
|
|
2669
2781
|
if (typeof resolved.browser.trace === "string" || !resolved.browser.trace) resolved.browser.trace = { mode: resolved.browser.trace || "off" };
|
|
2670
2782
|
if (resolved.browser.trace.tracesDir != null) resolved.browser.trace.tracesDir = resolvePath(resolved.browser.trace.tracesDir, resolved.root);
|
|
2671
2783
|
if (toArray(resolved.reporters).some((reporter) => {
|
|
2672
|
-
|
|
2784
|
+
if (Array.isArray(reporter)) return reporter[0] === "html";
|
|
2785
|
+
return false;
|
|
2673
2786
|
})) resolved.includeTaskLocation ??= true;
|
|
2674
|
-
|
|
2787
|
+
resolved.server ??= {};
|
|
2788
|
+
resolved.server.deps ??= {};
|
|
2789
|
+
if (resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP) {
|
|
2675
2790
|
const userFolder = resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP;
|
|
2676
2791
|
resolved.dumpDir = resolve(resolved.root, typeof userFolder === "string" && userFolder !== "true" ? userFolder : ".vitest-dump", resolved.name || "root");
|
|
2677
2792
|
}
|
|
2678
|
-
|
|
2793
|
+
resolved.testTimeout ??= resolved.browser.enabled ? 3e4 : 5e3;
|
|
2794
|
+
resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4;
|
|
2795
|
+
return resolved;
|
|
2679
2796
|
}
|
|
2680
2797
|
function isBrowserEnabled(config) {
|
|
2681
2798
|
return Boolean(config.browser?.enabled);
|
|
@@ -2693,21 +2810,32 @@ function resolveCoverageReporters(configReporters) {
|
|
|
2693
2810
|
return resolvedReporters;
|
|
2694
2811
|
}
|
|
2695
2812
|
function isChromiumName(provider, name) {
|
|
2696
|
-
|
|
2813
|
+
if (provider === "playwright") return name === "chromium";
|
|
2814
|
+
return name === "chrome" || name === "edge";
|
|
2697
2815
|
}
|
|
2698
2816
|
function hasBrowserChromium(vitest, config) {
|
|
2699
2817
|
const browser = config.browser;
|
|
2700
|
-
|
|
2818
|
+
if (!browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled) return false;
|
|
2819
|
+
if (browser.name) return isChromiumName(browser.provider.name, browser.name);
|
|
2820
|
+
if (!browser.instances) return false;
|
|
2821
|
+
return browser.instances.some((instance) => {
|
|
2701
2822
|
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
2702
|
-
|
|
2703
|
-
|
|
2823
|
+
// browser config is filtered out
|
|
2824
|
+
if (!vitest.matchesProjectFilter(name)) return false;
|
|
2825
|
+
return isChromiumName(browser.provider.name, instance.browser);
|
|
2826
|
+
});
|
|
2704
2827
|
}
|
|
2705
2828
|
function hasOnlyBrowserChromium(vitest, config) {
|
|
2706
2829
|
const browser = config.browser;
|
|
2707
|
-
|
|
2830
|
+
if (!browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled) return false;
|
|
2831
|
+
if (browser.name) return isChromiumName(browser.provider.name, browser.name);
|
|
2832
|
+
if (!browser.instances) return false;
|
|
2833
|
+
return browser.instances.every((instance) => {
|
|
2708
2834
|
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
2709
|
-
|
|
2710
|
-
|
|
2835
|
+
// browser config is filtered out
|
|
2836
|
+
if (!vitest.matchesProjectFilter(name)) return true;
|
|
2837
|
+
return isChromiumName(browser.provider.name, instance.browser);
|
|
2838
|
+
});
|
|
2711
2839
|
}
|
|
2712
2840
|
|
|
2713
2841
|
const THRESHOLD_KEYS = [
|
|
@@ -2715,11 +2843,14 @@ const THRESHOLD_KEYS = [
|
|
|
2715
2843
|
"functions",
|
|
2716
2844
|
"statements",
|
|
2717
2845
|
"branches"
|
|
2718
|
-
]
|
|
2846
|
+
];
|
|
2847
|
+
const GLOBAL_THRESHOLDS_KEY = "global";
|
|
2848
|
+
const DEFAULT_PROJECT = Symbol.for("default-project");
|
|
2719
2849
|
let uniqueId = 0;
|
|
2720
2850
|
async function getCoverageProvider(options, loader) {
|
|
2721
2851
|
const coverageModule = await resolveCoverageProviderModule(options, loader);
|
|
2722
|
-
|
|
2852
|
+
if (coverageModule) return coverageModule.getProvider();
|
|
2853
|
+
return null;
|
|
2723
2854
|
}
|
|
2724
2855
|
class BaseCoverageProvider {
|
|
2725
2856
|
ctx;
|
|
@@ -2732,7 +2863,8 @@ class BaseCoverageProvider {
|
|
|
2732
2863
|
coverageFilesDirectory;
|
|
2733
2864
|
roots = [];
|
|
2734
2865
|
_initialize(ctx) {
|
|
2735
|
-
|
|
2866
|
+
this.ctx = ctx;
|
|
2867
|
+
if (ctx.version !== this.version) ctx.logger.warn(c.yellow(`Loaded ${c.inverse(c.yellow(` vitest@${ctx.version} `))} and ${c.inverse(c.yellow(` @vitest/coverage-${this.name}@${this.version} `))}.
|
|
2736
2868
|
Running mixed versions is not supported and may lead into bugs
|
|
2737
2869
|
Update your dependencies and make sure the versions match.`));
|
|
2738
2870
|
const config = ctx._coverageOptions;
|
|
@@ -2750,25 +2882,34 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2750
2882
|
statements: config.thresholds["100"] ? 100 : config.thresholds.statements
|
|
2751
2883
|
}
|
|
2752
2884
|
};
|
|
2753
|
-
const shard = this.ctx.config.shard
|
|
2885
|
+
const shard = this.ctx.config.shard;
|
|
2886
|
+
const tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
|
|
2887
|
+
this.coverageFilesDirectory = resolve(this.options.reportsDirectory, tempDirectory);
|
|
2754
2888
|
// If --project filter is set pick only roots of resolved projects
|
|
2755
|
-
this.
|
|
2889
|
+
this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
|
|
2756
2890
|
}
|
|
2757
2891
|
/**
|
|
2758
2892
|
* Check if file matches `coverage.include` but not `coverage.exclude`
|
|
2759
2893
|
*/
|
|
2760
2894
|
isIncluded(_filename, root) {
|
|
2761
|
-
const roots = root ? [root] : this.roots
|
|
2895
|
+
const roots = root ? [root] : this.roots;
|
|
2896
|
+
const filename = slash(_filename);
|
|
2897
|
+
const cacheHit = this.globCache.get(filename);
|
|
2762
2898
|
if (cacheHit !== void 0) return cacheHit;
|
|
2763
2899
|
// File outside project root with default allowExternal
|
|
2764
|
-
if (this.options.allowExternal === false && roots.every((root) => !filename.startsWith(root)))
|
|
2900
|
+
if (this.options.allowExternal === false && roots.every((root) => !filename.startsWith(root))) {
|
|
2901
|
+
this.globCache.set(filename, false);
|
|
2902
|
+
return false;
|
|
2903
|
+
}
|
|
2765
2904
|
// By default `coverage.include` matches all files, except "coverage.exclude"
|
|
2766
|
-
const glob = this.options.include || "**"
|
|
2905
|
+
const glob = this.options.include || "**";
|
|
2906
|
+
const included = pm.isMatch(filename, glob, {
|
|
2767
2907
|
contains: true,
|
|
2768
2908
|
dot: true,
|
|
2769
2909
|
ignore: this.options.exclude
|
|
2770
2910
|
});
|
|
2771
|
-
|
|
2911
|
+
this.globCache.set(filename, included);
|
|
2912
|
+
return included;
|
|
2772
2913
|
}
|
|
2773
2914
|
async getUntestedFilesByRoot(testedFiles, include, root) {
|
|
2774
2915
|
let includedFiles = await glob(include, {
|
|
@@ -2778,7 +2919,9 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2778
2919
|
dot: true,
|
|
2779
2920
|
onlyFiles: true
|
|
2780
2921
|
});
|
|
2781
|
-
|
|
2922
|
+
// Run again through picomatch as tinyglobby's exclude pattern is different ({ "exclude": ["math"] } should ignore "src/math.ts")
|
|
2923
|
+
includedFiles = includedFiles.filter((file) => this.isIncluded(file, root));
|
|
2924
|
+
if (this.ctx.config.changed) includedFiles = (this.ctx.config.related || []).filter((file) => includedFiles.includes(file));
|
|
2782
2925
|
return includedFiles.map((file) => slash(path.resolve(root, file)));
|
|
2783
2926
|
}
|
|
2784
2927
|
async getUntestedFiles(testedFiles) {
|
|
@@ -2809,49 +2952,66 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2809
2952
|
force: true,
|
|
2810
2953
|
maxRetries: 10
|
|
2811
2954
|
});
|
|
2812
|
-
await promises.mkdir(this.coverageFilesDirectory, { recursive: true })
|
|
2955
|
+
await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
|
|
2956
|
+
this.coverageFiles = /* @__PURE__ */ new Map();
|
|
2957
|
+
this.pendingPromises = [];
|
|
2813
2958
|
}
|
|
2814
2959
|
onAfterSuiteRun({ coverage, environment, projectName, testFiles }) {
|
|
2815
2960
|
if (!coverage) return;
|
|
2816
2961
|
let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
|
|
2817
|
-
if (!entry)
|
|
2818
|
-
|
|
2962
|
+
if (!entry) {
|
|
2963
|
+
entry = {};
|
|
2964
|
+
this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
|
|
2965
|
+
}
|
|
2966
|
+
const testFilenames = testFiles.join();
|
|
2967
|
+
const filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
|
|
2968
|
+
entry[environment] ??= {};
|
|
2819
2969
|
// If there's a result from previous run, overwrite it
|
|
2820
|
-
entry[environment]
|
|
2970
|
+
entry[environment][testFilenames] = filename;
|
|
2821
2971
|
const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
|
|
2822
2972
|
this.pendingPromises.push(promise);
|
|
2823
2973
|
}
|
|
2824
2974
|
async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
|
|
2825
2975
|
let index = 0;
|
|
2826
2976
|
const total = this.pendingPromises.length;
|
|
2827
|
-
await Promise.all(this.pendingPromises)
|
|
2977
|
+
await Promise.all(this.pendingPromises);
|
|
2978
|
+
this.pendingPromises = [];
|
|
2828
2979
|
for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) for (const [environment, coverageByTestfiles] of Object.entries(coveragePerProject)) {
|
|
2829
|
-
const filenames = Object.values(coverageByTestfiles)
|
|
2980
|
+
const filenames = Object.values(coverageByTestfiles);
|
|
2981
|
+
const project = this.ctx.getProjectByName(projectName);
|
|
2830
2982
|
for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
|
|
2831
|
-
if (onDebug.enabled)
|
|
2983
|
+
if (onDebug.enabled) {
|
|
2984
|
+
index += chunk.length;
|
|
2985
|
+
onDebug(`Reading coverage results ${index}/${total}`);
|
|
2986
|
+
}
|
|
2832
2987
|
await Promise.all(chunk.map(async (filename) => {
|
|
2833
|
-
const contents = await promises.readFile(filename, "utf-8")
|
|
2834
|
-
onFileRead(
|
|
2988
|
+
const contents = await promises.readFile(filename, "utf-8");
|
|
2989
|
+
onFileRead(JSON.parse(contents));
|
|
2835
2990
|
}));
|
|
2836
2991
|
}
|
|
2837
2992
|
await onFinished(project, environment);
|
|
2838
2993
|
}
|
|
2839
2994
|
}
|
|
2840
2995
|
async cleanAfterRun() {
|
|
2996
|
+
this.coverageFiles = /* @__PURE__ */ new Map();
|
|
2997
|
+
await promises.rm(this.coverageFilesDirectory, { recursive: true });
|
|
2841
2998
|
// Remove empty reports directory, e.g. when only text-reporter is used
|
|
2842
|
-
if (
|
|
2999
|
+
if (readdirSync(this.options.reportsDirectory).length === 0) await promises.rm(this.options.reportsDirectory, { recursive: true });
|
|
2843
3000
|
}
|
|
2844
3001
|
async onTestFailure() {
|
|
2845
3002
|
if (!this.options.reportOnFailure) await this.cleanAfterRun();
|
|
2846
3003
|
}
|
|
2847
3004
|
async reportCoverage(coverageMap, { allTestsRun }) {
|
|
2848
|
-
|
|
3005
|
+
await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun);
|
|
3006
|
+
if (!(!this.options.cleanOnRerun && this.ctx.config.watch)) await this.cleanAfterRun();
|
|
2849
3007
|
}
|
|
2850
3008
|
async reportThresholds(coverageMap, allTestsRun) {
|
|
2851
3009
|
const resolvedThresholds = this.resolveThresholds(coverageMap);
|
|
2852
|
-
|
|
3010
|
+
this.checkThresholds(resolvedThresholds);
|
|
3011
|
+
if (this.options.thresholds?.autoUpdate && allTestsRun) {
|
|
2853
3012
|
if (!this.ctx.vite.config.configFile) throw new Error("Missing configurationFile. The \"coverage.thresholds.autoUpdate\" can only be enabled when configuration file is used.");
|
|
2854
|
-
const configFilePath = this.ctx.vite.config.configFile
|
|
3013
|
+
const configFilePath = this.ctx.vite.config.configFile;
|
|
3014
|
+
const configModule = await this.parseConfigModule(configFilePath);
|
|
2855
3015
|
await this.updateThresholds({
|
|
2856
3016
|
thresholds: resolvedThresholds,
|
|
2857
3017
|
configurationFile: configModule,
|
|
@@ -2865,10 +3025,16 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2865
3025
|
* for specific files defined by glob pattern or global for all other files.
|
|
2866
3026
|
*/
|
|
2867
3027
|
resolveThresholds(coverageMap) {
|
|
2868
|
-
const resolvedThresholds = []
|
|
3028
|
+
const resolvedThresholds = [];
|
|
3029
|
+
const files = coverageMap.files();
|
|
3030
|
+
const globalCoverageMap = this.createCoverageMap();
|
|
2869
3031
|
for (const key of Object.keys(this.options.thresholds)) {
|
|
2870
3032
|
if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) continue;
|
|
2871
|
-
const glob = key
|
|
3033
|
+
const glob = key;
|
|
3034
|
+
const globThresholds = resolveGlobThresholds(this.options.thresholds[glob]);
|
|
3035
|
+
const globCoverageMap = this.createCoverageMap();
|
|
3036
|
+
const matcher = pm(glob);
|
|
3037
|
+
const matchingFiles = files.filter((file) => matcher(relative(this.ctx.config.root, file)));
|
|
2872
3038
|
for (const file of matchingFiles) {
|
|
2873
3039
|
const fileCoverage = coverageMap.fileCoverageFor(file);
|
|
2874
3040
|
globCoverageMap.addFileCoverage(fileCoverage);
|
|
@@ -2884,7 +3050,7 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2884
3050
|
const fileCoverage = coverageMap.fileCoverageFor(file);
|
|
2885
3051
|
globalCoverageMap.addFileCoverage(fileCoverage);
|
|
2886
3052
|
}
|
|
2887
|
-
|
|
3053
|
+
resolvedThresholds.unshift({
|
|
2888
3054
|
name: GLOBAL_THRESHOLDS_KEY,
|
|
2889
3055
|
coverageMap: globalCoverageMap,
|
|
2890
3056
|
thresholds: {
|
|
@@ -2893,7 +3059,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2893
3059
|
lines: this.options.thresholds?.lines,
|
|
2894
3060
|
statements: this.options.thresholds?.statements
|
|
2895
3061
|
}
|
|
2896
|
-
})
|
|
3062
|
+
});
|
|
3063
|
+
return resolvedThresholds;
|
|
2897
3064
|
}
|
|
2898
3065
|
/**
|
|
2899
3066
|
* Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
|
|
@@ -2931,7 +3098,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2931
3098
|
this.ctx.logger.error(errorMessage);
|
|
2932
3099
|
}
|
|
2933
3100
|
} else {
|
|
2934
|
-
const uncovered = summary.data[thresholdKey].total - summary.data[thresholdKey].covered
|
|
3101
|
+
const uncovered = summary.data[thresholdKey].total - summary.data[thresholdKey].covered;
|
|
3102
|
+
const absoluteThreshold = threshold * -1;
|
|
2935
3103
|
if (uncovered > absoluteThreshold) {
|
|
2936
3104
|
process.exitCode = 1;
|
|
2937
3105
|
/**
|
|
@@ -2955,7 +3123,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2955
3123
|
const config = resolveConfig(configurationFile);
|
|
2956
3124
|
assertConfigurationModule(config);
|
|
2957
3125
|
for (const { coverageMap, thresholds, name } of allThresholds) {
|
|
2958
|
-
const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()]
|
|
3126
|
+
const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()];
|
|
3127
|
+
const thresholdsToUpdate = [];
|
|
2959
3128
|
for (const key of THRESHOLD_KEYS) {
|
|
2960
3129
|
const threshold = thresholds[key] ?? 100;
|
|
2961
3130
|
/**
|
|
@@ -2966,7 +3135,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2966
3135
|
const actual = Math.min(...summaries.map((summary) => summary[key].pct));
|
|
2967
3136
|
if (actual > threshold) thresholdsToUpdate.push([key, actual]);
|
|
2968
3137
|
} else {
|
|
2969
|
-
const absoluteThreshold = threshold * -1
|
|
3138
|
+
const absoluteThreshold = threshold * -1;
|
|
3139
|
+
const actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
|
|
2970
3140
|
if (actual < absoluteThreshold) {
|
|
2971
3141
|
// If everything was covered, set new threshold to 100% (since a threshold of 0 would be considered as 0%)
|
|
2972
3142
|
const updatedThreshold = actual === 0 ? 100 : actual * -1;
|
|
@@ -2986,7 +3156,10 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2986
3156
|
}
|
|
2987
3157
|
}
|
|
2988
3158
|
}
|
|
2989
|
-
if (updatedThresholds)
|
|
3159
|
+
if (updatedThresholds) {
|
|
3160
|
+
this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds.");
|
|
3161
|
+
onUpdate();
|
|
3162
|
+
}
|
|
2990
3163
|
}
|
|
2991
3164
|
async mergeReports(coverageMaps) {
|
|
2992
3165
|
const coverageMap = this.createCoverageMap();
|
|
@@ -2998,8 +3171,10 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2998
3171
|
}
|
|
2999
3172
|
toSlices(array, size) {
|
|
3000
3173
|
return array.reduce((chunks, item) => {
|
|
3001
|
-
const index = Math.max(0, chunks.length - 1)
|
|
3002
|
-
|
|
3174
|
+
const index = Math.max(0, chunks.length - 1);
|
|
3175
|
+
const lastChunk = chunks[index] || [];
|
|
3176
|
+
chunks[index] = lastChunk;
|
|
3177
|
+
if (lastChunk.length >= size) chunks.push([item]);
|
|
3003
3178
|
else lastChunk.push(item);
|
|
3004
3179
|
return chunks;
|
|
3005
3180
|
}, []);
|
|
@@ -3040,12 +3215,14 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3040
3215
|
* Narrow down `unknown` glob thresholds to resolved ones
|
|
3041
3216
|
*/
|
|
3042
3217
|
function resolveGlobThresholds(thresholds) {
|
|
3043
|
-
|
|
3218
|
+
if (!thresholds || typeof thresholds !== "object") return {};
|
|
3219
|
+
if (100 in thresholds && thresholds[100] === true) return {
|
|
3044
3220
|
lines: 100,
|
|
3045
3221
|
branches: 100,
|
|
3046
3222
|
functions: 100,
|
|
3047
3223
|
statements: 100
|
|
3048
|
-
}
|
|
3224
|
+
};
|
|
3225
|
+
return {
|
|
3049
3226
|
lines: "lines" in thresholds && typeof thresholds.lines === "number" ? thresholds.lines : void 0,
|
|
3050
3227
|
branches: "branches" in thresholds && typeof thresholds.branches === "number" ? thresholds.branches : void 0,
|
|
3051
3228
|
functions: "functions" in thresholds && typeof thresholds.functions === "number" ? thresholds.functions : void 0,
|
|
@@ -3071,7 +3248,8 @@ function resolveConfig(configModule) {
|
|
|
3071
3248
|
if (config) return config;
|
|
3072
3249
|
// "export default mergeConfig(..., defineConfig(...))"
|
|
3073
3250
|
if (mod.$type === "function-call" && mod.$callee === "mergeConfig") {
|
|
3074
|
-
|
|
3251
|
+
config = resolveMergeConfig(mod);
|
|
3252
|
+
if (config) return config;
|
|
3075
3253
|
}
|
|
3076
3254
|
} catch (error) {
|
|
3077
3255
|
// Reduce magicast's verbose errors to readable ones
|