vitest 4.0.7 → 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.D3GxgUMI.js → base.BgTO2qAg.js} +71 -36
- package/dist/chunks/{benchmark.DHKMYAts.js → benchmark.B3N2zMcH.js} +9 -4
- package/dist/chunks/{browser.d.-LKfRopd.d.ts → browser.d.DTTM2PTh.d.ts} +1 -1
- package/dist/chunks/{cac.G9DAn-c7.js → cac.CfkWq8Qy.js} +115 -42
- package/dist/chunks/{cli-api.Csks4as1.js → cli-api.BQ-bjcRi.js} +1857 -839
- package/dist/chunks/console.Cf-YriPC.js +146 -0
- package/dist/chunks/{coverage.C2LA1DSL.js → coverage.NVjCOln1.js} +273 -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.DxtanrNO.d.ts → global.d.DVdCfKp5.d.ts} +1 -1
- package/dist/chunks/{globals.BGT_RUsD.js → globals.DOh96BiR.js} +5 -5
- package/dist/chunks/{index.DEPqWSIZ.js → index.BY4-tcno.js} +33 -16
- 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.CWIFvlX5.js → index.DfKyPFVi.js} +159 -54
- package/dist/chunks/{index.CVpyv-Zg.js → index.kotH7DY7.js} +832 -373
- package/dist/chunks/{index.jMQYiEWE.js → index.op2Re5rn.js} +22 -12
- package/dist/chunks/{init-forks.IU-xQ2_X.js → init-forks.2hx7cf78.js} +14 -4
- package/dist/chunks/{init-threads.C_NWvZkU.js → init-threads.Cm4OCIWA.js} +1 -1
- package/dist/chunks/{init.fmH9J833.js → init.DMDG-idf.js} +53 -30
- package/dist/chunks/{inspector.DLZxSeU3.js → inspector.CvyFGlXm.js} +25 -10
- package/dist/chunks/{moduleRunner.d.DEkTotCv.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.Cpes8Bt6.d.ts → plugin.d.D4RrtywJ.d.ts} +1 -1
- package/dist/chunks/{reporters.d.CSNcMDxF.d.ts → reporters.d.Da1D1VbQ.d.ts} +6 -5
- package/dist/chunks/{rpc.D38ahn14.js → rpc.BUV7uWKJ.js} +20 -7
- package/dist/chunks/{setup-common.DR1sucx6.js → setup-common.LGjNSzXp.js} +20 -8
- package/dist/chunks/{startModuleRunner.Cn7hCL7D.js → startModuleRunner.BOmUtLIO.js} +206 -83
- package/dist/chunks/{test.B6aJd6T3.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.BL7_zzOr.js → vm.BIkCDs68.js} +177 -71
- package/dist/chunks/{worker.d.D25zYZ7N.d.ts → worker.d.DadbA89M.d.ts} +30 -2
- 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 +1 -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 +1 -1
- package/dist/worker.js +15 -15
- package/dist/workers/forks.js +16 -16
- package/dist/workers/runVmTests.js +41 -22
- package/dist/workers/threads.js +16 -16
- package/dist/workers/vmForks.js +11 -11
- package/dist/workers/vmThreads.js +11 -11
- package/package.json +20 -20
- package/dist/chunks/console.CTJL2nuH.js +0 -115
|
@@ -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 {
|
|
@@ -2382,25 +2385,35 @@ class BaseSequencer {
|
|
|
2382
2385
|
// Isolated run first
|
|
2383
2386
|
if (a.project.config.isolate && !b.project.config.isolate) return -1;
|
|
2384
2387
|
if (!a.project.config.isolate && b.project.config.isolate) return 1;
|
|
2385
|
-
const keyA = `${a.project.name}:${relative(this.ctx.config.root, a.moduleId)}
|
|
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);
|
|
2386
2392
|
if (!aState || !bState) {
|
|
2387
|
-
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;
|
|
2388
2397
|
// run larger files first
|
|
2389
|
-
return
|
|
2398
|
+
return statsB.size - statsA.size;
|
|
2390
2399
|
}
|
|
2400
|
+
// run failed first
|
|
2401
|
+
if (aState.failed && !bState.failed) return -1;
|
|
2402
|
+
if (!aState.failed && bState.failed) return 1;
|
|
2391
2403
|
// run longer first
|
|
2392
|
-
return
|
|
2404
|
+
return bState.duration - aState.duration;
|
|
2393
2405
|
});
|
|
2394
2406
|
}
|
|
2395
2407
|
// Calculate distributed shard range [start, end] distributed equally
|
|
2396
2408
|
calculateShardRange(filesCount, index, count) {
|
|
2397
|
-
const baseShardSize = Math.floor(filesCount / count)
|
|
2409
|
+
const baseShardSize = Math.floor(filesCount / count);
|
|
2410
|
+
const remainderTestFilesCount = filesCount % count;
|
|
2398
2411
|
if (remainderTestFilesCount >= index) {
|
|
2399
|
-
const shardSize = baseShardSize + 1
|
|
2400
|
-
return [
|
|
2412
|
+
const shardSize = baseShardSize + 1;
|
|
2413
|
+
return [shardSize * (index - 1), shardSize * index];
|
|
2401
2414
|
}
|
|
2402
|
-
const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize
|
|
2403
|
-
return [shardStart,
|
|
2415
|
+
const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize;
|
|
2416
|
+
return [shardStart, shardStart + baseShardSize];
|
|
2404
2417
|
}
|
|
2405
2418
|
}
|
|
2406
2419
|
|
|
@@ -2419,10 +2432,11 @@ function parseInspector(inspect) {
|
|
|
2419
2432
|
if (typeof inspect === "number") return { port: inspect };
|
|
2420
2433
|
if (inspect.match(/https?:\//)) throw new Error(`Inspector host cannot be a URL. Use "host:port" instead of "${inspect}"`);
|
|
2421
2434
|
const [host, port] = inspect.split(":");
|
|
2422
|
-
|
|
2435
|
+
if (!port) return { host };
|
|
2436
|
+
return {
|
|
2423
2437
|
host,
|
|
2424
2438
|
port: Number(port) || defaultInspectPort
|
|
2425
|
-
}
|
|
2439
|
+
};
|
|
2426
2440
|
}
|
|
2427
2441
|
function resolveApiServerConfig(options, defaultPort) {
|
|
2428
2442
|
let api;
|
|
@@ -2440,10 +2454,12 @@ function resolveApiServerConfig(options, defaultPort) {
|
|
|
2440
2454
|
return api;
|
|
2441
2455
|
}
|
|
2442
2456
|
function resolveInlineWorkerOption(value) {
|
|
2443
|
-
|
|
2457
|
+
if (typeof value === "string" && value.trim().endsWith("%")) return getWorkersCountByPercentage(value);
|
|
2458
|
+
else return Number(value);
|
|
2444
2459
|
}
|
|
2445
2460
|
function resolveConfig$1(vitest, options, viteConfig) {
|
|
2446
|
-
const mode = vitest.mode
|
|
2461
|
+
const mode = vitest.mode;
|
|
2462
|
+
const logger = vitest.logger;
|
|
2447
2463
|
if (options.dom) {
|
|
2448
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}"`));
|
|
2449
2465
|
options.environment = "happy-dom";
|
|
@@ -2454,18 +2470,30 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2454
2470
|
root: viteConfig.root,
|
|
2455
2471
|
mode
|
|
2456
2472
|
};
|
|
2457
|
-
if (options.pool && typeof options.pool !== "string")
|
|
2458
|
-
|
|
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.`);
|
|
2459
2483
|
const inspector = resolved.inspect || resolved.inspectBrk;
|
|
2460
|
-
|
|
2484
|
+
resolved.inspector = {
|
|
2461
2485
|
...resolved.inspector,
|
|
2462
2486
|
...parseInspector(inspector),
|
|
2463
2487
|
enabled: !!inspector,
|
|
2464
2488
|
waitForDebugger: options.inspector?.waitForDebugger ?? !!resolved.inspectBrk
|
|
2465
|
-
}
|
|
2466
|
-
if (
|
|
2489
|
+
};
|
|
2490
|
+
if (viteConfig.base !== "/") resolved.base = viteConfig.base;
|
|
2491
|
+
resolved.clearScreen = resolved.clearScreen ?? viteConfig.clearScreen ?? true;
|
|
2492
|
+
if (options.shard) {
|
|
2467
2493
|
if (resolved.watch) throw new Error("You cannot use --shard option with enabled watch");
|
|
2468
|
-
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));
|
|
2469
2497
|
if (Number.isNaN(count) || count <= 0) throw new Error("--shard <count> must be a positive number");
|
|
2470
2498
|
if (Number.isNaN(index) || index <= 0 || index > count) throw new Error("--shard <index> must be a positive number less then <count>");
|
|
2471
2499
|
resolved.shard = {
|
|
@@ -2479,7 +2507,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2479
2507
|
if (!(options.fileParallelism ?? mode !== "benchmark"))
|
|
2480
2508
|
// ignore user config, parallelism cannot be implemented without limiting workers
|
|
2481
2509
|
resolved.maxWorkers = 1;
|
|
2482
|
-
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
|
+
}
|
|
2483
2514
|
if (resolved.inspect || resolved.inspectBrk) {
|
|
2484
2515
|
if (resolved.maxWorkers !== 1) {
|
|
2485
2516
|
const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
|
|
@@ -2498,12 +2529,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2498
2529
|
// open the default browser anyway
|
|
2499
2530
|
if (!browser.instances.length && browser.provider?.name === "preview") browser.instances = [{ browser: "chromium" }];
|
|
2500
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);
|
|
2501
2534
|
// if `instances` were defined, but now they are empty,
|
|
2502
2535
|
// let's throw an error because the filter is invalid
|
|
2503
|
-
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(""));
|
|
2504
2537
|
}
|
|
2505
2538
|
}
|
|
2506
|
-
const containsChromium = hasBrowserChromium(vitest, resolved)
|
|
2539
|
+
const containsChromium = hasBrowserChromium(vitest, resolved);
|
|
2540
|
+
const hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
|
|
2507
2541
|
// Browser-mode "Chromium" only features:
|
|
2508
2542
|
if (browser.enabled && (!containsChromium || !hasOnlyChromium)) {
|
|
2509
2543
|
const browserConfig = `
|
|
@@ -2515,7 +2549,9 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2515
2549
|
],
|
|
2516
2550
|
},
|
|
2517
2551
|
}
|
|
2518
|
-
`.trim()
|
|
2552
|
+
`.trim();
|
|
2553
|
+
const preferredProvider = !browser.provider?.name || browser.provider.name === "preview" ? "playwright" : browser.provider.name;
|
|
2554
|
+
const correctExample = `
|
|
2519
2555
|
{
|
|
2520
2556
|
browser: {
|
|
2521
2557
|
provider: ${preferredProvider}(),
|
|
@@ -2542,12 +2578,29 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2542
2578
|
throw new Error(`${inspectOption} does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or disable ${inspectOption}\n`);
|
|
2543
2579
|
}
|
|
2544
2580
|
}
|
|
2545
|
-
|
|
2581
|
+
resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter);
|
|
2582
|
+
if (resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
|
|
2546
2583
|
const reportsDirectory = resolve(resolved.root, resolved.coverage.reportsDirectory);
|
|
2547
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`);
|
|
2548
2585
|
}
|
|
2549
2586
|
if (resolved.coverage.enabled && resolved.coverage.provider === "custom" && resolved.coverage.customProviderModule) resolved.coverage.customProviderModule = resolvePath(resolved.coverage.customProviderModule, resolved.root);
|
|
2550
|
-
|
|
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 = [
|
|
2551
2604
|
...resolved.coverage.exclude,
|
|
2552
2605
|
...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
|
|
2553
2606
|
...resolved.include,
|
|
@@ -2556,28 +2609,45 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2556
2609
|
"**/virtual:*",
|
|
2557
2610
|
"**/__x00__*",
|
|
2558
2611
|
"**/node_modules/**"
|
|
2559
|
-
].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);
|
|
2560
2615
|
if (resolved.runner) resolved.runner = resolvePath(resolved.runner, resolved.root);
|
|
2561
|
-
|
|
2562
|
-
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 = [];
|
|
2563
2621
|
// TODO: support it via separate config (like DiffOptions) or via `Function.toString()`
|
|
2564
|
-
if (
|
|
2622
|
+
if (typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
|
|
2565
2623
|
}
|
|
2566
2624
|
const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT;
|
|
2567
|
-
|
|
2625
|
+
resolved.snapshotOptions = {
|
|
2568
2626
|
expand: resolved.expandSnapshotDiff ?? false,
|
|
2569
2627
|
snapshotFormat: resolved.snapshotFormat || {},
|
|
2570
2628
|
updateSnapshot: isCI && !UPDATE_SNAPSHOT ? "none" : UPDATE_SNAPSHOT ? "all" : "new",
|
|
2571
2629
|
resolveSnapshotPath: options.resolveSnapshotPath,
|
|
2572
2630
|
snapshotEnvironment: null
|
|
2573
|
-
}
|
|
2574
|
-
|
|
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;
|
|
2575
2639
|
if (process.env.VITEST_MAX_WORKERS) resolved.maxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS);
|
|
2576
2640
|
if (mode === "benchmark") {
|
|
2577
2641
|
resolved.benchmark = {
|
|
2578
2642
|
...benchmarkConfigDefaults,
|
|
2579
2643
|
...resolved.benchmark
|
|
2580
|
-
}
|
|
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;
|
|
2581
2651
|
const reporters = Array.from(new Set([...toArray(resolved.benchmark.reporters), ...toArray(options.reporter)])).filter(Boolean);
|
|
2582
2652
|
if (reporters.length) resolved.benchmark.reporters = reporters;
|
|
2583
2653
|
else resolved.benchmark.reporters = ["default"];
|
|
@@ -2586,11 +2656,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2586
2656
|
if (options.compare) resolved.benchmark.compare = options.compare;
|
|
2587
2657
|
if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
|
|
2588
2658
|
}
|
|
2589
|
-
if (typeof resolved.diff === "string")
|
|
2590
|
-
|
|
2659
|
+
if (typeof resolved.diff === "string") {
|
|
2660
|
+
resolved.diff = resolvePath(resolved.diff, resolved.root);
|
|
2661
|
+
resolved.forceRerunTriggers.push(resolved.diff);
|
|
2662
|
+
}
|
|
2663
|
+
resolved.api = {
|
|
2591
2664
|
...resolveApiServerConfig(options, defaultPort),
|
|
2592
2665
|
token: crypto.randomUUID()
|
|
2593
|
-
}
|
|
2666
|
+
};
|
|
2667
|
+
if (options.related) resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
|
|
2594
2668
|
/*
|
|
2595
2669
|
* Reporters can be defined in many different ways:
|
|
2596
2670
|
* { reporter: 'json' }
|
|
@@ -2619,8 +2693,11 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2619
2693
|
if (mode !== "benchmark") {
|
|
2620
2694
|
// @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
|
|
2621
2695
|
// it is passed down as "vitest --reporter ../reporter.js"
|
|
2622
|
-
const reportersFromCLI = resolved.reporter
|
|
2623
|
-
|
|
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;
|
|
2624
2701
|
});
|
|
2625
2702
|
if (cliReporters.length) {
|
|
2626
2703
|
// When CLI reporters are specified, preserve options from config file
|
|
@@ -2634,56 +2711,88 @@ function resolveConfig$1(vitest, options, viteConfig) {
|
|
|
2634
2711
|
}
|
|
2635
2712
|
}
|
|
2636
2713
|
if (!resolved.reporters.length) {
|
|
2714
|
+
resolved.reporters.push(["default", {}]);
|
|
2637
2715
|
// also enable github-actions reporter as a default
|
|
2638
|
-
if (
|
|
2716
|
+
if (process.env.GITHUB_ACTIONS === "true") resolved.reporters.push(["github-actions", {}]);
|
|
2639
2717
|
}
|
|
2640
2718
|
if (resolved.changed) resolved.passWithNoTests ??= true;
|
|
2641
|
-
|
|
2719
|
+
resolved.css ??= {};
|
|
2720
|
+
if (typeof resolved.css === "object") {
|
|
2721
|
+
resolved.css.modules ??= {};
|
|
2722
|
+
resolved.css.modules.classNameStrategy ??= "stable";
|
|
2723
|
+
}
|
|
2642
2724
|
if (resolved.cache !== false) {
|
|
2643
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"`);
|
|
2644
2726
|
resolved.cache = { dir: viteConfig.cacheDir };
|
|
2645
2727
|
}
|
|
2646
|
-
|
|
2728
|
+
resolved.sequence ??= {};
|
|
2729
|
+
if (resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
|
|
2647
2730
|
const { files, tests } = resolved.sequence.shuffle;
|
|
2648
|
-
resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer
|
|
2731
|
+
resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer;
|
|
2732
|
+
resolved.sequence.shuffle = tests;
|
|
2649
2733
|
}
|
|
2650
2734
|
if (!resolved.sequence?.sequencer)
|
|
2651
2735
|
// CLI flag has higher priority
|
|
2652
2736
|
resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
|
|
2653
|
-
|
|
2654
|
-
|
|
2737
|
+
resolved.sequence.groupOrder ??= 0;
|
|
2738
|
+
resolved.sequence.hooks ??= "stack";
|
|
2739
|
+
if (resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
|
|
2740
|
+
resolved.typecheck = {
|
|
2655
2741
|
...configDefaults.typecheck,
|
|
2656
2742
|
...resolved.typecheck
|
|
2657
|
-
}
|
|
2658
|
-
|
|
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);
|
|
2659
2755
|
if (resolved.inspector.enabled) resolved.browser.trackUnhandledErrors ??= false;
|
|
2660
|
-
|
|
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") {
|
|
2661
2762
|
const source = `@vitest/browser-${resolved.browser.provider}`;
|
|
2662
|
-
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`);
|
|
2663
2764
|
}
|
|
2664
2765
|
const isPreview = resolved.browser.provider?.name === "preview";
|
|
2665
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.`);
|
|
2666
|
-
if (isPreview && resolved.browser.screenshotFailures === true)
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
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;
|
|
2672
2775
|
if (resolved.browser.provider && resolved.browser.provider.options == null) resolved.browser.provider.options = {};
|
|
2776
|
+
resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort };
|
|
2673
2777
|
// enable includeTaskLocation by default in UI mode
|
|
2674
|
-
if (resolved.browser.
|
|
2778
|
+
if (resolved.browser.enabled) {
|
|
2675
2779
|
if (resolved.browser.ui) resolved.includeTaskLocation ??= true;
|
|
2676
2780
|
} else if (resolved.ui) resolved.includeTaskLocation ??= true;
|
|
2677
2781
|
if (typeof resolved.browser.trace === "string" || !resolved.browser.trace) resolved.browser.trace = { mode: resolved.browser.trace || "off" };
|
|
2678
2782
|
if (resolved.browser.trace.tracesDir != null) resolved.browser.trace.tracesDir = resolvePath(resolved.browser.trace.tracesDir, resolved.root);
|
|
2679
2783
|
if (toArray(resolved.reporters).some((reporter) => {
|
|
2680
|
-
|
|
2784
|
+
if (Array.isArray(reporter)) return reporter[0] === "html";
|
|
2785
|
+
return false;
|
|
2681
2786
|
})) resolved.includeTaskLocation ??= true;
|
|
2682
|
-
|
|
2787
|
+
resolved.server ??= {};
|
|
2788
|
+
resolved.server.deps ??= {};
|
|
2789
|
+
if (resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP) {
|
|
2683
2790
|
const userFolder = resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP;
|
|
2684
2791
|
resolved.dumpDir = resolve(resolved.root, typeof userFolder === "string" && userFolder !== "true" ? userFolder : ".vitest-dump", resolved.name || "root");
|
|
2685
2792
|
}
|
|
2686
|
-
|
|
2793
|
+
resolved.testTimeout ??= resolved.browser.enabled ? 3e4 : 5e3;
|
|
2794
|
+
resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4;
|
|
2795
|
+
return resolved;
|
|
2687
2796
|
}
|
|
2688
2797
|
function isBrowserEnabled(config) {
|
|
2689
2798
|
return Boolean(config.browser?.enabled);
|
|
@@ -2701,21 +2810,32 @@ function resolveCoverageReporters(configReporters) {
|
|
|
2701
2810
|
return resolvedReporters;
|
|
2702
2811
|
}
|
|
2703
2812
|
function isChromiumName(provider, name) {
|
|
2704
|
-
|
|
2813
|
+
if (provider === "playwright") return name === "chromium";
|
|
2814
|
+
return name === "chrome" || name === "edge";
|
|
2705
2815
|
}
|
|
2706
2816
|
function hasBrowserChromium(vitest, config) {
|
|
2707
2817
|
const browser = config.browser;
|
|
2708
|
-
|
|
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) => {
|
|
2709
2822
|
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
2710
|
-
|
|
2711
|
-
|
|
2823
|
+
// browser config is filtered out
|
|
2824
|
+
if (!vitest.matchesProjectFilter(name)) return false;
|
|
2825
|
+
return isChromiumName(browser.provider.name, instance.browser);
|
|
2826
|
+
});
|
|
2712
2827
|
}
|
|
2713
2828
|
function hasOnlyBrowserChromium(vitest, config) {
|
|
2714
2829
|
const browser = config.browser;
|
|
2715
|
-
|
|
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) => {
|
|
2716
2834
|
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
2717
|
-
|
|
2718
|
-
|
|
2835
|
+
// browser config is filtered out
|
|
2836
|
+
if (!vitest.matchesProjectFilter(name)) return true;
|
|
2837
|
+
return isChromiumName(browser.provider.name, instance.browser);
|
|
2838
|
+
});
|
|
2719
2839
|
}
|
|
2720
2840
|
|
|
2721
2841
|
const THRESHOLD_KEYS = [
|
|
@@ -2723,11 +2843,14 @@ const THRESHOLD_KEYS = [
|
|
|
2723
2843
|
"functions",
|
|
2724
2844
|
"statements",
|
|
2725
2845
|
"branches"
|
|
2726
|
-
]
|
|
2846
|
+
];
|
|
2847
|
+
const GLOBAL_THRESHOLDS_KEY = "global";
|
|
2848
|
+
const DEFAULT_PROJECT = Symbol.for("default-project");
|
|
2727
2849
|
let uniqueId = 0;
|
|
2728
2850
|
async function getCoverageProvider(options, loader) {
|
|
2729
2851
|
const coverageModule = await resolveCoverageProviderModule(options, loader);
|
|
2730
|
-
|
|
2852
|
+
if (coverageModule) return coverageModule.getProvider();
|
|
2853
|
+
return null;
|
|
2731
2854
|
}
|
|
2732
2855
|
class BaseCoverageProvider {
|
|
2733
2856
|
ctx;
|
|
@@ -2740,7 +2863,8 @@ class BaseCoverageProvider {
|
|
|
2740
2863
|
coverageFilesDirectory;
|
|
2741
2864
|
roots = [];
|
|
2742
2865
|
_initialize(ctx) {
|
|
2743
|
-
|
|
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} `))}.
|
|
2744
2868
|
Running mixed versions is not supported and may lead into bugs
|
|
2745
2869
|
Update your dependencies and make sure the versions match.`));
|
|
2746
2870
|
const config = ctx._coverageOptions;
|
|
@@ -2758,25 +2882,34 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2758
2882
|
statements: config.thresholds["100"] ? 100 : config.thresholds.statements
|
|
2759
2883
|
}
|
|
2760
2884
|
};
|
|
2761
|
-
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);
|
|
2762
2888
|
// If --project filter is set pick only roots of resolved projects
|
|
2763
|
-
this.
|
|
2889
|
+
this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
|
|
2764
2890
|
}
|
|
2765
2891
|
/**
|
|
2766
2892
|
* Check if file matches `coverage.include` but not `coverage.exclude`
|
|
2767
2893
|
*/
|
|
2768
2894
|
isIncluded(_filename, root) {
|
|
2769
|
-
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);
|
|
2770
2898
|
if (cacheHit !== void 0) return cacheHit;
|
|
2771
2899
|
// File outside project root with default allowExternal
|
|
2772
|
-
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
|
+
}
|
|
2773
2904
|
// By default `coverage.include` matches all files, except "coverage.exclude"
|
|
2774
|
-
const glob = this.options.include || "**"
|
|
2905
|
+
const glob = this.options.include || "**";
|
|
2906
|
+
const included = pm.isMatch(filename, glob, {
|
|
2775
2907
|
contains: true,
|
|
2776
2908
|
dot: true,
|
|
2777
2909
|
ignore: this.options.exclude
|
|
2778
2910
|
});
|
|
2779
|
-
|
|
2911
|
+
this.globCache.set(filename, included);
|
|
2912
|
+
return included;
|
|
2780
2913
|
}
|
|
2781
2914
|
async getUntestedFilesByRoot(testedFiles, include, root) {
|
|
2782
2915
|
let includedFiles = await glob(include, {
|
|
@@ -2786,7 +2919,9 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2786
2919
|
dot: true,
|
|
2787
2920
|
onlyFiles: true
|
|
2788
2921
|
});
|
|
2789
|
-
|
|
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));
|
|
2790
2925
|
return includedFiles.map((file) => slash(path.resolve(root, file)));
|
|
2791
2926
|
}
|
|
2792
2927
|
async getUntestedFiles(testedFiles) {
|
|
@@ -2817,49 +2952,66 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2817
2952
|
force: true,
|
|
2818
2953
|
maxRetries: 10
|
|
2819
2954
|
});
|
|
2820
|
-
await promises.mkdir(this.coverageFilesDirectory, { recursive: true })
|
|
2955
|
+
await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
|
|
2956
|
+
this.coverageFiles = /* @__PURE__ */ new Map();
|
|
2957
|
+
this.pendingPromises = [];
|
|
2821
2958
|
}
|
|
2822
2959
|
onAfterSuiteRun({ coverage, environment, projectName, testFiles }) {
|
|
2823
2960
|
if (!coverage) return;
|
|
2824
2961
|
let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
|
|
2825
|
-
if (!entry)
|
|
2826
|
-
|
|
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] ??= {};
|
|
2827
2969
|
// If there's a result from previous run, overwrite it
|
|
2828
|
-
entry[environment]
|
|
2970
|
+
entry[environment][testFilenames] = filename;
|
|
2829
2971
|
const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
|
|
2830
2972
|
this.pendingPromises.push(promise);
|
|
2831
2973
|
}
|
|
2832
2974
|
async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
|
|
2833
2975
|
let index = 0;
|
|
2834
2976
|
const total = this.pendingPromises.length;
|
|
2835
|
-
await Promise.all(this.pendingPromises)
|
|
2977
|
+
await Promise.all(this.pendingPromises);
|
|
2978
|
+
this.pendingPromises = [];
|
|
2836
2979
|
for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) for (const [environment, coverageByTestfiles] of Object.entries(coveragePerProject)) {
|
|
2837
|
-
const filenames = Object.values(coverageByTestfiles)
|
|
2980
|
+
const filenames = Object.values(coverageByTestfiles);
|
|
2981
|
+
const project = this.ctx.getProjectByName(projectName);
|
|
2838
2982
|
for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
|
|
2839
|
-
if (onDebug.enabled)
|
|
2983
|
+
if (onDebug.enabled) {
|
|
2984
|
+
index += chunk.length;
|
|
2985
|
+
onDebug(`Reading coverage results ${index}/${total}`);
|
|
2986
|
+
}
|
|
2840
2987
|
await Promise.all(chunk.map(async (filename) => {
|
|
2841
|
-
const contents = await promises.readFile(filename, "utf-8")
|
|
2842
|
-
onFileRead(
|
|
2988
|
+
const contents = await promises.readFile(filename, "utf-8");
|
|
2989
|
+
onFileRead(JSON.parse(contents));
|
|
2843
2990
|
}));
|
|
2844
2991
|
}
|
|
2845
2992
|
await onFinished(project, environment);
|
|
2846
2993
|
}
|
|
2847
2994
|
}
|
|
2848
2995
|
async cleanAfterRun() {
|
|
2996
|
+
this.coverageFiles = /* @__PURE__ */ new Map();
|
|
2997
|
+
await promises.rm(this.coverageFilesDirectory, { recursive: true });
|
|
2849
2998
|
// Remove empty reports directory, e.g. when only text-reporter is used
|
|
2850
|
-
if (
|
|
2999
|
+
if (readdirSync(this.options.reportsDirectory).length === 0) await promises.rm(this.options.reportsDirectory, { recursive: true });
|
|
2851
3000
|
}
|
|
2852
3001
|
async onTestFailure() {
|
|
2853
3002
|
if (!this.options.reportOnFailure) await this.cleanAfterRun();
|
|
2854
3003
|
}
|
|
2855
3004
|
async reportCoverage(coverageMap, { allTestsRun }) {
|
|
2856
|
-
|
|
3005
|
+
await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun);
|
|
3006
|
+
if (!(!this.options.cleanOnRerun && this.ctx.config.watch)) await this.cleanAfterRun();
|
|
2857
3007
|
}
|
|
2858
3008
|
async reportThresholds(coverageMap, allTestsRun) {
|
|
2859
3009
|
const resolvedThresholds = this.resolveThresholds(coverageMap);
|
|
2860
|
-
|
|
3010
|
+
this.checkThresholds(resolvedThresholds);
|
|
3011
|
+
if (this.options.thresholds?.autoUpdate && allTestsRun) {
|
|
2861
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.");
|
|
2862
|
-
const configFilePath = this.ctx.vite.config.configFile
|
|
3013
|
+
const configFilePath = this.ctx.vite.config.configFile;
|
|
3014
|
+
const configModule = await this.parseConfigModule(configFilePath);
|
|
2863
3015
|
await this.updateThresholds({
|
|
2864
3016
|
thresholds: resolvedThresholds,
|
|
2865
3017
|
configurationFile: configModule,
|
|
@@ -2873,10 +3025,16 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2873
3025
|
* for specific files defined by glob pattern or global for all other files.
|
|
2874
3026
|
*/
|
|
2875
3027
|
resolveThresholds(coverageMap) {
|
|
2876
|
-
const resolvedThresholds = []
|
|
3028
|
+
const resolvedThresholds = [];
|
|
3029
|
+
const files = coverageMap.files();
|
|
3030
|
+
const globalCoverageMap = this.createCoverageMap();
|
|
2877
3031
|
for (const key of Object.keys(this.options.thresholds)) {
|
|
2878
3032
|
if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) continue;
|
|
2879
|
-
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)));
|
|
2880
3038
|
for (const file of matchingFiles) {
|
|
2881
3039
|
const fileCoverage = coverageMap.fileCoverageFor(file);
|
|
2882
3040
|
globCoverageMap.addFileCoverage(fileCoverage);
|
|
@@ -2892,7 +3050,7 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2892
3050
|
const fileCoverage = coverageMap.fileCoverageFor(file);
|
|
2893
3051
|
globalCoverageMap.addFileCoverage(fileCoverage);
|
|
2894
3052
|
}
|
|
2895
|
-
|
|
3053
|
+
resolvedThresholds.unshift({
|
|
2896
3054
|
name: GLOBAL_THRESHOLDS_KEY,
|
|
2897
3055
|
coverageMap: globalCoverageMap,
|
|
2898
3056
|
thresholds: {
|
|
@@ -2901,7 +3059,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2901
3059
|
lines: this.options.thresholds?.lines,
|
|
2902
3060
|
statements: this.options.thresholds?.statements
|
|
2903
3061
|
}
|
|
2904
|
-
})
|
|
3062
|
+
});
|
|
3063
|
+
return resolvedThresholds;
|
|
2905
3064
|
}
|
|
2906
3065
|
/**
|
|
2907
3066
|
* Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
|
|
@@ -2939,7 +3098,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2939
3098
|
this.ctx.logger.error(errorMessage);
|
|
2940
3099
|
}
|
|
2941
3100
|
} else {
|
|
2942
|
-
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;
|
|
2943
3103
|
if (uncovered > absoluteThreshold) {
|
|
2944
3104
|
process.exitCode = 1;
|
|
2945
3105
|
/**
|
|
@@ -2963,7 +3123,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2963
3123
|
const config = resolveConfig(configurationFile);
|
|
2964
3124
|
assertConfigurationModule(config);
|
|
2965
3125
|
for (const { coverageMap, thresholds, name } of allThresholds) {
|
|
2966
|
-
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 = [];
|
|
2967
3128
|
for (const key of THRESHOLD_KEYS) {
|
|
2968
3129
|
const threshold = thresholds[key] ?? 100;
|
|
2969
3130
|
/**
|
|
@@ -2974,7 +3135,8 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2974
3135
|
const actual = Math.min(...summaries.map((summary) => summary[key].pct));
|
|
2975
3136
|
if (actual > threshold) thresholdsToUpdate.push([key, actual]);
|
|
2976
3137
|
} else {
|
|
2977
|
-
const absoluteThreshold = threshold * -1
|
|
3138
|
+
const absoluteThreshold = threshold * -1;
|
|
3139
|
+
const actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
|
|
2978
3140
|
if (actual < absoluteThreshold) {
|
|
2979
3141
|
// If everything was covered, set new threshold to 100% (since a threshold of 0 would be considered as 0%)
|
|
2980
3142
|
const updatedThreshold = actual === 0 ? 100 : actual * -1;
|
|
@@ -2994,7 +3156,10 @@ Update your dependencies and make sure the versions match.`));
|
|
|
2994
3156
|
}
|
|
2995
3157
|
}
|
|
2996
3158
|
}
|
|
2997
|
-
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
|
+
}
|
|
2998
3163
|
}
|
|
2999
3164
|
async mergeReports(coverageMaps) {
|
|
3000
3165
|
const coverageMap = this.createCoverageMap();
|
|
@@ -3006,8 +3171,10 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3006
3171
|
}
|
|
3007
3172
|
toSlices(array, size) {
|
|
3008
3173
|
return array.reduce((chunks, item) => {
|
|
3009
|
-
const index = Math.max(0, chunks.length - 1)
|
|
3010
|
-
|
|
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]);
|
|
3011
3178
|
else lastChunk.push(item);
|
|
3012
3179
|
return chunks;
|
|
3013
3180
|
}, []);
|
|
@@ -3048,12 +3215,14 @@ Update your dependencies and make sure the versions match.`));
|
|
|
3048
3215
|
* Narrow down `unknown` glob thresholds to resolved ones
|
|
3049
3216
|
*/
|
|
3050
3217
|
function resolveGlobThresholds(thresholds) {
|
|
3051
|
-
|
|
3218
|
+
if (!thresholds || typeof thresholds !== "object") return {};
|
|
3219
|
+
if (100 in thresholds && thresholds[100] === true) return {
|
|
3052
3220
|
lines: 100,
|
|
3053
3221
|
branches: 100,
|
|
3054
3222
|
functions: 100,
|
|
3055
3223
|
statements: 100
|
|
3056
|
-
}
|
|
3224
|
+
};
|
|
3225
|
+
return {
|
|
3057
3226
|
lines: "lines" in thresholds && typeof thresholds.lines === "number" ? thresholds.lines : void 0,
|
|
3058
3227
|
branches: "branches" in thresholds && typeof thresholds.branches === "number" ? thresholds.branches : void 0,
|
|
3059
3228
|
functions: "functions" in thresholds && typeof thresholds.functions === "number" ? thresholds.functions : void 0,
|
|
@@ -3079,7 +3248,8 @@ function resolveConfig(configModule) {
|
|
|
3079
3248
|
if (config) return config;
|
|
3080
3249
|
// "export default mergeConfig(..., defineConfig(...))"
|
|
3081
3250
|
if (mod.$type === "function-call" && mod.$callee === "mergeConfig") {
|
|
3082
|
-
|
|
3251
|
+
config = resolveMergeConfig(mod);
|
|
3252
|
+
if (config) return config;
|
|
3083
3253
|
}
|
|
3084
3254
|
} catch (error) {
|
|
3085
3255
|
// Reduce magicast's verbose errors to readable ones
|