vitest 4.1.0-beta.1 → 4.1.0-beta.2
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 +36 -0
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -2
- package/dist/chunks/acorn.B2iPLyUM.js +5958 -0
- package/dist/chunks/{base.CBRNZa3k.js → base.DiopZV8F.js} +48 -14
- package/dist/chunks/{benchmark.B3N2zMcH.js → benchmark.BoqSLF53.js} +1 -1
- package/dist/chunks/{browser.d.8hOapKZr.d.ts → browser.d.BE4kbYok.d.ts} +2 -1
- package/dist/chunks/{cac.B1v3xxoC.js → cac.C4jjt2RX.js} +797 -13
- package/dist/chunks/{cli-api.B4CqEpI6.js → cli-api.ChbI1JU9.js} +322 -124
- package/dist/chunks/{config.d.idH22YSr.d.ts → config.d.Cr1Ep39N.d.ts} +6 -1
- package/dist/chunks/{console.uGgdMhyZ.js → console.CNlG1KsP.js} +2 -2
- package/dist/chunks/{constants.D_Q9UYh-.js → constants.B63TT-Bl.js} +1 -1
- package/dist/chunks/coverage.tyqbzn4W.js +1001 -0
- package/dist/chunks/{creator.C7WwjkuR.js → creator.yyCHuw5R.js} +1 -1
- package/dist/chunks/{global.d.B15mdLcR.d.ts → global.d.JeWMqlOm.d.ts} +1 -1
- package/dist/chunks/{globals.DjuGMoMc.js → globals.C6Ecf1TO.js} +6 -6
- package/dist/chunks/{index.Dm4xqZ0s.js → index.B-iBE_Gx.js} +20 -4
- package/dist/chunks/{coverage.BMlOMIWl.js → index.BCY_7LL2.js} +5 -969
- package/dist/chunks/{index.BiOAd_ki.js → index.CAN630q3.js} +7 -7
- package/dist/chunks/{index.DyBZXrH3.js → index.CFulQRmC.js} +1 -1
- package/dist/chunks/{index.BEFi2-_3.js → index.CouFDptX.js} +2 -2
- package/dist/chunks/{init-forks.CHeQ9Moq.js → init-forks.BnCXPazU.js} +1 -1
- package/dist/chunks/{init-threads.uZiNAuPk.js → init-threads.Cyh2PqXi.js} +1 -1
- package/dist/chunks/{init.DVtKdFty.js → init.B95Mm0Iz.js} +47 -9
- package/dist/chunks/native.mV0-490A.js +148 -0
- package/dist/chunks/nativeModuleMocker.D_q5sFv6.js +206 -0
- package/dist/chunks/nativeModuleRunner.BIakptoF.js +36 -0
- package/dist/chunks/{node.Ce0vMQM7.js → node.CrSEwhm4.js} +1 -1
- package/dist/chunks/{plugin.d.D8KU2PY_.d.ts → plugin.d.C9o5bttz.d.ts} +1 -1
- package/dist/chunks/{reporters.d.Db3MiIWX.d.ts → reporters.d.7faYdkxy.d.ts} +120 -51
- package/dist/chunks/{rpc.HLmECnw_.js → rpc.DcRWTy5G.js} +1 -1
- package/dist/chunks/{rpc.d.RH3apGEf.d.ts → rpc.d.CM7x9-sm.d.ts} +1 -0
- package/dist/chunks/{setup-common.BcqLPsn5.js → setup-common.cvFp-ao9.js} +2 -2
- package/dist/chunks/{startModuleRunner.C5CcWyXW.js → startVitestModuleRunner.BK-u7y4N.js} +163 -372
- package/dist/chunks/{test.prxIahgM.js → test.G82XYNFk.js} +9 -4
- package/dist/chunks/{utils.DvEY5TfP.js → utils.DT4VyRyl.js} +5 -1
- package/dist/chunks/{vm.CrifS09m.js → vm.BdLtzhnj.js} +13 -6
- package/dist/chunks/{worker.d.Bji1eq5g.d.ts → worker.d.CPzI2ZzJ.d.ts} +2 -2
- package/dist/cli.js +4 -3
- package/dist/config.d.ts +8 -8
- package/dist/config.js +1 -1
- package/dist/coverage.d.ts +7 -5
- package/dist/coverage.js +5 -4
- package/dist/index.d.ts +18 -23
- package/dist/index.js +5 -5
- package/dist/module-evaluator.d.ts +10 -1
- package/dist/node.d.ts +9 -9
- package/dist/node.js +18 -16
- package/dist/nodejs-worker-loader.js +41 -0
- package/dist/reporters.d.ts +5 -5
- package/dist/reporters.js +2 -2
- package/dist/runners.d.ts +2 -1
- package/dist/runners.js +4 -4
- package/dist/runtime.js +4 -5
- package/dist/snapshot.js +2 -2
- package/dist/suite.js +2 -2
- package/dist/worker.d.ts +6 -6
- package/dist/worker.js +25 -18
- package/dist/workers/forks.js +21 -14
- package/dist/workers/runVmTests.js +7 -7
- package/dist/workers/threads.js +21 -14
- package/dist/workers/vmForks.js +14 -10
- package/dist/workers/vmThreads.js +14 -10
- package/package.json +17 -14
- package/suppress-warnings.cjs +1 -0
|
@@ -1,27 +1,13 @@
|
|
|
1
|
-
import fs, { statSync, realpathSync
|
|
2
|
-
import path, { win32, dirname, join } from 'node:path';
|
|
3
|
-
import { slash, shuffle, toArray } from '@vitest/utils/helpers';
|
|
4
|
-
import { isAbsolute, resolve, relative, normalize } from 'pathe';
|
|
5
|
-
import pm from 'picomatch';
|
|
6
|
-
import { glob } from 'tinyglobby';
|
|
7
|
-
import c from 'tinyrainbow';
|
|
8
|
-
import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.BOqNVLsY.js';
|
|
9
|
-
import crypto from 'node:crypto';
|
|
10
|
-
import { fileURLToPath as fileURLToPath$1, pathToFileURL as pathToFileURL$1, URL as URL$1 } from 'node:url';
|
|
1
|
+
import fs, { statSync, realpathSync } from 'node:fs';
|
|
11
2
|
import { builtinModules, createRequire } from 'node:module';
|
|
3
|
+
import path, { win32, dirname, join } from 'node:path';
|
|
12
4
|
import process$1 from 'node:process';
|
|
13
5
|
import fs$1 from 'node:fs/promises';
|
|
6
|
+
import { fileURLToPath as fileURLToPath$1, pathToFileURL as pathToFileURL$1, URL as URL$1 } from 'node:url';
|
|
7
|
+
import { isAbsolute } from 'pathe';
|
|
14
8
|
import assert from 'node:assert';
|
|
15
9
|
import v8 from 'node:v8';
|
|
16
10
|
import { format, inspect } from 'node:util';
|
|
17
|
-
import { mergeConfig } from 'vite';
|
|
18
|
-
import { c as configFiles, d as defaultBrowserPort, b as defaultInspectPort, a as defaultPort } from './constants.D_Q9UYh-.js';
|
|
19
|
-
import './env.D4Lgay0q.js';
|
|
20
|
-
import nodeos__default from 'node:os';
|
|
21
|
-
import { isCI, provider } from 'std-env';
|
|
22
|
-
import { r as resolveCoverageProviderModule } from './coverage.D_JHT54q.js';
|
|
23
|
-
|
|
24
|
-
const hash = crypto.hash ?? ((algorithm, data, outputEncoding) => crypto.createHash(algorithm).update(data).digest(outputEncoding));
|
|
25
11
|
|
|
26
12
|
const JOIN_LEADING_SLASH_RE = /^\.?\//;
|
|
27
13
|
function withTrailingSlash(input = "", respectQueryAndFragment) {
|
|
@@ -2349,954 +2335,4 @@ const isPackageListed = quansync(function* (name, cwd) {
|
|
|
2349
2335
|
});
|
|
2350
2336
|
isPackageListed.sync;
|
|
2351
2337
|
|
|
2352
|
-
|
|
2353
|
-
const maxWorkersCount = nodeos__default.availableParallelism?.() ?? nodeos__default.cpus().length;
|
|
2354
|
-
const workersCountByPercentage = Math.round(Number.parseInt(percent) / 100 * maxWorkersCount);
|
|
2355
|
-
return Math.max(1, Math.min(maxWorkersCount, workersCountByPercentage));
|
|
2356
|
-
}
|
|
2357
|
-
|
|
2358
|
-
class BaseSequencer {
|
|
2359
|
-
ctx;
|
|
2360
|
-
constructor(ctx) {
|
|
2361
|
-
this.ctx = ctx;
|
|
2362
|
-
}
|
|
2363
|
-
// async so it can be extended by other sequelizers
|
|
2364
|
-
async shard(files) {
|
|
2365
|
-
const { config } = this.ctx;
|
|
2366
|
-
const { index, count } = config.shard;
|
|
2367
|
-
const [shardStart, shardEnd] = this.calculateShardRange(files.length, index, count);
|
|
2368
|
-
return [...files].map((spec) => {
|
|
2369
|
-
const specPath = resolve(slash(config.root), slash(spec.moduleId))?.slice(config.root.length);
|
|
2370
|
-
return {
|
|
2371
|
-
spec,
|
|
2372
|
-
hash: hash("sha1", specPath, "hex")
|
|
2373
|
-
};
|
|
2374
|
-
}).sort((a, b) => a.hash < b.hash ? -1 : a.hash > b.hash ? 1 : 0).slice(shardStart, shardEnd).map(({ spec }) => spec);
|
|
2375
|
-
}
|
|
2376
|
-
// async so it can be extended by other sequelizers
|
|
2377
|
-
async sort(files) {
|
|
2378
|
-
const cache = this.ctx.cache;
|
|
2379
|
-
return [...files].sort((a, b) => {
|
|
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);
|
|
2392
|
-
if (!aState || !bState) {
|
|
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;
|
|
2397
|
-
// run larger files first
|
|
2398
|
-
return statsB.size - statsA.size;
|
|
2399
|
-
}
|
|
2400
|
-
// run failed first
|
|
2401
|
-
if (aState.failed && !bState.failed) return -1;
|
|
2402
|
-
if (!aState.failed && bState.failed) return 1;
|
|
2403
|
-
// run longer first
|
|
2404
|
-
return bState.duration - aState.duration;
|
|
2405
|
-
});
|
|
2406
|
-
}
|
|
2407
|
-
// Calculate distributed shard range [start, end] distributed equally
|
|
2408
|
-
calculateShardRange(filesCount, index, count) {
|
|
2409
|
-
const baseShardSize = Math.floor(filesCount / count);
|
|
2410
|
-
const remainderTestFilesCount = filesCount % count;
|
|
2411
|
-
if (remainderTestFilesCount >= index) {
|
|
2412
|
-
const shardSize = baseShardSize + 1;
|
|
2413
|
-
return [shardSize * (index - 1), shardSize * index];
|
|
2414
|
-
}
|
|
2415
|
-
const shardStart = remainderTestFilesCount * (baseShardSize + 1) + (index - remainderTestFilesCount - 1) * baseShardSize;
|
|
2416
|
-
return [shardStart, shardStart + baseShardSize];
|
|
2417
|
-
}
|
|
2418
|
-
}
|
|
2419
|
-
|
|
2420
|
-
class RandomSequencer extends BaseSequencer {
|
|
2421
|
-
async sort(files) {
|
|
2422
|
-
const { sequence } = this.ctx.config;
|
|
2423
|
-
return shuffle(files, sequence.seed);
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
function resolvePath(path, root) {
|
|
2428
|
-
return normalize(/* @__PURE__ */ resolveModule(path, { paths: [root] }) ?? resolve(root, path));
|
|
2429
|
-
}
|
|
2430
|
-
function parseInspector(inspect) {
|
|
2431
|
-
if (typeof inspect === "boolean" || inspect === void 0) return {};
|
|
2432
|
-
if (typeof inspect === "number") return { port: inspect };
|
|
2433
|
-
if (inspect.match(/https?:\//)) throw new Error(`Inspector host cannot be a URL. Use "host:port" instead of "${inspect}"`);
|
|
2434
|
-
const [host, port] = inspect.split(":");
|
|
2435
|
-
if (!port) return { host };
|
|
2436
|
-
return {
|
|
2437
|
-
host,
|
|
2438
|
-
port: Number(port) || defaultInspectPort
|
|
2439
|
-
};
|
|
2440
|
-
}
|
|
2441
|
-
function resolveApiServerConfig(options, defaultPort) {
|
|
2442
|
-
let api;
|
|
2443
|
-
if (options.ui && !options.api) api = { port: defaultPort };
|
|
2444
|
-
else if (options.api === true) api = { port: defaultPort };
|
|
2445
|
-
else if (typeof options.api === "number") api = { port: options.api };
|
|
2446
|
-
if (typeof options.api === "object") if (api) {
|
|
2447
|
-
if (options.api.port) api.port = options.api.port;
|
|
2448
|
-
if (options.api.strictPort) api.strictPort = options.api.strictPort;
|
|
2449
|
-
if (options.api.host) api.host = options.api.host;
|
|
2450
|
-
} else api = { ...options.api };
|
|
2451
|
-
if (api) {
|
|
2452
|
-
if (!api.port && !api.middlewareMode) api.port = defaultPort;
|
|
2453
|
-
} else api = { middlewareMode: true };
|
|
2454
|
-
return api;
|
|
2455
|
-
}
|
|
2456
|
-
function resolveInlineWorkerOption(value) {
|
|
2457
|
-
if (typeof value === "string" && value.trim().endsWith("%")) return getWorkersCountByPercentage(value);
|
|
2458
|
-
else return Number(value);
|
|
2459
|
-
}
|
|
2460
|
-
function resolveConfig$1(vitest, options, viteConfig) {
|
|
2461
|
-
const mode = vitest.mode;
|
|
2462
|
-
const logger = vitest.logger;
|
|
2463
|
-
if (options.dom) {
|
|
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}"`));
|
|
2465
|
-
options.environment = "happy-dom";
|
|
2466
|
-
}
|
|
2467
|
-
const resolved = {
|
|
2468
|
-
...configDefaults,
|
|
2469
|
-
...options,
|
|
2470
|
-
root: viteConfig.root,
|
|
2471
|
-
mode
|
|
2472
|
-
};
|
|
2473
|
-
if (resolved.retry && typeof resolved.retry === "object" && typeof resolved.retry.condition === "function") {
|
|
2474
|
-
logger.console.warn(c.yellow("Warning: retry.condition function cannot be used inside a config file. Use a RegExp pattern instead, or define the function in your test file."));
|
|
2475
|
-
resolved.retry = {
|
|
2476
|
-
...resolved.retry,
|
|
2477
|
-
condition: void 0
|
|
2478
|
-
};
|
|
2479
|
-
}
|
|
2480
|
-
if (options.pool && typeof options.pool !== "string") {
|
|
2481
|
-
resolved.pool = options.pool.name;
|
|
2482
|
-
resolved.poolRunner = options.pool;
|
|
2483
|
-
}
|
|
2484
|
-
if ("poolOptions" in resolved) logger.deprecate("`test.poolOptions` was removed in Vitest 4. All previous `poolOptions` are now top-level options. Please, refer to the migration guide: https://vitest.dev/guide/migration#pool-rework");
|
|
2485
|
-
resolved.pool ??= "forks";
|
|
2486
|
-
resolved.project = toArray(resolved.project);
|
|
2487
|
-
resolved.provide ??= {};
|
|
2488
|
-
resolved.name = typeof options.name === "string" ? options.name : options.name?.label || "";
|
|
2489
|
-
resolved.color = typeof options.name !== "string" ? options.name?.color : void 0;
|
|
2490
|
-
if (resolved.environment === "browser") throw new Error(`Looks like you set "test.environment" to "browser". To enable Browser Mode, use "test.browser.enabled" instead.`);
|
|
2491
|
-
const inspector = resolved.inspect || resolved.inspectBrk;
|
|
2492
|
-
resolved.inspector = {
|
|
2493
|
-
...resolved.inspector,
|
|
2494
|
-
...parseInspector(inspector),
|
|
2495
|
-
enabled: !!inspector,
|
|
2496
|
-
waitForDebugger: options.inspector?.waitForDebugger ?? !!resolved.inspectBrk
|
|
2497
|
-
};
|
|
2498
|
-
if (viteConfig.base !== "/") resolved.base = viteConfig.base;
|
|
2499
|
-
resolved.clearScreen = resolved.clearScreen ?? viteConfig.clearScreen ?? true;
|
|
2500
|
-
if (options.shard) {
|
|
2501
|
-
if (resolved.watch) throw new Error("You cannot use --shard option with enabled watch");
|
|
2502
|
-
const [indexString, countString] = options.shard.split("/");
|
|
2503
|
-
const index = Math.abs(Number.parseInt(indexString, 10));
|
|
2504
|
-
const count = Math.abs(Number.parseInt(countString, 10));
|
|
2505
|
-
if (Number.isNaN(count) || count <= 0) throw new Error("--shard <count> must be a positive number");
|
|
2506
|
-
if (Number.isNaN(index) || index <= 0 || index > count) throw new Error("--shard <index> must be a positive number less then <count>");
|
|
2507
|
-
resolved.shard = {
|
|
2508
|
-
index,
|
|
2509
|
-
count
|
|
2510
|
-
};
|
|
2511
|
-
}
|
|
2512
|
-
if (resolved.standalone && !resolved.watch) throw new Error(`Vitest standalone mode requires --watch`);
|
|
2513
|
-
if (resolved.mergeReports && resolved.watch) throw new Error(`Cannot merge reports with --watch enabled`);
|
|
2514
|
-
if (resolved.maxWorkers) resolved.maxWorkers = resolveInlineWorkerOption(resolved.maxWorkers);
|
|
2515
|
-
if (!(options.fileParallelism ?? mode !== "benchmark"))
|
|
2516
|
-
// ignore user config, parallelism cannot be implemented without limiting workers
|
|
2517
|
-
resolved.maxWorkers = 1;
|
|
2518
|
-
if (resolved.maxConcurrency === 0) {
|
|
2519
|
-
logger.console.warn(c.yellow(`The option "maxConcurrency" cannot be set to 0. Using default value ${configDefaults.maxConcurrency} instead.`));
|
|
2520
|
-
resolved.maxConcurrency = configDefaults.maxConcurrency;
|
|
2521
|
-
}
|
|
2522
|
-
if (resolved.inspect || resolved.inspectBrk) {
|
|
2523
|
-
if (resolved.maxWorkers !== 1) {
|
|
2524
|
-
const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
|
|
2525
|
-
throw new Error(`You cannot use ${inspectOption} without "--no-file-parallelism"`);
|
|
2526
|
-
}
|
|
2527
|
-
}
|
|
2528
|
-
// apply browser CLI options only if the config already has the browser config and not disabled manually
|
|
2529
|
-
if (vitest._cliOptions.browser && resolved.browser && (resolved.browser.enabled !== false || vitest._cliOptions.browser.enabled)) resolved.browser = mergeConfig(resolved.browser, vitest._cliOptions.browser);
|
|
2530
|
-
resolved.browser ??= {};
|
|
2531
|
-
const browser = resolved.browser;
|
|
2532
|
-
if (browser.enabled) {
|
|
2533
|
-
const instances = browser.instances;
|
|
2534
|
-
if (!browser.instances) browser.instances = [];
|
|
2535
|
-
// use `chromium` by default when the preview provider is specified
|
|
2536
|
-
// for a smoother experience. if chromium is not available, it will
|
|
2537
|
-
// open the default browser anyway
|
|
2538
|
-
if (!browser.instances.length && browser.provider?.name === "preview") browser.instances = [{ browser: "chromium" }];
|
|
2539
|
-
if (browser.name && instances?.length) {
|
|
2540
|
-
// --browser=chromium filters configs to a single one
|
|
2541
|
-
browser.instances = browser.instances.filter((instance) => instance.browser === browser.name);
|
|
2542
|
-
// if `instances` were defined, but now they are empty,
|
|
2543
|
-
// let's throw an error because the filter is invalid
|
|
2544
|
-
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(""));
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
const containsChromium = hasBrowserChromium(vitest, resolved);
|
|
2548
|
-
const hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
|
|
2549
|
-
// Browser-mode "Chromium" only features:
|
|
2550
|
-
if (browser.enabled && (!containsChromium || !hasOnlyChromium)) {
|
|
2551
|
-
const browserConfig = `
|
|
2552
|
-
{
|
|
2553
|
-
browser: {
|
|
2554
|
-
provider: ${browser.provider?.name || "preview"}(),
|
|
2555
|
-
instances: [
|
|
2556
|
-
${(browser.instances || []).map((i) => `{ browser: '${i.browser}' }`).join(",\n ")}
|
|
2557
|
-
],
|
|
2558
|
-
},
|
|
2559
|
-
}
|
|
2560
|
-
`.trim();
|
|
2561
|
-
const preferredProvider = !browser.provider?.name || browser.provider.name === "preview" ? "playwright" : browser.provider.name;
|
|
2562
|
-
const correctExample = `
|
|
2563
|
-
{
|
|
2564
|
-
browser: {
|
|
2565
|
-
provider: ${preferredProvider}(),
|
|
2566
|
-
instances: [
|
|
2567
|
-
{ browser: '${preferredProvider === "playwright" ? "chromium" : "chrome"}' }
|
|
2568
|
-
],
|
|
2569
|
-
},
|
|
2570
|
-
}
|
|
2571
|
-
`.trim();
|
|
2572
|
-
// requires all projects to be chromium
|
|
2573
|
-
if (!hasOnlyChromium && resolved.coverage.enabled && resolved.coverage.provider === "v8") {
|
|
2574
|
-
const coverageExample = `
|
|
2575
|
-
{
|
|
2576
|
-
coverage: {
|
|
2577
|
-
provider: 'istanbul',
|
|
2578
|
-
},
|
|
2579
|
-
}
|
|
2580
|
-
`.trim();
|
|
2581
|
-
throw new Error(`@vitest/coverage-v8 does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or change your coverage provider to:\n${coverageExample}\n`);
|
|
2582
|
-
}
|
|
2583
|
-
// ignores non-chromium browsers when there is at least one chromium project
|
|
2584
|
-
if (!containsChromium && (resolved.inspect || resolved.inspectBrk)) {
|
|
2585
|
-
const inspectOption = `--inspect${resolved.inspectBrk ? "-brk" : ""}`;
|
|
2586
|
-
throw new Error(`${inspectOption} does not work with\n${browserConfig}\n\nUse either:\n${correctExample}\n\n...or disable ${inspectOption}\n`);
|
|
2587
|
-
}
|
|
2588
|
-
}
|
|
2589
|
-
resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter);
|
|
2590
|
-
if (resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
|
|
2591
|
-
const reportsDirectory = resolve(resolved.root, resolved.coverage.reportsDirectory);
|
|
2592
|
-
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`);
|
|
2593
|
-
}
|
|
2594
|
-
if (resolved.coverage.enabled && resolved.coverage.provider === "custom" && resolved.coverage.customProviderModule) resolved.coverage.customProviderModule = resolvePath(resolved.coverage.customProviderModule, resolved.root);
|
|
2595
|
-
resolved.expect ??= {};
|
|
2596
|
-
resolved.deps ??= {};
|
|
2597
|
-
resolved.deps.moduleDirectories ??= [];
|
|
2598
|
-
resolved.deps.optimizer ??= {};
|
|
2599
|
-
resolved.deps.optimizer.ssr ??= {};
|
|
2600
|
-
resolved.deps.optimizer.ssr.enabled ??= false;
|
|
2601
|
-
resolved.deps.optimizer.client ??= {};
|
|
2602
|
-
resolved.deps.optimizer.client.enabled ??= false;
|
|
2603
|
-
resolved.deps.web ??= {};
|
|
2604
|
-
resolved.deps.web.transformAssets ??= true;
|
|
2605
|
-
resolved.deps.web.transformCss ??= true;
|
|
2606
|
-
resolved.deps.web.transformGlobPattern ??= [];
|
|
2607
|
-
resolved.setupFiles = toArray(resolved.setupFiles || []).map((file) => resolvePath(file, resolved.root));
|
|
2608
|
-
resolved.globalSetup = toArray(resolved.globalSetup || []).map((file) => resolvePath(file, resolved.root));
|
|
2609
|
-
// Add hard-coded default coverage exclusions. These cannot be overidden by user config.
|
|
2610
|
-
// Override original exclude array for cases where user re-uses same object in test.exclude.
|
|
2611
|
-
resolved.coverage.exclude = [
|
|
2612
|
-
...resolved.coverage.exclude,
|
|
2613
|
-
...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
|
|
2614
|
-
...resolved.include,
|
|
2615
|
-
resolved.config && slash(resolved.config),
|
|
2616
|
-
...configFiles,
|
|
2617
|
-
"**/virtual:*",
|
|
2618
|
-
"**/__x00__*",
|
|
2619
|
-
"**/node_modules/**"
|
|
2620
|
-
].filter((pattern) => typeof pattern === "string");
|
|
2621
|
-
resolved.forceRerunTriggers = [...resolved.forceRerunTriggers, ...resolved.setupFiles];
|
|
2622
|
-
if (resolved.cliExclude) resolved.exclude.push(...resolved.cliExclude);
|
|
2623
|
-
if (resolved.runner) resolved.runner = resolvePath(resolved.runner, resolved.root);
|
|
2624
|
-
resolved.attachmentsDir = resolve(resolved.root, resolved.attachmentsDir ?? ".vitest-attachments");
|
|
2625
|
-
if (resolved.snapshotEnvironment) resolved.snapshotEnvironment = resolvePath(resolved.snapshotEnvironment, resolved.root);
|
|
2626
|
-
resolved.testNamePattern = resolved.testNamePattern ? resolved.testNamePattern instanceof RegExp ? resolved.testNamePattern : new RegExp(resolved.testNamePattern) : void 0;
|
|
2627
|
-
if (resolved.snapshotFormat && "plugins" in resolved.snapshotFormat) {
|
|
2628
|
-
resolved.snapshotFormat.plugins = [];
|
|
2629
|
-
// TODO: support it via separate config (like DiffOptions) or via `Function.toString()`
|
|
2630
|
-
if (typeof resolved.snapshotFormat.compareKeys === "function") throw new TypeError(`"snapshotFormat.compareKeys" function is not supported.`);
|
|
2631
|
-
}
|
|
2632
|
-
const UPDATE_SNAPSHOT = resolved.update || process.env.UPDATE_SNAPSHOT;
|
|
2633
|
-
resolved.snapshotOptions = {
|
|
2634
|
-
expand: resolved.expandSnapshotDiff ?? false,
|
|
2635
|
-
snapshotFormat: resolved.snapshotFormat || {},
|
|
2636
|
-
updateSnapshot: isCI && !UPDATE_SNAPSHOT ? "none" : UPDATE_SNAPSHOT ? "all" : "new",
|
|
2637
|
-
resolveSnapshotPath: options.resolveSnapshotPath,
|
|
2638
|
-
snapshotEnvironment: null
|
|
2639
|
-
};
|
|
2640
|
-
resolved.snapshotSerializers ??= [];
|
|
2641
|
-
resolved.snapshotSerializers = resolved.snapshotSerializers.map((file) => resolvePath(file, resolved.root));
|
|
2642
|
-
resolved.forceRerunTriggers.push(...resolved.snapshotSerializers);
|
|
2643
|
-
if (options.resolveSnapshotPath) delete resolved.resolveSnapshotPath;
|
|
2644
|
-
resolved.execArgv ??= [];
|
|
2645
|
-
resolved.pool ??= "threads";
|
|
2646
|
-
if (resolved.pool === "vmForks" || resolved.pool === "vmThreads" || resolved.pool === "typescript") resolved.isolate = false;
|
|
2647
|
-
if (process.env.VITEST_MAX_WORKERS) resolved.maxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS);
|
|
2648
|
-
if (mode === "benchmark") {
|
|
2649
|
-
resolved.benchmark = {
|
|
2650
|
-
...benchmarkConfigDefaults,
|
|
2651
|
-
...resolved.benchmark
|
|
2652
|
-
};
|
|
2653
|
-
// override test config
|
|
2654
|
-
resolved.coverage.enabled = false;
|
|
2655
|
-
resolved.typecheck.enabled = false;
|
|
2656
|
-
resolved.include = resolved.benchmark.include;
|
|
2657
|
-
resolved.exclude = resolved.benchmark.exclude;
|
|
2658
|
-
resolved.includeSource = resolved.benchmark.includeSource;
|
|
2659
|
-
const reporters = Array.from(new Set([...toArray(resolved.benchmark.reporters), ...toArray(options.reporter)])).filter(Boolean);
|
|
2660
|
-
if (reporters.length) resolved.benchmark.reporters = reporters;
|
|
2661
|
-
else resolved.benchmark.reporters = ["default"];
|
|
2662
|
-
if (options.outputFile) resolved.benchmark.outputFile = options.outputFile;
|
|
2663
|
-
// --compare from cli
|
|
2664
|
-
if (options.compare) resolved.benchmark.compare = options.compare;
|
|
2665
|
-
if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
|
|
2666
|
-
}
|
|
2667
|
-
if (typeof resolved.diff === "string") {
|
|
2668
|
-
resolved.diff = resolvePath(resolved.diff, resolved.root);
|
|
2669
|
-
resolved.forceRerunTriggers.push(resolved.diff);
|
|
2670
|
-
}
|
|
2671
|
-
resolved.api = {
|
|
2672
|
-
...resolveApiServerConfig(options, defaultPort),
|
|
2673
|
-
token: crypto.randomUUID()
|
|
2674
|
-
};
|
|
2675
|
-
if (options.related) resolved.related = toArray(options.related).map((file) => resolve(resolved.root, file));
|
|
2676
|
-
/*
|
|
2677
|
-
* Reporters can be defined in many different ways:
|
|
2678
|
-
* { reporter: 'json' }
|
|
2679
|
-
* { reporter: { onFinish() { method() } } }
|
|
2680
|
-
* { reporter: ['json', { onFinish() { method() } }] }
|
|
2681
|
-
* { reporter: [[ 'json' ]] }
|
|
2682
|
-
* { reporter: [[ 'json' ], 'html'] }
|
|
2683
|
-
* { reporter: [[ 'json', { outputFile: 'test.json' } ], 'html'] }
|
|
2684
|
-
*/
|
|
2685
|
-
if (options.reporters) if (!Array.isArray(options.reporters))
|
|
2686
|
-
// Reporter name, e.g. { reporters: 'json' }
|
|
2687
|
-
if (typeof options.reporters === "string") resolved.reporters = [[options.reporters, {}]];
|
|
2688
|
-
else resolved.reporters = [options.reporters];
|
|
2689
|
-
else {
|
|
2690
|
-
resolved.reporters = [];
|
|
2691
|
-
for (const reporter of options.reporters) if (Array.isArray(reporter))
|
|
2692
|
-
// Reporter with options, e.g. { reporters: [ [ 'json', { outputFile: 'test.json' } ] ] }
|
|
2693
|
-
resolved.reporters.push([reporter[0], reporter[1] || {}]);
|
|
2694
|
-
else if (typeof reporter === "string")
|
|
2695
|
-
// Reporter name in array, e.g. { reporters: ["html", "json"]}
|
|
2696
|
-
resolved.reporters.push([reporter, {}]);
|
|
2697
|
-
else
|
|
2698
|
-
// Inline reporter, e.g. { reporter: [{ onFinish() { method() } }] }
|
|
2699
|
-
resolved.reporters.push(reporter);
|
|
2700
|
-
}
|
|
2701
|
-
if (mode !== "benchmark") {
|
|
2702
|
-
// @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
|
|
2703
|
-
// it is passed down as "vitest --reporter ../reporter.js"
|
|
2704
|
-
const reportersFromCLI = resolved.reporter;
|
|
2705
|
-
const cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
|
|
2706
|
-
// ./reporter.js || ../reporter.js, but not .reporters/reporter.js
|
|
2707
|
-
if (/^\.\.?\//.test(reporter)) return resolve(process.cwd(), reporter);
|
|
2708
|
-
return reporter;
|
|
2709
|
-
});
|
|
2710
|
-
if (cliReporters.length) {
|
|
2711
|
-
// When CLI reporters are specified, preserve options from config file
|
|
2712
|
-
const configReportersMap = /* @__PURE__ */ new Map();
|
|
2713
|
-
// Build a map of reporter names to their options from the config
|
|
2714
|
-
for (const reporter of resolved.reporters) if (Array.isArray(reporter)) {
|
|
2715
|
-
const [reporterName, reporterOptions] = reporter;
|
|
2716
|
-
if (typeof reporterName === "string") configReportersMap.set(reporterName, reporterOptions);
|
|
2717
|
-
}
|
|
2718
|
-
resolved.reporters = Array.from(new Set(toArray(cliReporters))).filter(Boolean).map((reporter) => [reporter, configReportersMap.get(reporter) || {}]);
|
|
2719
|
-
}
|
|
2720
|
-
}
|
|
2721
|
-
if (!resolved.reporters.length) {
|
|
2722
|
-
resolved.reporters.push(["default", {}]);
|
|
2723
|
-
// also enable github-actions reporter as a default
|
|
2724
|
-
if (process.env.GITHUB_ACTIONS === "true") resolved.reporters.push(["github-actions", {}]);
|
|
2725
|
-
}
|
|
2726
|
-
if (resolved.changed) resolved.passWithNoTests ??= true;
|
|
2727
|
-
resolved.css ??= {};
|
|
2728
|
-
if (typeof resolved.css === "object") {
|
|
2729
|
-
resolved.css.modules ??= {};
|
|
2730
|
-
resolved.css.modules.classNameStrategy ??= "stable";
|
|
2731
|
-
}
|
|
2732
|
-
if (resolved.cache !== false) {
|
|
2733
|
-
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"`);
|
|
2734
|
-
resolved.cache = { dir: viteConfig.cacheDir };
|
|
2735
|
-
}
|
|
2736
|
-
resolved.sequence ??= {};
|
|
2737
|
-
if (resolved.sequence.shuffle && typeof resolved.sequence.shuffle === "object") {
|
|
2738
|
-
const { files, tests } = resolved.sequence.shuffle;
|
|
2739
|
-
resolved.sequence.sequencer ??= files ? RandomSequencer : BaseSequencer;
|
|
2740
|
-
resolved.sequence.shuffle = tests;
|
|
2741
|
-
}
|
|
2742
|
-
if (!resolved.sequence?.sequencer)
|
|
2743
|
-
// CLI flag has higher priority
|
|
2744
|
-
resolved.sequence.sequencer = resolved.sequence.shuffle ? RandomSequencer : BaseSequencer;
|
|
2745
|
-
resolved.sequence.groupOrder ??= 0;
|
|
2746
|
-
resolved.sequence.hooks ??= "stack";
|
|
2747
|
-
if (resolved.sequence.sequencer === RandomSequencer) resolved.sequence.seed ??= Date.now();
|
|
2748
|
-
resolved.typecheck = {
|
|
2749
|
-
...configDefaults.typecheck,
|
|
2750
|
-
...resolved.typecheck
|
|
2751
|
-
};
|
|
2752
|
-
resolved.typecheck ??= {};
|
|
2753
|
-
resolved.typecheck.enabled ??= false;
|
|
2754
|
-
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."));
|
|
2755
|
-
resolved.browser.enabled ??= false;
|
|
2756
|
-
resolved.browser.headless ??= isCI;
|
|
2757
|
-
resolved.browser.isolate ??= resolved.isolate ?? true;
|
|
2758
|
-
resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark";
|
|
2759
|
-
// disable in headless mode by default, and if CI is detected
|
|
2760
|
-
resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI;
|
|
2761
|
-
resolved.browser.commands ??= {};
|
|
2762
|
-
if (resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve(resolved.root, resolved.browser.screenshotDirectory);
|
|
2763
|
-
if (resolved.inspector.enabled) resolved.browser.trackUnhandledErrors ??= false;
|
|
2764
|
-
resolved.browser.viewport ??= {};
|
|
2765
|
-
resolved.browser.viewport.width ??= 414;
|
|
2766
|
-
resolved.browser.viewport.height ??= 896;
|
|
2767
|
-
resolved.browser.locators ??= {};
|
|
2768
|
-
resolved.browser.locators.testIdAttribute ??= "data-testid";
|
|
2769
|
-
if (typeof resolved.browser.provider === "string") {
|
|
2770
|
-
const source = `@vitest/browser-${resolved.browser.provider}`;
|
|
2771
|
-
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`);
|
|
2772
|
-
}
|
|
2773
|
-
const isPreview = resolved.browser.provider?.name === "preview";
|
|
2774
|
-
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.`);
|
|
2775
|
-
if (isPreview && resolved.browser.screenshotFailures === true) {
|
|
2776
|
-
console.warn(c.yellow([
|
|
2777
|
-
`Browser provider "preview" doesn't support screenshots, `,
|
|
2778
|
-
`so "browser.screenshotFailures" option is forcefully disabled. `,
|
|
2779
|
-
`Set "browser.screenshotFailures" to false or remove it from the config to suppress this warning.`
|
|
2780
|
-
].join("")));
|
|
2781
|
-
resolved.browser.screenshotFailures = false;
|
|
2782
|
-
} else resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui;
|
|
2783
|
-
if (resolved.browser.provider && resolved.browser.provider.options == null) resolved.browser.provider.options = {};
|
|
2784
|
-
resolved.browser.api = resolveApiServerConfig(resolved.browser, defaultBrowserPort) || { port: defaultBrowserPort };
|
|
2785
|
-
// enable includeTaskLocation by default in UI mode
|
|
2786
|
-
if (resolved.browser.enabled) {
|
|
2787
|
-
if (resolved.browser.ui) resolved.includeTaskLocation ??= true;
|
|
2788
|
-
} else if (resolved.ui) resolved.includeTaskLocation ??= true;
|
|
2789
|
-
if (typeof resolved.browser.trace === "string" || !resolved.browser.trace) resolved.browser.trace = { mode: resolved.browser.trace || "off" };
|
|
2790
|
-
if (resolved.browser.trace.tracesDir != null) resolved.browser.trace.tracesDir = resolvePath(resolved.browser.trace.tracesDir, resolved.root);
|
|
2791
|
-
if (toArray(resolved.reporters).some((reporter) => {
|
|
2792
|
-
if (Array.isArray(reporter)) return reporter[0] === "html";
|
|
2793
|
-
return false;
|
|
2794
|
-
})) resolved.includeTaskLocation ??= true;
|
|
2795
|
-
resolved.server ??= {};
|
|
2796
|
-
resolved.server.deps ??= {};
|
|
2797
|
-
if (resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP) {
|
|
2798
|
-
const userFolder = resolved.server.debug?.dump || process.env.VITEST_DEBUG_DUMP;
|
|
2799
|
-
resolved.dumpDir = resolve(resolved.root, typeof userFolder === "string" && userFolder !== "true" ? userFolder : ".vitest-dump", resolved.name || "root");
|
|
2800
|
-
}
|
|
2801
|
-
resolved.testTimeout ??= resolved.browser.enabled ? 15e3 : 5e3;
|
|
2802
|
-
resolved.hookTimeout ??= resolved.browser.enabled ? 3e4 : 1e4;
|
|
2803
|
-
resolved.experimental ??= {};
|
|
2804
|
-
if (resolved.experimental.openTelemetry?.sdkPath) {
|
|
2805
|
-
const sdkPath = resolve(resolved.root, resolved.experimental.openTelemetry.sdkPath);
|
|
2806
|
-
resolved.experimental.openTelemetry.sdkPath = pathToFileURL$1(sdkPath).toString();
|
|
2807
|
-
}
|
|
2808
|
-
if (resolved.experimental.openTelemetry?.browserSdkPath) {
|
|
2809
|
-
const browserSdkPath = resolve(resolved.root, resolved.experimental.openTelemetry.browserSdkPath);
|
|
2810
|
-
resolved.experimental.openTelemetry.browserSdkPath = browserSdkPath;
|
|
2811
|
-
}
|
|
2812
|
-
if (resolved.experimental.fsModuleCachePath) resolved.experimental.fsModuleCachePath = resolve(resolved.root, resolved.experimental.fsModuleCachePath);
|
|
2813
|
-
return resolved;
|
|
2814
|
-
}
|
|
2815
|
-
function isBrowserEnabled(config) {
|
|
2816
|
-
return Boolean(config.browser?.enabled);
|
|
2817
|
-
}
|
|
2818
|
-
function resolveCoverageReporters(configReporters) {
|
|
2819
|
-
// E.g. { reporter: "html" }
|
|
2820
|
-
if (!Array.isArray(configReporters)) return [[configReporters, {}]];
|
|
2821
|
-
const resolvedReporters = [];
|
|
2822
|
-
for (const reporter of configReporters) if (Array.isArray(reporter))
|
|
2823
|
-
// E.g. { reporter: [ ["html", { skipEmpty: true }], ["lcov"], ["json", { file: "map.json" }] ]}
|
|
2824
|
-
resolvedReporters.push([reporter[0], reporter[1] || {}]);
|
|
2825
|
-
else
|
|
2826
|
-
// E.g. { reporter: ["html", "json"]}
|
|
2827
|
-
resolvedReporters.push([reporter, {}]);
|
|
2828
|
-
return resolvedReporters;
|
|
2829
|
-
}
|
|
2830
|
-
function isChromiumName(provider, name) {
|
|
2831
|
-
if (provider === "playwright") return name === "chromium";
|
|
2832
|
-
return name === "chrome" || name === "edge";
|
|
2833
|
-
}
|
|
2834
|
-
function hasBrowserChromium(vitest, config) {
|
|
2835
|
-
const browser = config.browser;
|
|
2836
|
-
if (!browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled) return false;
|
|
2837
|
-
if (browser.name) return isChromiumName(browser.provider.name, browser.name);
|
|
2838
|
-
if (!browser.instances) return false;
|
|
2839
|
-
return browser.instances.some((instance) => {
|
|
2840
|
-
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
2841
|
-
// browser config is filtered out
|
|
2842
|
-
if (!vitest.matchesProjectFilter(name)) return false;
|
|
2843
|
-
return isChromiumName(browser.provider.name, instance.browser);
|
|
2844
|
-
});
|
|
2845
|
-
}
|
|
2846
|
-
function hasOnlyBrowserChromium(vitest, config) {
|
|
2847
|
-
const browser = config.browser;
|
|
2848
|
-
if (!browser || !browser.provider || browser.provider.name === "preview" || !browser.enabled) return false;
|
|
2849
|
-
if (browser.name) return isChromiumName(browser.provider.name, browser.name);
|
|
2850
|
-
if (!browser.instances) return false;
|
|
2851
|
-
return browser.instances.every((instance) => {
|
|
2852
|
-
const name = instance.name || (config.name ? `${config.name} (${instance.browser})` : instance.browser);
|
|
2853
|
-
// browser config is filtered out
|
|
2854
|
-
if (!vitest.matchesProjectFilter(name)) return true;
|
|
2855
|
-
return isChromiumName(browser.provider.name, instance.browser);
|
|
2856
|
-
});
|
|
2857
|
-
}
|
|
2858
|
-
|
|
2859
|
-
const THRESHOLD_KEYS = [
|
|
2860
|
-
"lines",
|
|
2861
|
-
"functions",
|
|
2862
|
-
"statements",
|
|
2863
|
-
"branches"
|
|
2864
|
-
];
|
|
2865
|
-
const GLOBAL_THRESHOLDS_KEY = "global";
|
|
2866
|
-
const DEFAULT_PROJECT = Symbol.for("default-project");
|
|
2867
|
-
let uniqueId = 0;
|
|
2868
|
-
async function getCoverageProvider(options, loader) {
|
|
2869
|
-
const coverageModule = await resolveCoverageProviderModule(options, loader);
|
|
2870
|
-
if (coverageModule) return coverageModule.getProvider();
|
|
2871
|
-
return null;
|
|
2872
|
-
}
|
|
2873
|
-
class BaseCoverageProvider {
|
|
2874
|
-
ctx;
|
|
2875
|
-
name;
|
|
2876
|
-
version;
|
|
2877
|
-
options;
|
|
2878
|
-
globCache = /* @__PURE__ */ new Map();
|
|
2879
|
-
coverageFiles = /* @__PURE__ */ new Map();
|
|
2880
|
-
pendingPromises = [];
|
|
2881
|
-
coverageFilesDirectory;
|
|
2882
|
-
roots = [];
|
|
2883
|
-
_initialize(ctx) {
|
|
2884
|
-
this.ctx = ctx;
|
|
2885
|
-
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} `))}.
|
|
2886
|
-
Running mixed versions is not supported and may lead into bugs
|
|
2887
|
-
Update your dependencies and make sure the versions match.`));
|
|
2888
|
-
const config = ctx._coverageOptions;
|
|
2889
|
-
this.options = {
|
|
2890
|
-
...coverageConfigDefaults,
|
|
2891
|
-
...config,
|
|
2892
|
-
provider: this.name,
|
|
2893
|
-
reportsDirectory: resolve(ctx.config.root, config.reportsDirectory || coverageConfigDefaults.reportsDirectory),
|
|
2894
|
-
reporter: resolveCoverageReporters(config.reporter || coverageConfigDefaults.reporter),
|
|
2895
|
-
thresholds: config.thresholds && {
|
|
2896
|
-
...config.thresholds,
|
|
2897
|
-
lines: config.thresholds["100"] ? 100 : config.thresholds.lines,
|
|
2898
|
-
branches: config.thresholds["100"] ? 100 : config.thresholds.branches,
|
|
2899
|
-
functions: config.thresholds["100"] ? 100 : config.thresholds.functions,
|
|
2900
|
-
statements: config.thresholds["100"] ? 100 : config.thresholds.statements
|
|
2901
|
-
}
|
|
2902
|
-
};
|
|
2903
|
-
const shard = this.ctx.config.shard;
|
|
2904
|
-
const tempDirectory = `.tmp${shard ? `-${shard.index}-${shard.count}` : ""}`;
|
|
2905
|
-
this.coverageFilesDirectory = resolve(this.options.reportsDirectory, tempDirectory);
|
|
2906
|
-
// If --project filter is set pick only roots of resolved projects
|
|
2907
|
-
this.roots = ctx.config.project?.length ? [...new Set(ctx.projects.map((project) => project.config.root))] : [ctx.config.root];
|
|
2908
|
-
}
|
|
2909
|
-
/**
|
|
2910
|
-
* Check if file matches `coverage.include` but not `coverage.exclude`
|
|
2911
|
-
*/
|
|
2912
|
-
isIncluded(_filename, root) {
|
|
2913
|
-
const roots = root ? [root] : this.roots;
|
|
2914
|
-
const filename = slash(_filename);
|
|
2915
|
-
const cacheHit = this.globCache.get(filename);
|
|
2916
|
-
if (cacheHit !== void 0) return cacheHit;
|
|
2917
|
-
// File outside project root with default allowExternal
|
|
2918
|
-
if (this.options.allowExternal === false && roots.every((root) => !filename.startsWith(root))) {
|
|
2919
|
-
this.globCache.set(filename, false);
|
|
2920
|
-
return false;
|
|
2921
|
-
}
|
|
2922
|
-
// By default `coverage.include` matches all files, except "coverage.exclude"
|
|
2923
|
-
const glob = this.options.include || "**";
|
|
2924
|
-
const included = pm.isMatch(filename, glob, {
|
|
2925
|
-
contains: true,
|
|
2926
|
-
dot: true,
|
|
2927
|
-
ignore: this.options.exclude
|
|
2928
|
-
});
|
|
2929
|
-
this.globCache.set(filename, included);
|
|
2930
|
-
return included;
|
|
2931
|
-
}
|
|
2932
|
-
async getUntestedFilesByRoot(testedFiles, include, root) {
|
|
2933
|
-
let includedFiles = await glob(include, {
|
|
2934
|
-
cwd: root,
|
|
2935
|
-
ignore: [...this.options.exclude, ...testedFiles.map((file) => slash(file))],
|
|
2936
|
-
absolute: true,
|
|
2937
|
-
dot: true,
|
|
2938
|
-
onlyFiles: true
|
|
2939
|
-
});
|
|
2940
|
-
// Run again through picomatch as tinyglobby's exclude pattern is different ({ "exclude": ["math"] } should ignore "src/math.ts")
|
|
2941
|
-
includedFiles = includedFiles.filter((file) => this.isIncluded(file, root));
|
|
2942
|
-
if (this.ctx.config.changed) includedFiles = (this.ctx.config.related || []).filter((file) => includedFiles.includes(file));
|
|
2943
|
-
return includedFiles.map((file) => slash(path.resolve(root, file)));
|
|
2944
|
-
}
|
|
2945
|
-
async getUntestedFiles(testedFiles) {
|
|
2946
|
-
if (this.options.include == null) return [];
|
|
2947
|
-
const rootMapper = this.getUntestedFilesByRoot.bind(this, testedFiles, this.options.include);
|
|
2948
|
-
return (await Promise.all(this.roots.map(rootMapper))).flatMap((files) => files);
|
|
2949
|
-
}
|
|
2950
|
-
createCoverageMap() {
|
|
2951
|
-
throw new Error("BaseReporter's createCoverageMap was not overwritten");
|
|
2952
|
-
}
|
|
2953
|
-
async generateReports(_, __) {
|
|
2954
|
-
throw new Error("BaseReporter's generateReports was not overwritten");
|
|
2955
|
-
}
|
|
2956
|
-
async parseConfigModule(_) {
|
|
2957
|
-
throw new Error("BaseReporter's parseConfigModule was not overwritten");
|
|
2958
|
-
}
|
|
2959
|
-
resolveOptions() {
|
|
2960
|
-
return this.options;
|
|
2961
|
-
}
|
|
2962
|
-
async clean(clean = true) {
|
|
2963
|
-
if (clean && existsSync(this.options.reportsDirectory)) await promises.rm(this.options.reportsDirectory, {
|
|
2964
|
-
recursive: true,
|
|
2965
|
-
force: true,
|
|
2966
|
-
maxRetries: 10
|
|
2967
|
-
});
|
|
2968
|
-
if (existsSync(this.coverageFilesDirectory)) await promises.rm(this.coverageFilesDirectory, {
|
|
2969
|
-
recursive: true,
|
|
2970
|
-
force: true,
|
|
2971
|
-
maxRetries: 10
|
|
2972
|
-
});
|
|
2973
|
-
await promises.mkdir(this.coverageFilesDirectory, { recursive: true });
|
|
2974
|
-
this.coverageFiles = /* @__PURE__ */ new Map();
|
|
2975
|
-
this.pendingPromises = [];
|
|
2976
|
-
}
|
|
2977
|
-
onAfterSuiteRun({ coverage, environment, projectName, testFiles }) {
|
|
2978
|
-
if (!coverage) return;
|
|
2979
|
-
let entry = this.coverageFiles.get(projectName || DEFAULT_PROJECT);
|
|
2980
|
-
if (!entry) {
|
|
2981
|
-
entry = {};
|
|
2982
|
-
this.coverageFiles.set(projectName || DEFAULT_PROJECT, entry);
|
|
2983
|
-
}
|
|
2984
|
-
const testFilenames = testFiles.join();
|
|
2985
|
-
const filename = resolve(this.coverageFilesDirectory, `coverage-${uniqueId++}.json`);
|
|
2986
|
-
entry[environment] ??= {};
|
|
2987
|
-
// If there's a result from previous run, overwrite it
|
|
2988
|
-
entry[environment][testFilenames] = filename;
|
|
2989
|
-
const promise = promises.writeFile(filename, JSON.stringify(coverage), "utf-8");
|
|
2990
|
-
this.pendingPromises.push(promise);
|
|
2991
|
-
}
|
|
2992
|
-
async readCoverageFiles({ onFileRead, onFinished, onDebug }) {
|
|
2993
|
-
let index = 0;
|
|
2994
|
-
const total = this.pendingPromises.length;
|
|
2995
|
-
await Promise.all(this.pendingPromises);
|
|
2996
|
-
this.pendingPromises = [];
|
|
2997
|
-
for (const [projectName, coveragePerProject] of this.coverageFiles.entries()) for (const [environment, coverageByTestfiles] of Object.entries(coveragePerProject)) {
|
|
2998
|
-
const filenames = Object.values(coverageByTestfiles);
|
|
2999
|
-
const project = this.ctx.getProjectByName(projectName);
|
|
3000
|
-
for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
|
|
3001
|
-
if (onDebug.enabled) {
|
|
3002
|
-
index += chunk.length;
|
|
3003
|
-
onDebug(`Reading coverage results ${index}/${total}`);
|
|
3004
|
-
}
|
|
3005
|
-
await Promise.all(chunk.map(async (filename) => {
|
|
3006
|
-
const contents = await promises.readFile(filename, "utf-8");
|
|
3007
|
-
onFileRead(JSON.parse(contents));
|
|
3008
|
-
}));
|
|
3009
|
-
}
|
|
3010
|
-
await onFinished(project, environment);
|
|
3011
|
-
}
|
|
3012
|
-
}
|
|
3013
|
-
async cleanAfterRun() {
|
|
3014
|
-
this.coverageFiles = /* @__PURE__ */ new Map();
|
|
3015
|
-
await promises.rm(this.coverageFilesDirectory, { recursive: true });
|
|
3016
|
-
// Remove empty reports directory, e.g. when only text-reporter is used
|
|
3017
|
-
if (readdirSync(this.options.reportsDirectory).length === 0) await promises.rm(this.options.reportsDirectory, { recursive: true });
|
|
3018
|
-
}
|
|
3019
|
-
async onTestFailure() {
|
|
3020
|
-
if (!this.options.reportOnFailure) await this.cleanAfterRun();
|
|
3021
|
-
}
|
|
3022
|
-
async reportCoverage(coverageMap, { allTestsRun }) {
|
|
3023
|
-
await this.generateReports(coverageMap || this.createCoverageMap(), allTestsRun);
|
|
3024
|
-
if (!(!this.options.cleanOnRerun && this.ctx.config.watch)) await this.cleanAfterRun();
|
|
3025
|
-
}
|
|
3026
|
-
async reportThresholds(coverageMap, allTestsRun) {
|
|
3027
|
-
const resolvedThresholds = this.resolveThresholds(coverageMap);
|
|
3028
|
-
this.checkThresholds(resolvedThresholds);
|
|
3029
|
-
if (this.options.thresholds?.autoUpdate && allTestsRun) {
|
|
3030
|
-
if (!this.ctx.vite.config.configFile) throw new Error("Missing configurationFile. The \"coverage.thresholds.autoUpdate\" can only be enabled when configuration file is used.");
|
|
3031
|
-
const configFilePath = this.ctx.vite.config.configFile;
|
|
3032
|
-
const configModule = await this.parseConfigModule(configFilePath);
|
|
3033
|
-
await this.updateThresholds({
|
|
3034
|
-
thresholds: resolvedThresholds,
|
|
3035
|
-
configurationFile: configModule,
|
|
3036
|
-
onUpdate: () => writeFileSync(configFilePath, configModule.generate().code, "utf-8")
|
|
3037
|
-
});
|
|
3038
|
-
}
|
|
3039
|
-
}
|
|
3040
|
-
/**
|
|
3041
|
-
* Constructs collected coverage and users' threshold options into separate sets
|
|
3042
|
-
* where each threshold set holds their own coverage maps. Threshold set is either
|
|
3043
|
-
* for specific files defined by glob pattern or global for all other files.
|
|
3044
|
-
*/
|
|
3045
|
-
resolveThresholds(coverageMap) {
|
|
3046
|
-
const resolvedThresholds = [];
|
|
3047
|
-
const files = coverageMap.files();
|
|
3048
|
-
const globalCoverageMap = this.createCoverageMap();
|
|
3049
|
-
for (const key of Object.keys(this.options.thresholds)) {
|
|
3050
|
-
if (key === "perFile" || key === "autoUpdate" || key === "100" || THRESHOLD_KEYS.includes(key)) continue;
|
|
3051
|
-
const glob = key;
|
|
3052
|
-
const globThresholds = resolveGlobThresholds(this.options.thresholds[glob]);
|
|
3053
|
-
const globCoverageMap = this.createCoverageMap();
|
|
3054
|
-
const matcher = pm(glob);
|
|
3055
|
-
const matchingFiles = files.filter((file) => matcher(relative(this.ctx.config.root, file)));
|
|
3056
|
-
for (const file of matchingFiles) {
|
|
3057
|
-
const fileCoverage = coverageMap.fileCoverageFor(file);
|
|
3058
|
-
globCoverageMap.addFileCoverage(fileCoverage);
|
|
3059
|
-
}
|
|
3060
|
-
resolvedThresholds.push({
|
|
3061
|
-
name: glob,
|
|
3062
|
-
coverageMap: globCoverageMap,
|
|
3063
|
-
thresholds: globThresholds
|
|
3064
|
-
});
|
|
3065
|
-
}
|
|
3066
|
-
// Global threshold is for all files, even if they are included by glob patterns
|
|
3067
|
-
for (const file of files) {
|
|
3068
|
-
const fileCoverage = coverageMap.fileCoverageFor(file);
|
|
3069
|
-
globalCoverageMap.addFileCoverage(fileCoverage);
|
|
3070
|
-
}
|
|
3071
|
-
resolvedThresholds.unshift({
|
|
3072
|
-
name: GLOBAL_THRESHOLDS_KEY,
|
|
3073
|
-
coverageMap: globalCoverageMap,
|
|
3074
|
-
thresholds: {
|
|
3075
|
-
branches: this.options.thresholds?.branches,
|
|
3076
|
-
functions: this.options.thresholds?.functions,
|
|
3077
|
-
lines: this.options.thresholds?.lines,
|
|
3078
|
-
statements: this.options.thresholds?.statements
|
|
3079
|
-
}
|
|
3080
|
-
});
|
|
3081
|
-
return resolvedThresholds;
|
|
3082
|
-
}
|
|
3083
|
-
/**
|
|
3084
|
-
* Check collected coverage against configured thresholds. Sets exit code to 1 when thresholds not reached.
|
|
3085
|
-
*/
|
|
3086
|
-
checkThresholds(allThresholds) {
|
|
3087
|
-
for (const { coverageMap, thresholds, name } of allThresholds) {
|
|
3088
|
-
if (thresholds.branches === void 0 && thresholds.functions === void 0 && thresholds.lines === void 0 && thresholds.statements === void 0) continue;
|
|
3089
|
-
// Construct list of coverage summaries where thresholds are compared against
|
|
3090
|
-
const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => ({
|
|
3091
|
-
file,
|
|
3092
|
-
summary: coverageMap.fileCoverageFor(file).toSummary()
|
|
3093
|
-
})) : [{
|
|
3094
|
-
file: null,
|
|
3095
|
-
summary: coverageMap.getCoverageSummary()
|
|
3096
|
-
}];
|
|
3097
|
-
// Check thresholds of each summary
|
|
3098
|
-
for (const { summary, file } of summaries) for (const thresholdKey of THRESHOLD_KEYS) {
|
|
3099
|
-
const threshold = thresholds[thresholdKey];
|
|
3100
|
-
if (threshold === void 0) continue;
|
|
3101
|
-
/**
|
|
3102
|
-
* Positive thresholds are treated as minimum coverage percentages (X means: X% of lines must be covered),
|
|
3103
|
-
* while negative thresholds are treated as maximum uncovered counts (-X means: X lines may be uncovered).
|
|
3104
|
-
*/
|
|
3105
|
-
if (threshold >= 0) {
|
|
3106
|
-
const coverage = summary.data[thresholdKey].pct;
|
|
3107
|
-
if (coverage < threshold) {
|
|
3108
|
-
process.exitCode = 1;
|
|
3109
|
-
/**
|
|
3110
|
-
* Generate error message based on perFile flag:
|
|
3111
|
-
* - ERROR: Coverage for statements (33.33%) does not meet threshold (85%) for src/math.ts
|
|
3112
|
-
* - ERROR: Coverage for statements (50%) does not meet global threshold (85%)
|
|
3113
|
-
*/
|
|
3114
|
-
let errorMessage = `ERROR: Coverage for ${thresholdKey} (${coverage}%) does not meet ${name === GLOBAL_THRESHOLDS_KEY ? name : `"${name}"`} threshold (${threshold}%)`;
|
|
3115
|
-
if (this.options.thresholds?.perFile && file) errorMessage += ` for ${relative("./", file).replace(/\\/g, "/")}`;
|
|
3116
|
-
this.ctx.logger.error(errorMessage);
|
|
3117
|
-
}
|
|
3118
|
-
} else {
|
|
3119
|
-
const uncovered = summary.data[thresholdKey].total - summary.data[thresholdKey].covered;
|
|
3120
|
-
const absoluteThreshold = threshold * -1;
|
|
3121
|
-
if (uncovered > absoluteThreshold) {
|
|
3122
|
-
process.exitCode = 1;
|
|
3123
|
-
/**
|
|
3124
|
-
* Generate error message based on perFile flag:
|
|
3125
|
-
* - ERROR: Uncovered statements (33) exceed threshold (30) for src/math.ts
|
|
3126
|
-
* - ERROR: Uncovered statements (33) exceed global threshold (30)
|
|
3127
|
-
*/
|
|
3128
|
-
let errorMessage = `ERROR: Uncovered ${thresholdKey} (${uncovered}) exceed ${name === GLOBAL_THRESHOLDS_KEY ? name : `"${name}"`} threshold (${absoluteThreshold})`;
|
|
3129
|
-
if (this.options.thresholds?.perFile && file) errorMessage += ` for ${relative("./", file).replace(/\\/g, "/")}`;
|
|
3130
|
-
this.ctx.logger.error(errorMessage);
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
}
|
|
3135
|
-
}
|
|
3136
|
-
/**
|
|
3137
|
-
* Check if current coverage is above configured thresholds and bump the thresholds if needed
|
|
3138
|
-
*/
|
|
3139
|
-
async updateThresholds({ thresholds: allThresholds, onUpdate, configurationFile }) {
|
|
3140
|
-
let updatedThresholds = false;
|
|
3141
|
-
const config = resolveConfig(configurationFile);
|
|
3142
|
-
assertConfigurationModule(config);
|
|
3143
|
-
for (const { coverageMap, thresholds, name } of allThresholds) {
|
|
3144
|
-
const summaries = this.options.thresholds?.perFile ? coverageMap.files().map((file) => coverageMap.fileCoverageFor(file).toSummary()) : [coverageMap.getCoverageSummary()];
|
|
3145
|
-
const thresholdsToUpdate = [];
|
|
3146
|
-
for (const key of THRESHOLD_KEYS) {
|
|
3147
|
-
const threshold = thresholds[key] ?? 100;
|
|
3148
|
-
/**
|
|
3149
|
-
* Positive thresholds are treated as minimum coverage percentages (X means: X% of lines must be covered),
|
|
3150
|
-
* while negative thresholds are treated as maximum uncovered counts (-X means: X lines may be uncovered).
|
|
3151
|
-
*/
|
|
3152
|
-
if (threshold >= 0) {
|
|
3153
|
-
const actual = Math.min(...summaries.map((summary) => summary[key].pct));
|
|
3154
|
-
if (actual > threshold) thresholdsToUpdate.push([key, actual]);
|
|
3155
|
-
} else {
|
|
3156
|
-
const absoluteThreshold = threshold * -1;
|
|
3157
|
-
const actual = Math.max(...summaries.map((summary) => summary[key].total - summary[key].covered));
|
|
3158
|
-
if (actual < absoluteThreshold) {
|
|
3159
|
-
// If everything was covered, set new threshold to 100% (since a threshold of 0 would be considered as 0%)
|
|
3160
|
-
const updatedThreshold = actual === 0 ? 100 : actual * -1;
|
|
3161
|
-
thresholdsToUpdate.push([key, updatedThreshold]);
|
|
3162
|
-
}
|
|
3163
|
-
}
|
|
3164
|
-
}
|
|
3165
|
-
if (thresholdsToUpdate.length === 0) continue;
|
|
3166
|
-
updatedThresholds = true;
|
|
3167
|
-
const thresholdFormatter = typeof this.options.thresholds?.autoUpdate === "function" ? this.options.thresholds?.autoUpdate : (value) => value;
|
|
3168
|
-
for (const [threshold, newValue] of thresholdsToUpdate) {
|
|
3169
|
-
const formattedValue = thresholdFormatter(newValue);
|
|
3170
|
-
if (name === GLOBAL_THRESHOLDS_KEY) config.test.coverage.thresholds[threshold] = formattedValue;
|
|
3171
|
-
else {
|
|
3172
|
-
const glob = config.test.coverage.thresholds[name];
|
|
3173
|
-
glob[threshold] = formattedValue;
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
}
|
|
3177
|
-
if (updatedThresholds) {
|
|
3178
|
-
this.ctx.logger.log("Updating thresholds to configuration file. You may want to push with updated coverage thresholds.");
|
|
3179
|
-
onUpdate();
|
|
3180
|
-
}
|
|
3181
|
-
}
|
|
3182
|
-
async mergeReports(coverageMaps) {
|
|
3183
|
-
const coverageMap = this.createCoverageMap();
|
|
3184
|
-
for (const coverage of coverageMaps) coverageMap.merge(coverage);
|
|
3185
|
-
await this.generateReports(coverageMap, true);
|
|
3186
|
-
}
|
|
3187
|
-
hasTerminalReporter(reporters) {
|
|
3188
|
-
return reporters.some(([reporter]) => reporter === "text" || reporter === "text-summary" || reporter === "text-lcov" || reporter === "teamcity");
|
|
3189
|
-
}
|
|
3190
|
-
toSlices(array, size) {
|
|
3191
|
-
return array.reduce((chunks, item) => {
|
|
3192
|
-
const index = Math.max(0, chunks.length - 1);
|
|
3193
|
-
const lastChunk = chunks[index] || [];
|
|
3194
|
-
chunks[index] = lastChunk;
|
|
3195
|
-
if (lastChunk.length >= size) chunks.push([item]);
|
|
3196
|
-
else lastChunk.push(item);
|
|
3197
|
-
return chunks;
|
|
3198
|
-
}, []);
|
|
3199
|
-
}
|
|
3200
|
-
createUncoveredFileTransformer(ctx) {
|
|
3201
|
-
const servers = [...ctx.projects.map((project) => ({
|
|
3202
|
-
root: project.config.root,
|
|
3203
|
-
isBrowserEnabled: project.isBrowserEnabled(),
|
|
3204
|
-
vite: project.vite,
|
|
3205
|
-
environment: project.config.environment
|
|
3206
|
-
})), (
|
|
3207
|
-
// Check core last as it will match all files anyway
|
|
3208
|
-
{
|
|
3209
|
-
root: ctx.config.root,
|
|
3210
|
-
vite: ctx.vite,
|
|
3211
|
-
isBrowserEnabled: ctx.getRootProject().isBrowserEnabled(),
|
|
3212
|
-
environment: ctx.config.environment
|
|
3213
|
-
})];
|
|
3214
|
-
return async function transformFile(filename) {
|
|
3215
|
-
let lastError;
|
|
3216
|
-
for (const { root, vite, isBrowserEnabled, environment } of servers) {
|
|
3217
|
-
// On Windows root doesn't start with "/" while filenames do
|
|
3218
|
-
if (!filename.startsWith(root) && !filename.startsWith(`/${root}`)) continue;
|
|
3219
|
-
if (isBrowserEnabled) {
|
|
3220
|
-
const result = await vite.environments.client.transformRequest(filename).catch(() => null);
|
|
3221
|
-
if (result) return result;
|
|
3222
|
-
}
|
|
3223
|
-
try {
|
|
3224
|
-
if (environment === "jsdom" || environment === "happy-dom") return await vite.environments.client.transformRequest(filename);
|
|
3225
|
-
return await vite.environments.ssr.transformRequest(filename);
|
|
3226
|
-
} catch (error) {
|
|
3227
|
-
lastError = error;
|
|
3228
|
-
}
|
|
3229
|
-
}
|
|
3230
|
-
// All vite servers failed to transform the file
|
|
3231
|
-
throw lastError;
|
|
3232
|
-
};
|
|
3233
|
-
}
|
|
3234
|
-
}
|
|
3235
|
-
/**
|
|
3236
|
-
* Narrow down `unknown` glob thresholds to resolved ones
|
|
3237
|
-
*/
|
|
3238
|
-
function resolveGlobThresholds(thresholds) {
|
|
3239
|
-
if (!thresholds || typeof thresholds !== "object") return {};
|
|
3240
|
-
if (100 in thresholds && thresholds[100] === true) return {
|
|
3241
|
-
lines: 100,
|
|
3242
|
-
branches: 100,
|
|
3243
|
-
functions: 100,
|
|
3244
|
-
statements: 100
|
|
3245
|
-
};
|
|
3246
|
-
return {
|
|
3247
|
-
lines: "lines" in thresholds && typeof thresholds.lines === "number" ? thresholds.lines : void 0,
|
|
3248
|
-
branches: "branches" in thresholds && typeof thresholds.branches === "number" ? thresholds.branches : void 0,
|
|
3249
|
-
functions: "functions" in thresholds && typeof thresholds.functions === "number" ? thresholds.functions : void 0,
|
|
3250
|
-
statements: "statements" in thresholds && typeof thresholds.statements === "number" ? thresholds.statements : void 0
|
|
3251
|
-
};
|
|
3252
|
-
}
|
|
3253
|
-
function assertConfigurationModule(config) {
|
|
3254
|
-
try {
|
|
3255
|
-
// @ts-expect-error -- Intentional unsafe null pointer check as wrapped in try-catch
|
|
3256
|
-
if (typeof config.test.coverage.thresholds !== "object") throw new TypeError("Expected config.test.coverage.thresholds to be an object");
|
|
3257
|
-
} catch (error) {
|
|
3258
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3259
|
-
throw new Error(`Unable to parse thresholds from configuration file: ${message}`);
|
|
3260
|
-
}
|
|
3261
|
-
}
|
|
3262
|
-
function resolveConfig(configModule) {
|
|
3263
|
-
const mod = configModule.exports.default;
|
|
3264
|
-
try {
|
|
3265
|
-
// Check for "export default { test: {...} }"
|
|
3266
|
-
if (mod.$type === "object") return mod;
|
|
3267
|
-
// "export default defineConfig(...)"
|
|
3268
|
-
let config = resolveDefineConfig(mod);
|
|
3269
|
-
if (config) return config;
|
|
3270
|
-
// "export default mergeConfig(..., defineConfig(...))"
|
|
3271
|
-
if (mod.$type === "function-call" && mod.$callee === "mergeConfig") {
|
|
3272
|
-
config = resolveMergeConfig(mod);
|
|
3273
|
-
if (config) return config;
|
|
3274
|
-
}
|
|
3275
|
-
} catch (error) {
|
|
3276
|
-
// Reduce magicast's verbose errors to readable ones
|
|
3277
|
-
throw new Error(error instanceof Error ? error.message : String(error));
|
|
3278
|
-
}
|
|
3279
|
-
throw new Error("Failed to update coverage thresholds. Configuration file is too complex.");
|
|
3280
|
-
}
|
|
3281
|
-
function resolveDefineConfig(mod) {
|
|
3282
|
-
if (mod.$type === "function-call" && mod.$callee === "defineConfig") {
|
|
3283
|
-
// "export default defineConfig({ test: {...} })"
|
|
3284
|
-
if (mod.$args[0].$type === "object") return mod.$args[0];
|
|
3285
|
-
if (mod.$args[0].$type === "arrow-function-expression") {
|
|
3286
|
-
if (mod.$args[0].$body.$type === "object")
|
|
3287
|
-
// "export default defineConfig(() => ({ test: {...} }))"
|
|
3288
|
-
return mod.$args[0].$body;
|
|
3289
|
-
// "export default defineConfig(() => mergeConfig({...}, ...))"
|
|
3290
|
-
const config = resolveMergeConfig(mod.$args[0].$body);
|
|
3291
|
-
if (config) return config;
|
|
3292
|
-
}
|
|
3293
|
-
}
|
|
3294
|
-
}
|
|
3295
|
-
function resolveMergeConfig(mod) {
|
|
3296
|
-
if (mod.$type === "function-call" && mod.$callee === "mergeConfig") for (const arg of mod.$args) {
|
|
3297
|
-
const config = resolveDefineConfig(arg);
|
|
3298
|
-
if (config) return config;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
|
|
3302
|
-
export { BaseCoverageProvider as B, RandomSequencer as R, resolveApiServerConfig as a, BaseSequencer as b, isBrowserEnabled as c, resolveModule as d, getCoverageProvider as g, hash as h, isPackageExists as i, resolveConfig$1 as r };
|
|
2338
|
+
export { isPackageExists as i, resolveModule as r };
|