vitest 3.1.3 → 3.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +1 -1
- package/dist/chunks/{base.DslwPSCy.js → base.SfTiRNZf.js} +1 -1
- package/dist/chunks/{cac.BN2e7cE1.js → cac.TfX2-DVH.js} +9 -7
- package/dist/chunks/{cli-api.Bti1vevt.js → cli-api.2970Nj9J.js} +908 -847
- package/dist/chunks/{coverage.87S59-Sl.js → coverage.z0LVMxgb.js} +3 -1
- package/dist/chunks/{environment.d.Dmw5ulng.d.ts → environment.d.D8YDy2v5.d.ts} +2 -1
- package/dist/chunks/{global.d.CXRAxnWc.d.ts → global.d.BCOHQEpR.d.ts} +7 -2
- package/dist/chunks/{globals.CZAEe_Gf.js → globals.Cg4NtV4P.js} +2 -2
- package/dist/chunks/{index.B0uVAVvx.js → index.BPc7M5ni.js} +1 -1
- package/dist/chunks/{index.De2FqGmR.js → index.CUacZlWG.js} +14 -8
- package/dist/chunks/{index.Cu2UlluP.js → index.DbWBPwtH.js} +2 -2
- package/dist/chunks/{reporters.d.DG9VKi4m.d.ts → reporters.d.DGm4k1Wx.d.ts} +51 -11
- package/dist/chunks/{runBaseTests.BV8m0B-u.js → runBaseTests.CguliJB5.js} +5 -5
- package/dist/chunks/{setup-common.AQcDs321.js → setup-common.BP6KrF_Z.js} +1 -1
- package/dist/chunks/{utils.Cc45eY3L.js → utils.8gfOgtry.js} +19 -12
- package/dist/chunks/{vi.ClIskdbk.js → vi.BFR5YIgu.js} +3 -0
- package/dist/chunks/{vite.d.D3ndlJcw.d.ts → vite.d.DjP_ALCZ.d.ts} +1 -1
- package/dist/chunks/{worker.d.CHGSOG0s.d.ts → worker.d.CoCI7hzP.d.ts} +1 -1
- package/dist/chunks/{worker.d.C-KN07Ls.d.ts → worker.d.D5Xdi-Zr.d.ts} +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.cjs +3 -0
- package/dist/config.d.ts +8 -5
- package/dist/config.js +3 -0
- package/dist/coverage.d.ts +3 -3
- package/dist/coverage.js +1 -1
- package/dist/environments.d.ts +2 -2
- package/dist/execute.d.ts +2 -2
- package/dist/index.d.ts +45 -10
- package/dist/index.js +2 -2
- package/dist/node.d.ts +8 -8
- package/dist/node.js +7 -7
- package/dist/reporters.d.ts +3 -3
- package/dist/reporters.js +2 -2
- package/dist/runners.d.ts +1 -1
- package/dist/runners.js +2 -2
- package/dist/workers/forks.js +1 -1
- package/dist/workers/runVmTests.js +5 -5
- package/dist/workers/threads.js +1 -1
- package/dist/workers.d.ts +3 -3
- package/dist/workers.js +1 -1
- package/package.json +11 -11
|
@@ -7,13 +7,13 @@ import { f as findUp, p as prompt } from './index.DBIGubLC.js';
|
|
|
7
7
|
import * as vite from 'vite';
|
|
8
8
|
import { searchForWorkspaceRoot, version, mergeConfig, createServer } from 'vite';
|
|
9
9
|
import { A as API_PATH, c as configFiles, d as defaultBrowserPort, w as workspacesFiles, a as defaultPort } from './constants.BZZyIeIE.js';
|
|
10
|
-
import { generateFileHash,
|
|
10
|
+
import { generateFileHash, limitConcurrency, createFileTask, hasFailed, getTasks, getTests } from '@vitest/runner/utils';
|
|
11
11
|
import { SnapshotManager } from '@vitest/snapshot/manager';
|
|
12
12
|
import { ViteNodeRunner } from 'vite-node/client';
|
|
13
13
|
import { ViteNodeServer } from 'vite-node/server';
|
|
14
|
-
import { v as version$1 } from './cac.
|
|
14
|
+
import { v as version$1 } from './cac.TfX2-DVH.js';
|
|
15
15
|
import { c as createBirpc } from './index.CJ0plNrh.js';
|
|
16
|
-
import { p as parse, s as stringify, g as printError, h as generateCodeFrame, b as BenchmarkReportsMap, R as ReportersMap, i as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.
|
|
16
|
+
import { p as parse, s as stringify, g as printError, h as generateCodeFrame, b as BenchmarkReportsMap, R as ReportersMap, i as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.CUacZlWG.js';
|
|
17
17
|
import require$$0$3 from 'events';
|
|
18
18
|
import require$$1$1 from 'https';
|
|
19
19
|
import require$$2 from 'http';
|
|
@@ -28,11 +28,11 @@ import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
|
|
|
28
28
|
import { parseErrorStacktrace } from '@vitest/utils/source-map';
|
|
29
29
|
import crypto from 'node:crypto';
|
|
30
30
|
import { distDir, rootDir } from '../path.js';
|
|
31
|
-
import { R as RandomSequencer, i as isPackageExists, h as hash, V as VitestCache, g as getFilePoolName, d as isBrowserEnabled, m as mm, r as resolveConfig, e as groupBy, f as getCoverageProvider, j as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.
|
|
31
|
+
import { R as RandomSequencer, i as isPackageExists, h as hash, V as VitestCache, g as getFilePoolName, d as isBrowserEnabled, m as mm, r as resolveConfig, e as groupBy, f as getCoverageProvider, j as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.z0LVMxgb.js';
|
|
32
32
|
import { c as convertTasksToEvents } from './typechecker.DYQbn8uK.js';
|
|
33
33
|
import { Console } from 'node:console';
|
|
34
34
|
import c from 'tinyrainbow';
|
|
35
|
-
import { c as formatProjectName, w as withLabel, e as errorBanner, d as divider } from './utils.
|
|
35
|
+
import { c as formatProjectName, w as withLabel, e as errorBanner, d as divider } from './utils.8gfOgtry.js';
|
|
36
36
|
import { createRequire } from 'node:module';
|
|
37
37
|
import url from 'node:url';
|
|
38
38
|
import { i as isTTY, a as isWindows } from './env.Dq0hM4Xv.js';
|
|
@@ -5125,6 +5125,12 @@ function setup(ctx, _server) {
|
|
|
5125
5125
|
getResolvedProjectNames() {
|
|
5126
5126
|
return ctx.projects.map((p) => p.name);
|
|
5127
5127
|
},
|
|
5128
|
+
getResolvedProjectLabels() {
|
|
5129
|
+
return ctx.projects.map((p) => ({
|
|
5130
|
+
name: p.name,
|
|
5131
|
+
color: p.color
|
|
5132
|
+
}));
|
|
5133
|
+
},
|
|
5128
5134
|
async getTransformResult(projectName, id, browser = false) {
|
|
5129
5135
|
const project = ctx.getProjectByName(projectName);
|
|
5130
5136
|
const result = browser ? await project.browser.vite.transformRequest(id) : await project.vitenode.transformRequest(id);
|
|
@@ -5409,6 +5415,9 @@ class Logger {
|
|
|
5409
5415
|
printError(err, options = {}) {
|
|
5410
5416
|
printError(err, this.ctx, this, options);
|
|
5411
5417
|
}
|
|
5418
|
+
deprecate(message) {
|
|
5419
|
+
this.log(c.bold(c.bgYellow(" DEPRECATED ")), c.yellow(message));
|
|
5420
|
+
}
|
|
5412
5421
|
clearHighlightCache(filename) {
|
|
5413
5422
|
if (filename) {
|
|
5414
5423
|
this._highlights.delete(filename);
|
|
@@ -5449,7 +5458,7 @@ class Logger {
|
|
|
5449
5458
|
const config = project.config;
|
|
5450
5459
|
const printConfig = !project.isRootProject() && project.name;
|
|
5451
5460
|
if (printConfig) {
|
|
5452
|
-
this.console.error(`\n${formatProjectName(project
|
|
5461
|
+
this.console.error(`\n${formatProjectName(project)}\n`);
|
|
5453
5462
|
}
|
|
5454
5463
|
if (config.include) {
|
|
5455
5464
|
this.console.error(c.dim("include: ") + c.yellow(config.include.join(comma)));
|
|
@@ -5501,7 +5510,7 @@ class Logger {
|
|
|
5501
5510
|
if (!origin) {
|
|
5502
5511
|
return;
|
|
5503
5512
|
}
|
|
5504
|
-
const output = project.isRootProject() ? "" : formatProjectName(project
|
|
5513
|
+
const output = project.isRootProject() ? "" : formatProjectName(project);
|
|
5505
5514
|
const provider = project.browser.provider.name;
|
|
5506
5515
|
const providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
|
|
5507
5516
|
this.log(c.dim(`${output}Browser runner started${providerString} ${c.dim("at")} ${c.blue(new URL("/", origin))}\n`));
|
|
@@ -6521,7 +6530,8 @@ function resolveOptimizerConfig(_testOptions, viteOptions, testConfig, viteCache
|
|
|
6521
6530
|
const runtime = currentInclude.filter((n) => n.endsWith("jsx-dev-runtime") || n.endsWith("jsx-runtime"));
|
|
6522
6531
|
exclude.push(...runtime);
|
|
6523
6532
|
const include = (testOptions.include || viteOptions?.include || []).filter((n) => !exclude.includes(n));
|
|
6524
|
-
|
|
6533
|
+
const projectName = typeof testConfig.name === "string" ? testConfig.name : testConfig.name?.label;
|
|
6534
|
+
newConfig.cacheDir = testConfig.cache !== false && testConfig.cache?.dir || VitestCache.resolveCacheDir(root, viteCacheDir, projectName);
|
|
6525
6535
|
newConfig.optimizeDeps = {
|
|
6526
6536
|
...viteOptions,
|
|
6527
6537
|
...testOptions,
|
|
@@ -6688,7 +6698,10 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
6688
6698
|
const defines = deleteDefineConfig(viteConfig);
|
|
6689
6699
|
const testConfig = viteConfig.test || {};
|
|
6690
6700
|
const root = testConfig.root || viteConfig.root || options.root;
|
|
6691
|
-
let name = testConfig.name
|
|
6701
|
+
let { label: name, color } = typeof testConfig.name === "string" ? { label: testConfig.name } : {
|
|
6702
|
+
label: "",
|
|
6703
|
+
...testConfig.name
|
|
6704
|
+
};
|
|
6692
6705
|
if (!name) {
|
|
6693
6706
|
if (typeof options.workspacePath === "string") {
|
|
6694
6707
|
const dir = options.workspacePath.endsWith("/") ? options.workspacePath.slice(0, -1) : dirname(options.workspacePath);
|
|
@@ -6726,7 +6739,10 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
6726
6739
|
fs: { allow: resolveFsAllow(project.vitest.config.root, project.vitest.vite.config.configFile) }
|
|
6727
6740
|
},
|
|
6728
6741
|
environments: { ssr: { resolve: resolveOptions } },
|
|
6729
|
-
test: { name
|
|
6742
|
+
test: { name: {
|
|
6743
|
+
label: name,
|
|
6744
|
+
color
|
|
6745
|
+
} }
|
|
6730
6746
|
};
|
|
6731
6747
|
if (project.vitest._options.browser && viteConfig.test?.browser) {
|
|
6732
6748
|
viteConfig.test.browser = mergeConfig(viteConfig.test.browser, project.vitest._options.browser);
|
|
@@ -6980,6 +6996,12 @@ class TestProject {
|
|
|
6980
6996
|
return this.config.name || "";
|
|
6981
6997
|
}
|
|
6982
6998
|
/**
|
|
6999
|
+
* The color used when reporting tasks of this project.
|
|
7000
|
+
*/
|
|
7001
|
+
get color() {
|
|
7002
|
+
return this.config.color;
|
|
7003
|
+
}
|
|
7004
|
+
/**
|
|
6983
7005
|
* Serialized project configuration. This is the config that tests receive.
|
|
6984
7006
|
*/
|
|
6985
7007
|
get serializedConfig() {
|
|
@@ -7364,182 +7386,499 @@ async function initializeProject(workspacePath, ctx, options) {
|
|
|
7364
7386
|
return project;
|
|
7365
7387
|
}
|
|
7366
7388
|
|
|
7367
|
-
async function
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
const CustomReporter = await loadCustomReporterModule(reporterName, runner);
|
|
7393
|
-
return new CustomReporter(reporterOptions);
|
|
7394
|
-
}
|
|
7389
|
+
async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projectsDefinition, names) {
|
|
7390
|
+
const { configFiles, projectConfigs, nonConfigDirectories } = await resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition);
|
|
7391
|
+
const overridesOptions = [
|
|
7392
|
+
"logHeapUsage",
|
|
7393
|
+
"allowOnly",
|
|
7394
|
+
"sequence",
|
|
7395
|
+
"testTimeout",
|
|
7396
|
+
"pool",
|
|
7397
|
+
"update",
|
|
7398
|
+
"globals",
|
|
7399
|
+
"expandSnapshotDiff",
|
|
7400
|
+
"disableConsoleIntercept",
|
|
7401
|
+
"retry",
|
|
7402
|
+
"testNamePattern",
|
|
7403
|
+
"passWithNoTests",
|
|
7404
|
+
"bail",
|
|
7405
|
+
"isolate",
|
|
7406
|
+
"printConsoleTrace",
|
|
7407
|
+
"inspect",
|
|
7408
|
+
"inspectBrk",
|
|
7409
|
+
"fileParallelism"
|
|
7410
|
+
];
|
|
7411
|
+
const cliOverrides = overridesOptions.reduce((acc, name) => {
|
|
7412
|
+
if (name in cliOptions) {
|
|
7413
|
+
acc[name] = cliOptions[name];
|
|
7395
7414
|
}
|
|
7396
|
-
return
|
|
7397
|
-
});
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7415
|
+
return acc;
|
|
7416
|
+
}, {});
|
|
7417
|
+
const projectPromises = [];
|
|
7418
|
+
const fileProjects = [...configFiles, ...nonConfigDirectories];
|
|
7419
|
+
const concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
|
|
7420
|
+
projectConfigs.forEach((options, index) => {
|
|
7421
|
+
const configRoot = workspaceConfigPath ? dirname(workspaceConfigPath) : vitest.config.root;
|
|
7422
|
+
const configFile = typeof options.extends === "string" ? resolve(configRoot, options.extends) : options.extends === true ? vitest.vite.config.configFile || false : false;
|
|
7423
|
+
const root = options.root ? resolve(configRoot, options.root) : vitest.config.root;
|
|
7424
|
+
projectPromises.push(concurrent(() => initializeProject(index, vitest, {
|
|
7425
|
+
...options,
|
|
7426
|
+
root,
|
|
7427
|
+
configFile,
|
|
7428
|
+
test: {
|
|
7429
|
+
...options.test,
|
|
7430
|
+
...cliOverrides
|
|
7409
7431
|
}
|
|
7410
|
-
}
|
|
7411
|
-
return referenceOrInstance;
|
|
7432
|
+
})));
|
|
7412
7433
|
});
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
if (colonIndex === -1) {
|
|
7419
|
-
return { filename: filter };
|
|
7420
|
-
}
|
|
7421
|
-
const [parsedFilename, lineNumber] = [filter.substring(0, colonIndex), filter.substring(colonIndex + 1)];
|
|
7422
|
-
if (lineNumber.match(/^\d+$/)) {
|
|
7423
|
-
return {
|
|
7424
|
-
filename: parsedFilename,
|
|
7425
|
-
lineNumber: Number.parseInt(lineNumber)
|
|
7426
|
-
};
|
|
7427
|
-
} else if (lineNumber.match(/^\d+-\d+$/)) {
|
|
7428
|
-
throw new RangeLocationFilterProvidedError(filter);
|
|
7429
|
-
} else {
|
|
7430
|
-
return { filename: filter };
|
|
7431
|
-
}
|
|
7432
|
-
}
|
|
7433
|
-
function groupFilters(filters) {
|
|
7434
|
-
const groupedFilters_ = groupBy(filters, (f) => f.filename);
|
|
7435
|
-
const groupedFilters = Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
|
|
7436
|
-
const [filename, filters] = entry;
|
|
7437
|
-
const testLocations = filters.map((f) => f.lineNumber);
|
|
7438
|
-
return [filename, testLocations.filter((l) => l !== undefined)];
|
|
7439
|
-
}));
|
|
7440
|
-
return groupedFilters;
|
|
7441
|
-
}
|
|
7442
|
-
|
|
7443
|
-
class VitestSpecifications {
|
|
7444
|
-
_cachedSpecs = new Map();
|
|
7445
|
-
constructor(vitest) {
|
|
7446
|
-
this.vitest = vitest;
|
|
7447
|
-
}
|
|
7448
|
-
getModuleSpecifications(moduleId) {
|
|
7449
|
-
const _cached = this.getCachedSpecifications(moduleId);
|
|
7450
|
-
if (_cached) {
|
|
7451
|
-
return _cached;
|
|
7452
|
-
}
|
|
7453
|
-
const specs = [];
|
|
7454
|
-
for (const project of this.vitest.projects) {
|
|
7455
|
-
if (project._isCachedTestFile(moduleId)) {
|
|
7456
|
-
specs.push(project.createSpecification(moduleId));
|
|
7457
|
-
}
|
|
7458
|
-
if (project._isCachedTypecheckFile(moduleId)) {
|
|
7459
|
-
specs.push(project.createSpecification(moduleId, [], "typescript"));
|
|
7434
|
+
for (const path of fileProjects) {
|
|
7435
|
+
if (vitest.vite.config.configFile === path) {
|
|
7436
|
+
const project = getDefaultTestProject(vitest);
|
|
7437
|
+
if (project) {
|
|
7438
|
+
projectPromises.push(Promise.resolve(project));
|
|
7460
7439
|
}
|
|
7440
|
+
continue;
|
|
7461
7441
|
}
|
|
7462
|
-
|
|
7463
|
-
|
|
7442
|
+
const configFile = path.endsWith("/") ? false : path;
|
|
7443
|
+
const root = path.endsWith("/") ? path : dirname(path);
|
|
7444
|
+
projectPromises.push(concurrent(() => initializeProject(path, vitest, {
|
|
7445
|
+
root,
|
|
7446
|
+
configFile,
|
|
7447
|
+
test: cliOverrides
|
|
7448
|
+
})));
|
|
7464
7449
|
}
|
|
7465
|
-
|
|
7466
|
-
|
|
7450
|
+
if (!projectPromises.length) {
|
|
7451
|
+
throw new Error([
|
|
7452
|
+
"No projects were found. Make sure your configuration is correct. ",
|
|
7453
|
+
vitest.config.project.length ? `The filter matched no projects: ${vitest.config.project.join(", ")}. ` : "",
|
|
7454
|
+
`The projects definition: ${JSON.stringify(projectsDefinition, null, 4)}.`
|
|
7455
|
+
].join(""));
|
|
7467
7456
|
}
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
if (
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
const testLines = groupFilters(parsedFilters.map((f) => ({
|
|
7476
|
-
...f,
|
|
7477
|
-
filename: resolve(dir, f.filename)
|
|
7478
|
-
})));
|
|
7479
|
-
const testLocHasMatch = {};
|
|
7480
|
-
await Promise.all(this.vitest.projects.map(async (project) => {
|
|
7481
|
-
const { testFiles, typecheckTestFiles } = await project.globTestFiles(parsedFilters.map((f) => f.filename));
|
|
7482
|
-
testFiles.forEach((file) => {
|
|
7483
|
-
const lines = testLines[file];
|
|
7484
|
-
testLocHasMatch[file] = true;
|
|
7485
|
-
const spec = project.createSpecification(file, lines);
|
|
7486
|
-
this.ensureSpecificationCached(spec);
|
|
7487
|
-
files.push(spec);
|
|
7488
|
-
});
|
|
7489
|
-
typecheckTestFiles.forEach((file) => {
|
|
7490
|
-
const lines = testLines[file];
|
|
7491
|
-
testLocHasMatch[file] = true;
|
|
7492
|
-
const spec = project.createSpecification(file, lines, "typescript");
|
|
7493
|
-
this.ensureSpecificationCached(spec);
|
|
7494
|
-
files.push(spec);
|
|
7495
|
-
});
|
|
7496
|
-
}));
|
|
7497
|
-
Object.entries(testLines).forEach(([filepath, loc]) => {
|
|
7498
|
-
if (loc.length !== 0 && !testLocHasMatch[filepath]) {
|
|
7499
|
-
throw new LocationFilterFileNotFoundError(relative(dir, filepath));
|
|
7457
|
+
const resolvedProjectsPromises = await Promise.allSettled(projectPromises);
|
|
7458
|
+
const errors = [];
|
|
7459
|
+
const resolvedProjects = [];
|
|
7460
|
+
for (const result of resolvedProjectsPromises) {
|
|
7461
|
+
if (result.status === "rejected") {
|
|
7462
|
+
if (result.reason instanceof VitestFilteredOutProjectError) {
|
|
7463
|
+
continue;
|
|
7500
7464
|
}
|
|
7501
|
-
|
|
7502
|
-
return files;
|
|
7503
|
-
}
|
|
7504
|
-
clearCache(moduleId) {
|
|
7505
|
-
if (moduleId) {
|
|
7506
|
-
this._cachedSpecs.delete(moduleId);
|
|
7465
|
+
errors.push(result.reason);
|
|
7507
7466
|
} else {
|
|
7508
|
-
|
|
7467
|
+
resolvedProjects.push(result.value);
|
|
7509
7468
|
}
|
|
7510
7469
|
}
|
|
7511
|
-
|
|
7512
|
-
|
|
7470
|
+
if (errors.length) {
|
|
7471
|
+
throw new AggregateError(errors, "Failed to initialize projects. There were errors during projects setup. See below for more details.");
|
|
7513
7472
|
}
|
|
7514
|
-
|
|
7515
|
-
const
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7473
|
+
for (const project of resolvedProjects) {
|
|
7474
|
+
const name = project.name;
|
|
7475
|
+
if (names.has(name)) {
|
|
7476
|
+
const duplicate = resolvedProjects.find((p) => p.name === name && p !== project);
|
|
7477
|
+
const filesError = fileProjects.length ? [
|
|
7478
|
+
"\n\nYour config matched these files:\n",
|
|
7479
|
+
fileProjects.map((p) => ` - ${relative(vitest.config.root, p)}`).join("\n"),
|
|
7480
|
+
"\n\n"
|
|
7481
|
+
].join("") : " ";
|
|
7482
|
+
throw new Error([
|
|
7483
|
+
`Project name "${name}"`,
|
|
7484
|
+
project.vite.config.configFile ? ` from "${relative(vitest.config.root, project.vite.config.configFile)}"` : "",
|
|
7485
|
+
" is not unique.",
|
|
7486
|
+
duplicate?.vite.config.configFile ? ` The project is already defined by "${relative(vitest.config.root, duplicate.vite.config.configFile)}".` : "",
|
|
7487
|
+
filesError,
|
|
7488
|
+
"All projects should have unique names. Make sure your configuration is correct."
|
|
7489
|
+
].join(""));
|
|
7523
7490
|
}
|
|
7524
|
-
|
|
7491
|
+
names.add(name);
|
|
7525
7492
|
}
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
throw new GitNotFoundError();
|
|
7534
|
-
}
|
|
7535
|
-
this.vitest.config.related = Array.from(new Set(related));
|
|
7536
|
-
}
|
|
7537
|
-
const related = this.vitest.config.related;
|
|
7538
|
-
if (!related) {
|
|
7539
|
-
return specs;
|
|
7493
|
+
return resolveBrowserProjects(vitest, names, resolvedProjects);
|
|
7494
|
+
}
|
|
7495
|
+
async function resolveBrowserProjects(vitest, names, resolvedProjects) {
|
|
7496
|
+
const removeProjects = new Set();
|
|
7497
|
+
resolvedProjects.forEach((project) => {
|
|
7498
|
+
if (!project.config.browser.enabled) {
|
|
7499
|
+
return;
|
|
7540
7500
|
}
|
|
7541
|
-
const
|
|
7542
|
-
|
|
7501
|
+
const instances = project.config.browser.instances || [];
|
|
7502
|
+
const browser = project.config.browser.name;
|
|
7503
|
+
if (instances.length === 0 && browser) {
|
|
7504
|
+
instances.push({
|
|
7505
|
+
browser,
|
|
7506
|
+
name: project.name ? `${project.name} (${browser})` : browser
|
|
7507
|
+
});
|
|
7508
|
+
vitest.logger.warn(withLabel("yellow", "Vitest", [
|
|
7509
|
+
`No browser "instances" were defined`,
|
|
7510
|
+
project.name ? ` for the "${project.name}" project. ` : ". ",
|
|
7511
|
+
`Running tests in "${project.config.browser.name}" browser. `,
|
|
7512
|
+
"The \"browser.name\" field is deprecated since Vitest 3. ",
|
|
7513
|
+
"Read more: https://vitest.dev/guide/browser/config#browser-instances"
|
|
7514
|
+
].filter(Boolean).join("")));
|
|
7515
|
+
}
|
|
7516
|
+
const originalName = project.config.name;
|
|
7517
|
+
const filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
|
|
7518
|
+
const newName = instance.name;
|
|
7519
|
+
return vitest.matchesProjectFilter(newName);
|
|
7520
|
+
});
|
|
7521
|
+
if (!filteredInstances.length) {
|
|
7522
|
+
removeProjects.add(project);
|
|
7523
|
+
return;
|
|
7524
|
+
}
|
|
7525
|
+
if (project.config.browser.providerOptions) {
|
|
7526
|
+
vitest.logger.warn(withLabel("yellow", "Vitest", `"providerOptions"${originalName ? ` in "${originalName}" project` : ""} is ignored because it's overridden by the configs. To hide this warning, remove the "providerOptions" property from the browser configuration.`));
|
|
7527
|
+
}
|
|
7528
|
+
filteredInstances.forEach((config, index) => {
|
|
7529
|
+
const browser = config.browser;
|
|
7530
|
+
if (!browser) {
|
|
7531
|
+
const nth = index + 1;
|
|
7532
|
+
const ending = nth === 2 ? "nd" : nth === 3 ? "rd" : "th";
|
|
7533
|
+
throw new Error(`The browser configuration must have a "browser" property. The ${nth}${ending} item in "browser.instances" doesn't have it. Make sure your${originalName ? ` "${originalName}"` : ""} configuration is correct.`);
|
|
7534
|
+
}
|
|
7535
|
+
const name = config.name;
|
|
7536
|
+
if (name == null) {
|
|
7537
|
+
throw new Error(`The browser configuration must have a "name" property. This is a bug in Vitest. Please, open a new issue with reproduction`);
|
|
7538
|
+
}
|
|
7539
|
+
if (names.has(name)) {
|
|
7540
|
+
throw new Error([
|
|
7541
|
+
`Cannot define a nested project for a ${browser} browser. The project name "${name}" was already defined. `,
|
|
7542
|
+
"If you have multiple instances for the same browser, make sure to define a custom \"name\". ",
|
|
7543
|
+
"All projects should have unique names. Make sure your configuration is correct."
|
|
7544
|
+
].join(""));
|
|
7545
|
+
}
|
|
7546
|
+
names.add(name);
|
|
7547
|
+
const clonedConfig = cloneConfig(project, config);
|
|
7548
|
+
clonedConfig.name = name;
|
|
7549
|
+
const clone = TestProject._cloneBrowserProject(project, clonedConfig);
|
|
7550
|
+
resolvedProjects.push(clone);
|
|
7551
|
+
});
|
|
7552
|
+
removeProjects.add(project);
|
|
7553
|
+
});
|
|
7554
|
+
resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
|
|
7555
|
+
const headedBrowserProjects = resolvedProjects.filter((project) => {
|
|
7556
|
+
return project.config.browser.enabled && !project.config.browser.headless;
|
|
7557
|
+
});
|
|
7558
|
+
if (headedBrowserProjects.length > 1) {
|
|
7559
|
+
const message = [`Found multiple projects that run browser tests in headed mode: "${headedBrowserProjects.map((p) => p.name).join("\", \"")}".`, ` Vitest cannot run multiple headed browsers at the same time.`].join("");
|
|
7560
|
+
if (!isTTY) {
|
|
7561
|
+
throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
|
|
7562
|
+
}
|
|
7563
|
+
const prompts = await import('./index.DBIGubLC.js').then(function (n) { return n.i; });
|
|
7564
|
+
const { projectName } = await prompts.default({
|
|
7565
|
+
type: "select",
|
|
7566
|
+
name: "projectName",
|
|
7567
|
+
choices: headedBrowserProjects.map((project) => ({
|
|
7568
|
+
title: project.name,
|
|
7569
|
+
value: project.name
|
|
7570
|
+
})),
|
|
7571
|
+
message: `${message} Select a single project to run or cancel and run tests with "headless: true" option. Note that you can also start tests with --browser=name or --project=name flag.`
|
|
7572
|
+
});
|
|
7573
|
+
if (!projectName) {
|
|
7574
|
+
throw new Error("The test run was aborted.");
|
|
7575
|
+
}
|
|
7576
|
+
return resolvedProjects.filter((project) => project.name === projectName);
|
|
7577
|
+
}
|
|
7578
|
+
return resolvedProjects;
|
|
7579
|
+
}
|
|
7580
|
+
function cloneConfig(project, { browser,...config }) {
|
|
7581
|
+
const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name,...overrideConfig } = config;
|
|
7582
|
+
const currentConfig = project.config.browser;
|
|
7583
|
+
return mergeConfig({
|
|
7584
|
+
...deepClone(project.config),
|
|
7585
|
+
browser: {
|
|
7586
|
+
...project.config.browser,
|
|
7587
|
+
locators: locators ? { testIdAttribute: locators.testIdAttribute ?? currentConfig.locators.testIdAttribute } : project.config.browser.locators,
|
|
7588
|
+
viewport: viewport ?? currentConfig.viewport,
|
|
7589
|
+
testerHtmlPath: testerHtmlPath ?? currentConfig.testerHtmlPath,
|
|
7590
|
+
screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory,
|
|
7591
|
+
screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures,
|
|
7592
|
+
headless: headless ?? currentConfig.headless,
|
|
7593
|
+
name: browser,
|
|
7594
|
+
providerOptions: config,
|
|
7595
|
+
instances: undefined
|
|
7596
|
+
}
|
|
7597
|
+
}, overrideConfig);
|
|
7598
|
+
}
|
|
7599
|
+
async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition) {
|
|
7600
|
+
const projectsOptions = [];
|
|
7601
|
+
const projectsConfigFiles = [];
|
|
7602
|
+
const projectsGlobMatches = [];
|
|
7603
|
+
const nonConfigProjectDirectories = [];
|
|
7604
|
+
for (const definition of projectsDefinition) {
|
|
7605
|
+
if (typeof definition === "string") {
|
|
7606
|
+
const stringOption = definition.replace("<rootDir>", vitest.config.root);
|
|
7607
|
+
if (!isDynamicPattern(stringOption)) {
|
|
7608
|
+
const file = resolve(vitest.config.root, stringOption);
|
|
7609
|
+
if (!existsSync(file)) {
|
|
7610
|
+
const relativeWorkspaceConfigPath = workspaceConfigPath ? relative(vitest.config.root, workspaceConfigPath) : undefined;
|
|
7611
|
+
const note = workspaceConfigPath ? `Workspace config file "${relativeWorkspaceConfigPath}"` : "Projects definition";
|
|
7612
|
+
throw new Error(`${note} references a non-existing file or a directory: ${file}`);
|
|
7613
|
+
}
|
|
7614
|
+
const stats = await promises.stat(file);
|
|
7615
|
+
if (stats.isFile()) {
|
|
7616
|
+
projectsConfigFiles.push(file);
|
|
7617
|
+
} else if (stats.isDirectory()) {
|
|
7618
|
+
const configFile = await resolveDirectoryConfig(file);
|
|
7619
|
+
if (configFile) {
|
|
7620
|
+
projectsConfigFiles.push(configFile);
|
|
7621
|
+
} else {
|
|
7622
|
+
const directory = file[file.length - 1] === "/" ? file : `${file}/`;
|
|
7623
|
+
nonConfigProjectDirectories.push(directory);
|
|
7624
|
+
}
|
|
7625
|
+
} else {
|
|
7626
|
+
throw new TypeError(`Unexpected file type: ${file}`);
|
|
7627
|
+
}
|
|
7628
|
+
} else {
|
|
7629
|
+
projectsGlobMatches.push(stringOption);
|
|
7630
|
+
}
|
|
7631
|
+
} else if (typeof definition === "function") {
|
|
7632
|
+
projectsOptions.push(await definition({
|
|
7633
|
+
command: vitest.vite.config.command,
|
|
7634
|
+
mode: vitest.vite.config.mode,
|
|
7635
|
+
isPreview: false,
|
|
7636
|
+
isSsrBuild: false
|
|
7637
|
+
}));
|
|
7638
|
+
} else {
|
|
7639
|
+
projectsOptions.push(await definition);
|
|
7640
|
+
}
|
|
7641
|
+
}
|
|
7642
|
+
if (projectsGlobMatches.length) {
|
|
7643
|
+
const globOptions = {
|
|
7644
|
+
absolute: true,
|
|
7645
|
+
dot: true,
|
|
7646
|
+
onlyFiles: false,
|
|
7647
|
+
cwd: vitest.config.root,
|
|
7648
|
+
expandDirectories: false,
|
|
7649
|
+
ignore: [
|
|
7650
|
+
"**/node_modules/**",
|
|
7651
|
+
"**/*.timestamp-*",
|
|
7652
|
+
"**/.DS_Store"
|
|
7653
|
+
]
|
|
7654
|
+
};
|
|
7655
|
+
const projectsFs = await glob(projectsGlobMatches, globOptions);
|
|
7656
|
+
await Promise.all(projectsFs.map(async (path) => {
|
|
7657
|
+
if (path.endsWith("/")) {
|
|
7658
|
+
const configFile = await resolveDirectoryConfig(path);
|
|
7659
|
+
if (configFile) {
|
|
7660
|
+
projectsConfigFiles.push(configFile);
|
|
7661
|
+
} else {
|
|
7662
|
+
nonConfigProjectDirectories.push(path);
|
|
7663
|
+
}
|
|
7664
|
+
} else {
|
|
7665
|
+
projectsConfigFiles.push(path);
|
|
7666
|
+
}
|
|
7667
|
+
}));
|
|
7668
|
+
}
|
|
7669
|
+
const projectConfigFiles = Array.from(new Set(projectsConfigFiles));
|
|
7670
|
+
return {
|
|
7671
|
+
projectConfigs: projectsOptions,
|
|
7672
|
+
nonConfigDirectories: nonConfigProjectDirectories,
|
|
7673
|
+
configFiles: projectConfigFiles
|
|
7674
|
+
};
|
|
7675
|
+
}
|
|
7676
|
+
async function resolveDirectoryConfig(directory) {
|
|
7677
|
+
const files = new Set(await promises.readdir(directory));
|
|
7678
|
+
const configFile = configFiles.find((file) => files.has(file));
|
|
7679
|
+
if (configFile) {
|
|
7680
|
+
return resolve(directory, configFile);
|
|
7681
|
+
}
|
|
7682
|
+
return null;
|
|
7683
|
+
}
|
|
7684
|
+
function getDefaultTestProject(vitest) {
|
|
7685
|
+
const filter = vitest.config.project;
|
|
7686
|
+
const project = vitest._ensureRootProject();
|
|
7687
|
+
if (!filter.length) {
|
|
7688
|
+
return project;
|
|
7689
|
+
}
|
|
7690
|
+
const hasProjects = getPotentialProjectNames(project).some((p) => vitest.matchesProjectFilter(p));
|
|
7691
|
+
if (hasProjects) {
|
|
7692
|
+
return project;
|
|
7693
|
+
}
|
|
7694
|
+
return null;
|
|
7695
|
+
}
|
|
7696
|
+
function getPotentialProjectNames(project) {
|
|
7697
|
+
const names = [project.name];
|
|
7698
|
+
if (project.config.browser.instances) {
|
|
7699
|
+
names.push(...project.config.browser.instances.map((i) => i.name));
|
|
7700
|
+
} else if (project.config.browser.name) {
|
|
7701
|
+
names.push(project.config.browser.name);
|
|
7702
|
+
}
|
|
7703
|
+
return names;
|
|
7704
|
+
}
|
|
7705
|
+
|
|
7706
|
+
async function loadCustomReporterModule(path, runner) {
|
|
7707
|
+
let customReporterModule;
|
|
7708
|
+
try {
|
|
7709
|
+
customReporterModule = await runner.executeId(path);
|
|
7710
|
+
} catch (customReporterModuleError) {
|
|
7711
|
+
throw new Error(`Failed to load custom Reporter from ${path}`, { cause: customReporterModuleError });
|
|
7712
|
+
}
|
|
7713
|
+
if (customReporterModule.default === null || customReporterModule.default === undefined) {
|
|
7714
|
+
throw new Error(`Custom reporter loaded from ${path} was not the default export`);
|
|
7715
|
+
}
|
|
7716
|
+
return customReporterModule.default;
|
|
7717
|
+
}
|
|
7718
|
+
function createReporters(reporterReferences, ctx) {
|
|
7719
|
+
const runner = ctx.runner;
|
|
7720
|
+
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
|
7721
|
+
if (Array.isArray(referenceOrInstance)) {
|
|
7722
|
+
const [reporterName, reporterOptions] = referenceOrInstance;
|
|
7723
|
+
if (reporterName === "html") {
|
|
7724
|
+
await ctx.packageInstaller.ensureInstalled("@vitest/ui", runner.root, ctx.version);
|
|
7725
|
+
const CustomReporter = await loadCustomReporterModule("@vitest/ui/reporter", runner);
|
|
7726
|
+
return new CustomReporter(reporterOptions);
|
|
7727
|
+
} else if (reporterName in ReportersMap) {
|
|
7728
|
+
const BuiltinReporter = ReportersMap[reporterName];
|
|
7729
|
+
return new BuiltinReporter(reporterOptions);
|
|
7730
|
+
} else {
|
|
7731
|
+
const CustomReporter = await loadCustomReporterModule(reporterName, runner);
|
|
7732
|
+
return new CustomReporter(reporterOptions);
|
|
7733
|
+
}
|
|
7734
|
+
}
|
|
7735
|
+
return referenceOrInstance;
|
|
7736
|
+
});
|
|
7737
|
+
return Promise.all(promisedReporters);
|
|
7738
|
+
}
|
|
7739
|
+
function createBenchmarkReporters(reporterReferences, runner) {
|
|
7740
|
+
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
|
7741
|
+
if (typeof referenceOrInstance === "string") {
|
|
7742
|
+
if (referenceOrInstance in BenchmarkReportsMap) {
|
|
7743
|
+
const BuiltinReporter = BenchmarkReportsMap[referenceOrInstance];
|
|
7744
|
+
return new BuiltinReporter();
|
|
7745
|
+
} else {
|
|
7746
|
+
const CustomReporter = await loadCustomReporterModule(referenceOrInstance, runner);
|
|
7747
|
+
return new CustomReporter();
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7750
|
+
return referenceOrInstance;
|
|
7751
|
+
});
|
|
7752
|
+
return Promise.all(promisedReporters);
|
|
7753
|
+
}
|
|
7754
|
+
|
|
7755
|
+
function parseFilter(filter) {
|
|
7756
|
+
const colonIndex = filter.lastIndexOf(":");
|
|
7757
|
+
if (colonIndex === -1) {
|
|
7758
|
+
return { filename: filter };
|
|
7759
|
+
}
|
|
7760
|
+
const [parsedFilename, lineNumber] = [filter.substring(0, colonIndex), filter.substring(colonIndex + 1)];
|
|
7761
|
+
if (lineNumber.match(/^\d+$/)) {
|
|
7762
|
+
return {
|
|
7763
|
+
filename: parsedFilename,
|
|
7764
|
+
lineNumber: Number.parseInt(lineNumber)
|
|
7765
|
+
};
|
|
7766
|
+
} else if (lineNumber.match(/^\d+-\d+$/)) {
|
|
7767
|
+
throw new RangeLocationFilterProvidedError(filter);
|
|
7768
|
+
} else {
|
|
7769
|
+
return { filename: filter };
|
|
7770
|
+
}
|
|
7771
|
+
}
|
|
7772
|
+
function groupFilters(filters) {
|
|
7773
|
+
const groupedFilters_ = groupBy(filters, (f) => f.filename);
|
|
7774
|
+
const groupedFilters = Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
|
|
7775
|
+
const [filename, filters] = entry;
|
|
7776
|
+
const testLocations = filters.map((f) => f.lineNumber);
|
|
7777
|
+
return [filename, testLocations.filter((l) => l !== undefined)];
|
|
7778
|
+
}));
|
|
7779
|
+
return groupedFilters;
|
|
7780
|
+
}
|
|
7781
|
+
|
|
7782
|
+
class VitestSpecifications {
|
|
7783
|
+
_cachedSpecs = new Map();
|
|
7784
|
+
constructor(vitest) {
|
|
7785
|
+
this.vitest = vitest;
|
|
7786
|
+
}
|
|
7787
|
+
getModuleSpecifications(moduleId) {
|
|
7788
|
+
const _cached = this.getCachedSpecifications(moduleId);
|
|
7789
|
+
if (_cached) {
|
|
7790
|
+
return _cached;
|
|
7791
|
+
}
|
|
7792
|
+
const specs = [];
|
|
7793
|
+
for (const project of this.vitest.projects) {
|
|
7794
|
+
if (project._isCachedTestFile(moduleId)) {
|
|
7795
|
+
specs.push(project.createSpecification(moduleId));
|
|
7796
|
+
}
|
|
7797
|
+
if (project._isCachedTypecheckFile(moduleId)) {
|
|
7798
|
+
specs.push(project.createSpecification(moduleId, [], "typescript"));
|
|
7799
|
+
}
|
|
7800
|
+
}
|
|
7801
|
+
specs.forEach((spec) => this.ensureSpecificationCached(spec));
|
|
7802
|
+
return specs;
|
|
7803
|
+
}
|
|
7804
|
+
async getRelevantTestSpecifications(filters = []) {
|
|
7805
|
+
return this.filterTestsBySource(await this.globTestSpecifications(filters));
|
|
7806
|
+
}
|
|
7807
|
+
async globTestSpecifications(filters = []) {
|
|
7808
|
+
const files = [];
|
|
7809
|
+
const dir = process.cwd();
|
|
7810
|
+
const parsedFilters = filters.map((f) => parseFilter(f));
|
|
7811
|
+
if (!this.vitest.config.includeTaskLocation && parsedFilters.some((f) => f.lineNumber !== undefined)) {
|
|
7812
|
+
throw new IncludeTaskLocationDisabledError();
|
|
7813
|
+
}
|
|
7814
|
+
const testLines = groupFilters(parsedFilters.map((f) => ({
|
|
7815
|
+
...f,
|
|
7816
|
+
filename: resolve(dir, f.filename)
|
|
7817
|
+
})));
|
|
7818
|
+
const testLocHasMatch = {};
|
|
7819
|
+
await Promise.all(this.vitest.projects.map(async (project) => {
|
|
7820
|
+
const { testFiles, typecheckTestFiles } = await project.globTestFiles(parsedFilters.map((f) => f.filename));
|
|
7821
|
+
testFiles.forEach((file) => {
|
|
7822
|
+
const lines = testLines[file];
|
|
7823
|
+
testLocHasMatch[file] = true;
|
|
7824
|
+
const spec = project.createSpecification(file, lines);
|
|
7825
|
+
this.ensureSpecificationCached(spec);
|
|
7826
|
+
files.push(spec);
|
|
7827
|
+
});
|
|
7828
|
+
typecheckTestFiles.forEach((file) => {
|
|
7829
|
+
const lines = testLines[file];
|
|
7830
|
+
testLocHasMatch[file] = true;
|
|
7831
|
+
const spec = project.createSpecification(file, lines, "typescript");
|
|
7832
|
+
this.ensureSpecificationCached(spec);
|
|
7833
|
+
files.push(spec);
|
|
7834
|
+
});
|
|
7835
|
+
}));
|
|
7836
|
+
Object.entries(testLines).forEach(([filepath, loc]) => {
|
|
7837
|
+
if (loc.length !== 0 && !testLocHasMatch[filepath]) {
|
|
7838
|
+
throw new LocationFilterFileNotFoundError(relative(dir, filepath));
|
|
7839
|
+
}
|
|
7840
|
+
});
|
|
7841
|
+
return files;
|
|
7842
|
+
}
|
|
7843
|
+
clearCache(moduleId) {
|
|
7844
|
+
if (moduleId) {
|
|
7845
|
+
this._cachedSpecs.delete(moduleId);
|
|
7846
|
+
} else {
|
|
7847
|
+
this._cachedSpecs.clear();
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
getCachedSpecifications(moduleId) {
|
|
7851
|
+
return this._cachedSpecs.get(moduleId);
|
|
7852
|
+
}
|
|
7853
|
+
ensureSpecificationCached(spec) {
|
|
7854
|
+
const file = spec.moduleId;
|
|
7855
|
+
const specs = this._cachedSpecs.get(file) || [];
|
|
7856
|
+
const index = specs.findIndex((_s) => _s.project === spec.project && _s.pool === spec.pool);
|
|
7857
|
+
if (index === -1) {
|
|
7858
|
+
specs.push(spec);
|
|
7859
|
+
this._cachedSpecs.set(file, specs);
|
|
7860
|
+
} else {
|
|
7861
|
+
specs.splice(index, 1, spec);
|
|
7862
|
+
}
|
|
7863
|
+
return specs;
|
|
7864
|
+
}
|
|
7865
|
+
async filterTestsBySource(specs) {
|
|
7866
|
+
if (this.vitest.config.changed && !this.vitest.config.related) {
|
|
7867
|
+
const { VitestGit } = await import('./git.DXfdBEfR.js');
|
|
7868
|
+
const vitestGit = new VitestGit(this.vitest.config.root);
|
|
7869
|
+
const related = await vitestGit.findChangedFiles({ changedSince: this.vitest.config.changed });
|
|
7870
|
+
if (!related) {
|
|
7871
|
+
process.exitCode = 1;
|
|
7872
|
+
throw new GitNotFoundError();
|
|
7873
|
+
}
|
|
7874
|
+
this.vitest.config.related = Array.from(new Set(related));
|
|
7875
|
+
}
|
|
7876
|
+
const related = this.vitest.config.related;
|
|
7877
|
+
if (!related) {
|
|
7878
|
+
return specs;
|
|
7879
|
+
}
|
|
7880
|
+
const forceRerunTriggers = this.vitest.config.forceRerunTriggers;
|
|
7881
|
+
if (forceRerunTriggers.length && mm(related, forceRerunTriggers).length) {
|
|
7543
7882
|
return specs;
|
|
7544
7883
|
}
|
|
7545
7884
|
if (!this.vitest.config.watch && !related.length) {
|
|
@@ -7998,720 +8337,433 @@ class StateManager {
|
|
|
7998
8337
|
errorsSet = new Set();
|
|
7999
8338
|
processTimeoutCauses = new Set();
|
|
8000
8339
|
reportedTasksMap = new WeakMap();
|
|
8001
|
-
blobs;
|
|
8002
|
-
catchError(err, type) {
|
|
8003
|
-
if (isAggregateError(err)) {
|
|
8004
|
-
return err.errors.forEach((error) => this.catchError(error, type));
|
|
8005
|
-
}
|
|
8006
|
-
if (err === Object(err)) {
|
|
8007
|
-
err.type = type;
|
|
8008
|
-
} else {
|
|
8009
|
-
err = {
|
|
8010
|
-
type,
|
|
8011
|
-
message: err
|
|
8012
|
-
};
|
|
8013
|
-
}
|
|
8014
|
-
const _err = err;
|
|
8015
|
-
if (_err && typeof _err === "object" && _err.code === "VITEST_PENDING") {
|
|
8016
|
-
const task = this.idMap.get(_err.taskId);
|
|
8017
|
-
if (task) {
|
|
8018
|
-
task.mode = "skip";
|
|
8019
|
-
task.result ??= { state: "skip" };
|
|
8020
|
-
task.result.state = "skip";
|
|
8021
|
-
task.result.note = _err.note;
|
|
8022
|
-
}
|
|
8023
|
-
return;
|
|
8024
|
-
}
|
|
8025
|
-
this.errorsSet.add(err);
|
|
8026
|
-
}
|
|
8027
|
-
clearErrors() {
|
|
8028
|
-
this.errorsSet.clear();
|
|
8029
|
-
}
|
|
8030
|
-
getUnhandledErrors() {
|
|
8031
|
-
return Array.from(this.errorsSet.values());
|
|
8032
|
-
}
|
|
8033
|
-
addProcessTimeoutCause(cause) {
|
|
8034
|
-
this.processTimeoutCauses.add(cause);
|
|
8035
|
-
}
|
|
8036
|
-
getProcessTimeoutCauses() {
|
|
8037
|
-
return Array.from(this.processTimeoutCauses.values());
|
|
8038
|
-
}
|
|
8039
|
-
getPaths() {
|
|
8040
|
-
return Array.from(this.pathsSet);
|
|
8041
|
-
}
|
|
8042
|
-
/**
|
|
8043
|
-
* Return files that were running or collected.
|
|
8044
|
-
*/
|
|
8045
|
-
getFiles(keys) {
|
|
8046
|
-
if (keys) {
|
|
8047
|
-
return keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local);
|
|
8048
|
-
}
|
|
8049
|
-
return Array.from(this.filesMap.values()).flat().filter((file) => !file.local).sort((f1, f2) => {
|
|
8050
|
-
if (f1.meta?.typecheck && f2.meta?.typecheck) {
|
|
8051
|
-
return 0;
|
|
8052
|
-
}
|
|
8053
|
-
if (f1.meta?.typecheck) {
|
|
8054
|
-
return -1;
|
|
8055
|
-
}
|
|
8056
|
-
return 1;
|
|
8057
|
-
});
|
|
8058
|
-
}
|
|
8059
|
-
getTestModules(keys) {
|
|
8060
|
-
return this.getFiles(keys).map((file) => this.getReportedEntity(file));
|
|
8061
|
-
}
|
|
8062
|
-
getFilepaths() {
|
|
8063
|
-
return Array.from(this.filesMap.keys());
|
|
8064
|
-
}
|
|
8065
|
-
getFailedFilepaths() {
|
|
8066
|
-
return this.getFiles().filter((i) => i.result?.state === "fail").map((i) => i.filepath);
|
|
8067
|
-
}
|
|
8068
|
-
collectPaths(paths = []) {
|
|
8069
|
-
paths.forEach((path) => {
|
|
8070
|
-
this.pathsSet.add(path);
|
|
8071
|
-
});
|
|
8072
|
-
}
|
|
8073
|
-
collectFiles(project, files = []) {
|
|
8074
|
-
files.forEach((file) => {
|
|
8075
|
-
const existing = this.filesMap.get(file.filepath) || [];
|
|
8076
|
-
const otherFiles = existing.filter((i) => i.projectName !== file.projectName || i.meta.typecheck !== file.meta.typecheck);
|
|
8077
|
-
const currentFile = existing.find((i) => i.projectName === file.projectName);
|
|
8078
|
-
if (currentFile) {
|
|
8079
|
-
file.logs = currentFile.logs;
|
|
8080
|
-
}
|
|
8081
|
-
otherFiles.push(file);
|
|
8082
|
-
this.filesMap.set(file.filepath, otherFiles);
|
|
8083
|
-
this.updateId(file, project);
|
|
8084
|
-
});
|
|
8085
|
-
}
|
|
8086
|
-
clearFiles(project, paths = []) {
|
|
8087
|
-
paths.forEach((path) => {
|
|
8088
|
-
const files = this.filesMap.get(path);
|
|
8089
|
-
const fileTask = createFileTask(path, project.config.root, project.config.name);
|
|
8090
|
-
fileTask.local = true;
|
|
8091
|
-
TestModule.register(fileTask, project);
|
|
8092
|
-
this.idMap.set(fileTask.id, fileTask);
|
|
8093
|
-
if (!files) {
|
|
8094
|
-
this.filesMap.set(path, [fileTask]);
|
|
8095
|
-
return;
|
|
8096
|
-
}
|
|
8097
|
-
const filtered = files.filter((file) => file.projectName !== project.config.name);
|
|
8098
|
-
if (!filtered.length) {
|
|
8099
|
-
this.filesMap.set(path, [fileTask]);
|
|
8100
|
-
} else {
|
|
8101
|
-
this.filesMap.set(path, [...filtered, fileTask]);
|
|
8102
|
-
}
|
|
8103
|
-
});
|
|
8104
|
-
}
|
|
8105
|
-
updateId(task, project) {
|
|
8106
|
-
if (this.idMap.get(task.id) === task) {
|
|
8107
|
-
return;
|
|
8108
|
-
}
|
|
8109
|
-
if (task.type === "suite" && "filepath" in task) {
|
|
8110
|
-
TestModule.register(task, project);
|
|
8111
|
-
} else if (task.type === "suite") {
|
|
8112
|
-
TestSuite.register(task, project);
|
|
8113
|
-
} else {
|
|
8114
|
-
TestCase.register(task, project);
|
|
8115
|
-
}
|
|
8116
|
-
this.idMap.set(task.id, task);
|
|
8117
|
-
if (task.type === "suite") {
|
|
8118
|
-
task.tasks.forEach((task) => {
|
|
8119
|
-
this.updateId(task, project);
|
|
8120
|
-
});
|
|
8121
|
-
}
|
|
8122
|
-
}
|
|
8123
|
-
getReportedEntity(task) {
|
|
8124
|
-
return this.reportedTasksMap.get(task);
|
|
8125
|
-
}
|
|
8126
|
-
updateTasks(packs) {
|
|
8127
|
-
for (const [id, result, meta] of packs) {
|
|
8128
|
-
const task = this.idMap.get(id);
|
|
8129
|
-
if (task) {
|
|
8130
|
-
task.result = result;
|
|
8131
|
-
task.meta = meta;
|
|
8132
|
-
if (result?.state === "skip") {
|
|
8133
|
-
task.mode = "skip";
|
|
8134
|
-
}
|
|
8135
|
-
}
|
|
8136
|
-
}
|
|
8137
|
-
}
|
|
8138
|
-
updateUserLog(log) {
|
|
8139
|
-
const task = log.taskId && this.idMap.get(log.taskId);
|
|
8140
|
-
if (task) {
|
|
8141
|
-
if (!task.logs) {
|
|
8142
|
-
task.logs = [];
|
|
8143
|
-
}
|
|
8144
|
-
task.logs.push(log);
|
|
8145
|
-
}
|
|
8146
|
-
}
|
|
8147
|
-
getCountOfFailedTests() {
|
|
8148
|
-
return Array.from(this.idMap.values()).filter((t) => t.result?.state === "fail").length;
|
|
8149
|
-
}
|
|
8150
|
-
cancelFiles(files, project) {
|
|
8151
|
-
this.collectFiles(project, files.map((filepath) => createFileTask(filepath, project.config.root, project.config.name)));
|
|
8152
|
-
}
|
|
8153
|
-
}
|
|
8154
|
-
|
|
8155
|
-
class TestRun {
|
|
8156
|
-
constructor(vitest) {
|
|
8157
|
-
this.vitest = vitest;
|
|
8158
|
-
}
|
|
8159
|
-
async start(specifications) {
|
|
8160
|
-
const filepaths = specifications.map((spec) => spec.moduleId);
|
|
8161
|
-
this.vitest.state.collectPaths(filepaths);
|
|
8162
|
-
await this.vitest.report("onPathsCollected", Array.from(new Set(filepaths)));
|
|
8163
|
-
await this.vitest.report("onSpecsCollected", specifications.map((spec) => spec.toJSON()));
|
|
8164
|
-
await this.vitest.report("onTestRunStart", [...specifications]);
|
|
8165
|
-
}
|
|
8166
|
-
async enqueued(project, file) {
|
|
8167
|
-
this.vitest.state.collectFiles(project, [file]);
|
|
8168
|
-
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8169
|
-
await this.vitest.report("onTestModuleQueued", testModule);
|
|
8170
|
-
}
|
|
8171
|
-
async collected(project, files) {
|
|
8172
|
-
this.vitest.state.collectFiles(project, files);
|
|
8173
|
-
await Promise.all([this.vitest.report("onCollected", files), ...files.map((file) => {
|
|
8174
|
-
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8175
|
-
return this.vitest.report("onTestModuleCollected", testModule);
|
|
8176
|
-
})]);
|
|
8177
|
-
}
|
|
8178
|
-
async log(log) {
|
|
8179
|
-
this.vitest.state.updateUserLog(log);
|
|
8180
|
-
await this.vitest.report("onUserConsoleLog", log);
|
|
8181
|
-
}
|
|
8182
|
-
async updated(update, events) {
|
|
8183
|
-
this.vitest.state.updateTasks(update);
|
|
8184
|
-
await this.vitest.report("onTaskUpdate", update);
|
|
8185
|
-
for (const [id, event] of events) {
|
|
8186
|
-
await this.reportEvent(id, event).catch((error) => {
|
|
8187
|
-
this.vitest.state.catchError(serializeError(error), "Unhandled Reporter Error");
|
|
8188
|
-
});
|
|
8189
|
-
}
|
|
8190
|
-
}
|
|
8191
|
-
async end(specifications, errors, coverage) {
|
|
8192
|
-
const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null);
|
|
8193
|
-
const files = modules.map((m) => m.task);
|
|
8194
|
-
const state = this.vitest.isCancelling ? "interrupted" : process.exitCode ? "failed" : "passed";
|
|
8195
|
-
try {
|
|
8196
|
-
await Promise.all([this.vitest.report("onTestRunEnd", modules, [...errors], state), this.vitest.report("onFinished", files, errors, coverage)]);
|
|
8197
|
-
} finally {
|
|
8198
|
-
if (coverage) {
|
|
8199
|
-
await this.vitest.report("onCoverage", coverage);
|
|
8200
|
-
}
|
|
8201
|
-
}
|
|
8202
|
-
}
|
|
8203
|
-
async reportEvent(id, event) {
|
|
8204
|
-
const task = this.vitest.state.idMap.get(id);
|
|
8205
|
-
const entity = task && this.vitest.state.getReportedEntity(task);
|
|
8206
|
-
assert$1(task && entity, `Entity must be found for task ${task?.name || id}`);
|
|
8207
|
-
if (event === "suite-prepare" && entity.type === "suite") {
|
|
8208
|
-
return await this.vitest.report("onTestSuiteReady", entity);
|
|
8209
|
-
}
|
|
8210
|
-
if (event === "suite-prepare" && entity.type === "module") {
|
|
8211
|
-
return await this.vitest.report("onTestModuleStart", entity);
|
|
8212
|
-
}
|
|
8213
|
-
if (event === "suite-finished") {
|
|
8214
|
-
assert$1(entity.type === "suite" || entity.type === "module", "Entity type must be suite or module");
|
|
8215
|
-
if (entity.state() === "skipped") {
|
|
8216
|
-
await this.reportChildren(entity.children);
|
|
8217
|
-
}
|
|
8218
|
-
if (entity.type === "module") {
|
|
8219
|
-
await this.vitest.report("onTestModuleEnd", entity);
|
|
8220
|
-
} else {
|
|
8221
|
-
await this.vitest.report("onTestSuiteResult", entity);
|
|
8222
|
-
}
|
|
8223
|
-
return;
|
|
8224
|
-
}
|
|
8225
|
-
if (event === "test-prepare" && entity.type === "test") {
|
|
8226
|
-
return await this.vitest.report("onTestCaseReady", entity);
|
|
8227
|
-
}
|
|
8228
|
-
if (event === "test-finished" && entity.type === "test") {
|
|
8229
|
-
return await this.vitest.report("onTestCaseResult", entity);
|
|
8230
|
-
}
|
|
8231
|
-
if (event.startsWith("before-hook") || event.startsWith("after-hook")) {
|
|
8232
|
-
const isBefore = event.startsWith("before-hook");
|
|
8233
|
-
const hook = entity.type === "test" ? {
|
|
8234
|
-
name: isBefore ? "beforeEach" : "afterEach",
|
|
8235
|
-
entity
|
|
8236
|
-
} : {
|
|
8237
|
-
name: isBefore ? "beforeAll" : "afterAll",
|
|
8238
|
-
entity
|
|
8340
|
+
blobs;
|
|
8341
|
+
catchError(err, type) {
|
|
8342
|
+
if (isAggregateError(err)) {
|
|
8343
|
+
return err.errors.forEach((error) => this.catchError(error, type));
|
|
8344
|
+
}
|
|
8345
|
+
if (err === Object(err)) {
|
|
8346
|
+
err.type = type;
|
|
8347
|
+
} else {
|
|
8348
|
+
err = {
|
|
8349
|
+
type,
|
|
8350
|
+
message: err
|
|
8239
8351
|
};
|
|
8240
|
-
if (event.endsWith("-start")) {
|
|
8241
|
-
await this.vitest.report("onHookStart", hook);
|
|
8242
|
-
} else {
|
|
8243
|
-
await this.vitest.report("onHookEnd", hook);
|
|
8244
|
-
}
|
|
8245
8352
|
}
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
if (
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
await this.reportChildren(child.children);
|
|
8255
|
-
await this.vitest.report("onTestSuiteResult", child);
|
|
8353
|
+
const _err = err;
|
|
8354
|
+
if (_err && typeof _err === "object" && _err.code === "VITEST_PENDING") {
|
|
8355
|
+
const task = this.idMap.get(_err.taskId);
|
|
8356
|
+
if (task) {
|
|
8357
|
+
task.mode = "skip";
|
|
8358
|
+
task.result ??= { state: "skip" };
|
|
8359
|
+
task.result.state = "skip";
|
|
8360
|
+
task.result.note = _err.note;
|
|
8256
8361
|
}
|
|
8362
|
+
return;
|
|
8257
8363
|
}
|
|
8364
|
+
this.errorsSet.add(err);
|
|
8258
8365
|
}
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
class VitestWatcher {
|
|
8262
|
-
/**
|
|
8263
|
-
* Modules that will be invalidated on the next run.
|
|
8264
|
-
*/
|
|
8265
|
-
invalidates = new Set();
|
|
8266
|
-
/**
|
|
8267
|
-
* Test files that have changed and need to be rerun.
|
|
8268
|
-
*/
|
|
8269
|
-
changedTests = new Set();
|
|
8270
|
-
_onRerun = [];
|
|
8271
|
-
constructor(vitest) {
|
|
8272
|
-
this.vitest = vitest;
|
|
8366
|
+
clearErrors() {
|
|
8367
|
+
this.errorsSet.clear();
|
|
8273
8368
|
}
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
* The callback can receive several files in case the changed file is imported by several test files.
|
|
8277
|
-
* Several invocations of this method will add multiple handlers.
|
|
8278
|
-
* @internal
|
|
8279
|
-
*/
|
|
8280
|
-
onWatcherRerun(cb) {
|
|
8281
|
-
this._onRerun.push(cb);
|
|
8282
|
-
return this;
|
|
8369
|
+
getUnhandledErrors() {
|
|
8370
|
+
return Array.from(this.errorsSet.values());
|
|
8283
8371
|
}
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
const watcher = this.vitest.vite.watcher;
|
|
8287
|
-
if (this.vitest.config.forceRerunTriggers.length) {
|
|
8288
|
-
watcher.add(this.vitest.config.forceRerunTriggers);
|
|
8289
|
-
}
|
|
8290
|
-
watcher.on("change", this.onChange);
|
|
8291
|
-
watcher.on("unlink", this.onUnlink);
|
|
8292
|
-
watcher.on("add", this.onAdd);
|
|
8293
|
-
this.unregisterWatcher = () => {
|
|
8294
|
-
watcher.off("change", this.onChange);
|
|
8295
|
-
watcher.off("unlink", this.onUnlink);
|
|
8296
|
-
watcher.off("add", this.onAdd);
|
|
8297
|
-
this.unregisterWatcher = noop;
|
|
8298
|
-
};
|
|
8299
|
-
return this;
|
|
8372
|
+
addProcessTimeoutCause(cause) {
|
|
8373
|
+
this.processTimeoutCauses.add(cause);
|
|
8300
8374
|
}
|
|
8301
|
-
|
|
8302
|
-
this.
|
|
8375
|
+
getProcessTimeoutCauses() {
|
|
8376
|
+
return Array.from(this.processTimeoutCauses.values());
|
|
8377
|
+
}
|
|
8378
|
+
getPaths() {
|
|
8379
|
+
return Array.from(this.pathsSet);
|
|
8303
8380
|
}
|
|
8304
|
-
onChange = (id) => {
|
|
8305
|
-
id = slash(id);
|
|
8306
|
-
this.vitest.logger.clearHighlightCache(id);
|
|
8307
|
-
this.vitest.invalidateFile(id);
|
|
8308
|
-
const needsRerun = this.handleFileChanged(id);
|
|
8309
|
-
if (needsRerun) {
|
|
8310
|
-
this.scheduleRerun(id);
|
|
8311
|
-
}
|
|
8312
|
-
};
|
|
8313
|
-
onUnlink = (id) => {
|
|
8314
|
-
id = slash(id);
|
|
8315
|
-
this.vitest.logger.clearHighlightCache(id);
|
|
8316
|
-
this.invalidates.add(id);
|
|
8317
|
-
if (this.vitest.state.filesMap.has(id)) {
|
|
8318
|
-
this.vitest.projects.forEach((project) => project._removeCachedTestFile(id));
|
|
8319
|
-
this.vitest.state.filesMap.delete(id);
|
|
8320
|
-
this.vitest.cache.results.removeFromCache(id);
|
|
8321
|
-
this.vitest.cache.stats.removeStats(id);
|
|
8322
|
-
this.changedTests.delete(id);
|
|
8323
|
-
this.vitest.report("onTestRemoved", id);
|
|
8324
|
-
}
|
|
8325
|
-
};
|
|
8326
|
-
onAdd = (id) => {
|
|
8327
|
-
id = slash(id);
|
|
8328
|
-
this.vitest.invalidateFile(id);
|
|
8329
|
-
let fileContent;
|
|
8330
|
-
const matchingProjects = [];
|
|
8331
|
-
this.vitest.projects.forEach((project) => {
|
|
8332
|
-
if (project.matchesTestGlob(id, () => fileContent ??= readFileSync(id, "utf-8"))) {
|
|
8333
|
-
matchingProjects.push(project);
|
|
8334
|
-
}
|
|
8335
|
-
});
|
|
8336
|
-
if (matchingProjects.length > 0) {
|
|
8337
|
-
this.changedTests.add(id);
|
|
8338
|
-
this.scheduleRerun(id);
|
|
8339
|
-
} else {
|
|
8340
|
-
const needsRerun = this.handleFileChanged(id);
|
|
8341
|
-
if (needsRerun) {
|
|
8342
|
-
this.scheduleRerun(id);
|
|
8343
|
-
}
|
|
8344
|
-
}
|
|
8345
|
-
};
|
|
8346
8381
|
/**
|
|
8347
|
-
*
|
|
8382
|
+
* Return files that were running or collected.
|
|
8348
8383
|
*/
|
|
8349
|
-
|
|
8350
|
-
if (
|
|
8351
|
-
return
|
|
8352
|
-
}
|
|
8353
|
-
if (mm.isMatch(filepath, this.vitest.config.forceRerunTriggers)) {
|
|
8354
|
-
this.vitest.state.getFilepaths().forEach((file) => this.changedTests.add(file));
|
|
8355
|
-
return true;
|
|
8356
|
-
}
|
|
8357
|
-
const projects = this.vitest.projects.filter((project) => {
|
|
8358
|
-
const moduleGraph = project.browser?.vite.moduleGraph || project.vite.moduleGraph;
|
|
8359
|
-
return moduleGraph.getModulesByFile(filepath)?.size;
|
|
8360
|
-
});
|
|
8361
|
-
if (!projects.length) {
|
|
8362
|
-
if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath))) {
|
|
8363
|
-
this.changedTests.add(filepath);
|
|
8364
|
-
return true;
|
|
8365
|
-
}
|
|
8366
|
-
return false;
|
|
8384
|
+
getFiles(keys) {
|
|
8385
|
+
if (keys) {
|
|
8386
|
+
return keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local);
|
|
8367
8387
|
}
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
if (!mods || !mods.size) {
|
|
8372
|
-
continue;
|
|
8373
|
-
}
|
|
8374
|
-
this.invalidates.add(filepath);
|
|
8375
|
-
if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
|
|
8376
|
-
this.changedTests.add(filepath);
|
|
8377
|
-
files.push(filepath);
|
|
8378
|
-
continue;
|
|
8388
|
+
return Array.from(this.filesMap.values()).flat().filter((file) => !file.local).sort((f1, f2) => {
|
|
8389
|
+
if (f1.meta?.typecheck && f2.meta?.typecheck) {
|
|
8390
|
+
return 0;
|
|
8379
8391
|
}
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
mod.importers.forEach((i) => {
|
|
8383
|
-
if (!i.file) {
|
|
8384
|
-
return;
|
|
8385
|
-
}
|
|
8386
|
-
const needsRerun = this.handleFileChanged(i.file);
|
|
8387
|
-
if (needsRerun) {
|
|
8388
|
-
rerun = true;
|
|
8389
|
-
}
|
|
8390
|
-
});
|
|
8392
|
+
if (f1.meta?.typecheck) {
|
|
8393
|
+
return -1;
|
|
8391
8394
|
}
|
|
8392
|
-
|
|
8393
|
-
|
|
8395
|
+
return 1;
|
|
8396
|
+
});
|
|
8397
|
+
}
|
|
8398
|
+
getTestModules(keys) {
|
|
8399
|
+
return this.getFiles(keys).map((file) => this.getReportedEntity(file));
|
|
8400
|
+
}
|
|
8401
|
+
getFilepaths() {
|
|
8402
|
+
return Array.from(this.filesMap.keys());
|
|
8403
|
+
}
|
|
8404
|
+
getFailedFilepaths() {
|
|
8405
|
+
return this.getFiles().filter((i) => i.result?.state === "fail").map((i) => i.filepath);
|
|
8406
|
+
}
|
|
8407
|
+
collectPaths(paths = []) {
|
|
8408
|
+
paths.forEach((path) => {
|
|
8409
|
+
this.pathsSet.add(path);
|
|
8410
|
+
});
|
|
8411
|
+
}
|
|
8412
|
+
collectFiles(project, files = []) {
|
|
8413
|
+
files.forEach((file) => {
|
|
8414
|
+
const existing = this.filesMap.get(file.filepath) || [];
|
|
8415
|
+
const otherFiles = existing.filter((i) => i.projectName !== file.projectName || i.meta.typecheck !== file.meta.typecheck);
|
|
8416
|
+
const currentFile = existing.find((i) => i.projectName === file.projectName);
|
|
8417
|
+
if (currentFile) {
|
|
8418
|
+
file.logs = currentFile.logs;
|
|
8394
8419
|
}
|
|
8395
|
-
|
|
8396
|
-
|
|
8420
|
+
otherFiles.push(file);
|
|
8421
|
+
this.filesMap.set(file.filepath, otherFiles);
|
|
8422
|
+
this.updateId(file, project);
|
|
8423
|
+
});
|
|
8397
8424
|
}
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
"update",
|
|
8409
|
-
"globals",
|
|
8410
|
-
"expandSnapshotDiff",
|
|
8411
|
-
"disableConsoleIntercept",
|
|
8412
|
-
"retry",
|
|
8413
|
-
"testNamePattern",
|
|
8414
|
-
"passWithNoTests",
|
|
8415
|
-
"bail",
|
|
8416
|
-
"isolate",
|
|
8417
|
-
"printConsoleTrace",
|
|
8418
|
-
"inspect",
|
|
8419
|
-
"inspectBrk",
|
|
8420
|
-
"fileParallelism"
|
|
8421
|
-
];
|
|
8422
|
-
const cliOverrides = overridesOptions.reduce((acc, name) => {
|
|
8423
|
-
if (name in cliOptions) {
|
|
8424
|
-
acc[name] = cliOptions[name];
|
|
8425
|
-
}
|
|
8426
|
-
return acc;
|
|
8427
|
-
}, {});
|
|
8428
|
-
const projectPromises = [];
|
|
8429
|
-
const fileProjects = [...configFiles, ...nonConfigDirectories];
|
|
8430
|
-
const concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
|
|
8431
|
-
projectConfigs.forEach((options, index) => {
|
|
8432
|
-
const configRoot = workspaceConfigPath ? dirname(workspaceConfigPath) : vitest.config.root;
|
|
8433
|
-
const configFile = typeof options.extends === "string" ? resolve(configRoot, options.extends) : options.extends === true ? vitest.vite.config.configFile || false : false;
|
|
8434
|
-
const root = options.root ? resolve(configRoot, options.root) : vitest.config.root;
|
|
8435
|
-
projectPromises.push(concurrent(() => initializeProject(index, vitest, {
|
|
8436
|
-
...options,
|
|
8437
|
-
root,
|
|
8438
|
-
configFile,
|
|
8439
|
-
test: {
|
|
8440
|
-
...options.test,
|
|
8441
|
-
...cliOverrides
|
|
8425
|
+
clearFiles(project, paths = []) {
|
|
8426
|
+
paths.forEach((path) => {
|
|
8427
|
+
const files = this.filesMap.get(path);
|
|
8428
|
+
const fileTask = createFileTask(path, project.config.root, project.config.name);
|
|
8429
|
+
fileTask.local = true;
|
|
8430
|
+
TestModule.register(fileTask, project);
|
|
8431
|
+
this.idMap.set(fileTask.id, fileTask);
|
|
8432
|
+
if (!files) {
|
|
8433
|
+
this.filesMap.set(path, [fileTask]);
|
|
8434
|
+
return;
|
|
8442
8435
|
}
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
if (project) {
|
|
8449
|
-
projectPromises.push(Promise.resolve(project));
|
|
8436
|
+
const filtered = files.filter((file) => file.projectName !== project.config.name);
|
|
8437
|
+
if (!filtered.length) {
|
|
8438
|
+
this.filesMap.set(path, [fileTask]);
|
|
8439
|
+
} else {
|
|
8440
|
+
this.filesMap.set(path, [...filtered, fileTask]);
|
|
8450
8441
|
}
|
|
8451
|
-
|
|
8442
|
+
});
|
|
8443
|
+
}
|
|
8444
|
+
updateId(task, project) {
|
|
8445
|
+
if (this.idMap.get(task.id) === task) {
|
|
8446
|
+
return;
|
|
8447
|
+
}
|
|
8448
|
+
if (task.type === "suite" && "filepath" in task) {
|
|
8449
|
+
TestModule.register(task, project);
|
|
8450
|
+
} else if (task.type === "suite") {
|
|
8451
|
+
TestSuite.register(task, project);
|
|
8452
|
+
} else {
|
|
8453
|
+
TestCase.register(task, project);
|
|
8454
|
+
}
|
|
8455
|
+
this.idMap.set(task.id, task);
|
|
8456
|
+
if (task.type === "suite") {
|
|
8457
|
+
task.tasks.forEach((task) => {
|
|
8458
|
+
this.updateId(task, project);
|
|
8459
|
+
});
|
|
8452
8460
|
}
|
|
8453
|
-
const configFile = path.endsWith("/") ? false : path;
|
|
8454
|
-
const root = path.endsWith("/") ? path : dirname(path);
|
|
8455
|
-
projectPromises.push(concurrent(() => initializeProject(path, vitest, {
|
|
8456
|
-
root,
|
|
8457
|
-
configFile,
|
|
8458
|
-
test: cliOverrides
|
|
8459
|
-
})));
|
|
8460
8461
|
}
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
"No projects were found. Make sure your configuration is correct. ",
|
|
8464
|
-
vitest.config.project.length ? `The filter matched no projects: ${vitest.config.project.join(", ")}. ` : "",
|
|
8465
|
-
`The workspace: ${JSON.stringify(workspaceDefinition, null, 4)}.`
|
|
8466
|
-
].join(""));
|
|
8462
|
+
getReportedEntity(task) {
|
|
8463
|
+
return this.reportedTasksMap.get(task);
|
|
8467
8464
|
}
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8465
|
+
updateTasks(packs) {
|
|
8466
|
+
for (const [id, result, meta] of packs) {
|
|
8467
|
+
const task = this.idMap.get(id);
|
|
8468
|
+
if (task) {
|
|
8469
|
+
task.result = result;
|
|
8470
|
+
task.meta = meta;
|
|
8471
|
+
if (result?.state === "skip") {
|
|
8472
|
+
task.mode = "skip";
|
|
8473
|
+
}
|
|
8475
8474
|
}
|
|
8476
|
-
errors.push(result.reason);
|
|
8477
|
-
} else {
|
|
8478
|
-
resolvedProjects.push(result.value);
|
|
8479
8475
|
}
|
|
8480
8476
|
}
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
const filesError = fileProjects.length ? [
|
|
8489
|
-
"\n\nYour config matched these files:\n",
|
|
8490
|
-
fileProjects.map((p) => ` - ${relative(vitest.config.root, p)}`).join("\n"),
|
|
8491
|
-
"\n\n"
|
|
8492
|
-
].join("") : [" "];
|
|
8493
|
-
throw new Error([
|
|
8494
|
-
`Project name "${name}"`,
|
|
8495
|
-
project.vite.config.configFile ? ` from "${relative(vitest.config.root, project.vite.config.configFile)}"` : "",
|
|
8496
|
-
" is not unique.",
|
|
8497
|
-
duplicate?.vite.config.configFile ? ` The project is already defined by "${relative(vitest.config.root, duplicate.vite.config.configFile)}".` : "",
|
|
8498
|
-
filesError,
|
|
8499
|
-
"All projects in a workspace should have unique names. Make sure your configuration is correct."
|
|
8500
|
-
].join(""));
|
|
8477
|
+
updateUserLog(log) {
|
|
8478
|
+
const task = log.taskId && this.idMap.get(log.taskId);
|
|
8479
|
+
if (task) {
|
|
8480
|
+
if (!task.logs) {
|
|
8481
|
+
task.logs = [];
|
|
8482
|
+
}
|
|
8483
|
+
task.logs.push(log);
|
|
8501
8484
|
}
|
|
8502
|
-
names.add(name);
|
|
8503
8485
|
}
|
|
8504
|
-
|
|
8486
|
+
getCountOfFailedTests() {
|
|
8487
|
+
return Array.from(this.idMap.values()).filter((t) => t.result?.state === "fail").length;
|
|
8488
|
+
}
|
|
8489
|
+
cancelFiles(files, project) {
|
|
8490
|
+
this.collectFiles(project, files.map((filepath) => createFileTask(filepath, project.config.root, project.config.name)));
|
|
8491
|
+
}
|
|
8505
8492
|
}
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
const
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8493
|
+
|
|
8494
|
+
class TestRun {
|
|
8495
|
+
constructor(vitest) {
|
|
8496
|
+
this.vitest = vitest;
|
|
8497
|
+
}
|
|
8498
|
+
async start(specifications) {
|
|
8499
|
+
const filepaths = specifications.map((spec) => spec.moduleId);
|
|
8500
|
+
this.vitest.state.collectPaths(filepaths);
|
|
8501
|
+
await this.vitest.report("onPathsCollected", Array.from(new Set(filepaths)));
|
|
8502
|
+
await this.vitest.report("onSpecsCollected", specifications.map((spec) => spec.toJSON()));
|
|
8503
|
+
await this.vitest.report("onTestRunStart", [...specifications]);
|
|
8504
|
+
}
|
|
8505
|
+
async enqueued(project, file) {
|
|
8506
|
+
this.vitest.state.collectFiles(project, [file]);
|
|
8507
|
+
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8508
|
+
await this.vitest.report("onTestModuleQueued", testModule);
|
|
8509
|
+
}
|
|
8510
|
+
async collected(project, files) {
|
|
8511
|
+
this.vitest.state.collectFiles(project, files);
|
|
8512
|
+
await Promise.all([this.vitest.report("onCollected", files), ...files.map((file) => {
|
|
8513
|
+
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8514
|
+
return this.vitest.report("onTestModuleCollected", testModule);
|
|
8515
|
+
})]);
|
|
8516
|
+
}
|
|
8517
|
+
async log(log) {
|
|
8518
|
+
this.vitest.state.updateUserLog(log);
|
|
8519
|
+
await this.vitest.report("onUserConsoleLog", log);
|
|
8520
|
+
}
|
|
8521
|
+
async updated(update, events) {
|
|
8522
|
+
this.vitest.state.updateTasks(update);
|
|
8523
|
+
await this.vitest.report("onTaskUpdate", update);
|
|
8524
|
+
for (const [id, event] of events) {
|
|
8525
|
+
await this.reportEvent(id, event).catch((error) => {
|
|
8526
|
+
this.vitest.state.catchError(serializeError(error), "Unhandled Reporter Error");
|
|
8518
8527
|
});
|
|
8519
|
-
console.warn(withLabel("yellow", "Vitest", [
|
|
8520
|
-
`No browser "instances" were defined`,
|
|
8521
|
-
project.name ? ` for the "${project.name}" project. ` : ". ",
|
|
8522
|
-
`Running tests in "${project.config.browser.name}" browser. `,
|
|
8523
|
-
"The \"browser.name\" field is deprecated since Vitest 3. ",
|
|
8524
|
-
"Read more: https://vitest.dev/guide/browser/config#browser-instances"
|
|
8525
|
-
].filter(Boolean).join("")));
|
|
8526
8528
|
}
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8529
|
+
}
|
|
8530
|
+
async end(specifications, errors, coverage) {
|
|
8531
|
+
const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null);
|
|
8532
|
+
const files = modules.map((m) => m.task);
|
|
8533
|
+
const state = this.vitest.isCancelling ? "interrupted" : process.exitCode ? "failed" : "passed";
|
|
8534
|
+
try {
|
|
8535
|
+
await Promise.all([this.vitest.report("onTestRunEnd", modules, [...errors], state), this.vitest.report("onFinished", files, errors, coverage)]);
|
|
8536
|
+
} finally {
|
|
8537
|
+
if (coverage) {
|
|
8538
|
+
await this.vitest.report("onCoverage", coverage);
|
|
8539
|
+
}
|
|
8535
8540
|
}
|
|
8536
|
-
|
|
8537
|
-
|
|
8541
|
+
}
|
|
8542
|
+
async reportEvent(id, event) {
|
|
8543
|
+
const task = this.vitest.state.idMap.get(id);
|
|
8544
|
+
const entity = task && this.vitest.state.getReportedEntity(task);
|
|
8545
|
+
assert$1(task && entity, `Entity must be found for task ${task?.name || id}`);
|
|
8546
|
+
if (event === "suite-prepare" && entity.type === "suite") {
|
|
8547
|
+
return await this.vitest.report("onTestSuiteReady", entity);
|
|
8538
8548
|
}
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
const name = config.name;
|
|
8547
|
-
if (name == null) {
|
|
8548
|
-
throw new Error(`The browser configuration must have a "name" property. This is a bug in Vitest. Please, open a new issue with reproduction`);
|
|
8549
|
+
if (event === "suite-prepare" && entity.type === "module") {
|
|
8550
|
+
return await this.vitest.report("onTestModuleStart", entity);
|
|
8551
|
+
}
|
|
8552
|
+
if (event === "suite-finished") {
|
|
8553
|
+
assert$1(entity.type === "suite" || entity.type === "module", "Entity type must be suite or module");
|
|
8554
|
+
if (entity.state() === "skipped") {
|
|
8555
|
+
await this.reportChildren(entity.children);
|
|
8549
8556
|
}
|
|
8550
|
-
if (
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
"All projects in a workspace should have unique names. Make sure your configuration is correct."
|
|
8555
|
-
].join(""));
|
|
8557
|
+
if (entity.type === "module") {
|
|
8558
|
+
await this.vitest.report("onTestModuleEnd", entity);
|
|
8559
|
+
} else {
|
|
8560
|
+
await this.vitest.report("onTestSuiteResult", entity);
|
|
8556
8561
|
}
|
|
8557
|
-
|
|
8558
|
-
const clonedConfig = cloneConfig(project, config);
|
|
8559
|
-
clonedConfig.name = name;
|
|
8560
|
-
const clone = TestProject._cloneBrowserProject(project, clonedConfig);
|
|
8561
|
-
resolvedProjects.push(clone);
|
|
8562
|
-
});
|
|
8563
|
-
removeProjects.add(project);
|
|
8564
|
-
});
|
|
8565
|
-
resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
|
|
8566
|
-
const headedBrowserProjects = resolvedProjects.filter((project) => {
|
|
8567
|
-
return project.config.browser.enabled && !project.config.browser.headless;
|
|
8568
|
-
});
|
|
8569
|
-
if (headedBrowserProjects.length > 1) {
|
|
8570
|
-
const message = [`Found multiple projects that run browser tests in headed mode: "${headedBrowserProjects.map((p) => p.name).join("\", \"")}".`, ` Vitest cannot run multiple headed browsers at the same time.`].join("");
|
|
8571
|
-
if (!isTTY) {
|
|
8572
|
-
throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
|
|
8573
|
-
}
|
|
8574
|
-
const prompts = await import('./index.DBIGubLC.js').then(function (n) { return n.i; });
|
|
8575
|
-
const { projectName } = await prompts.default({
|
|
8576
|
-
type: "select",
|
|
8577
|
-
name: "projectName",
|
|
8578
|
-
choices: headedBrowserProjects.map((project) => ({
|
|
8579
|
-
title: project.name,
|
|
8580
|
-
value: project.name
|
|
8581
|
-
})),
|
|
8582
|
-
message: `${message} Select a single project to run or cancel and run tests with "headless: true" option. Note that you can also start tests with --browser=name or --project=name flag.`
|
|
8583
|
-
});
|
|
8584
|
-
if (!projectName) {
|
|
8585
|
-
throw new Error("The test run was aborted.");
|
|
8586
|
-
}
|
|
8587
|
-
return resolvedProjects.filter((project) => project.name === projectName);
|
|
8588
|
-
}
|
|
8589
|
-
return resolvedProjects;
|
|
8590
|
-
}
|
|
8591
|
-
function cloneConfig(project, { browser,...config }) {
|
|
8592
|
-
const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name,...overrideConfig } = config;
|
|
8593
|
-
const currentConfig = project.config.browser;
|
|
8594
|
-
return mergeConfig({
|
|
8595
|
-
...deepClone(project.config),
|
|
8596
|
-
browser: {
|
|
8597
|
-
...project.config.browser,
|
|
8598
|
-
locators: locators ? { testIdAttribute: locators.testIdAttribute ?? currentConfig.locators.testIdAttribute } : project.config.browser.locators,
|
|
8599
|
-
viewport: viewport ?? currentConfig.viewport,
|
|
8600
|
-
testerHtmlPath: testerHtmlPath ?? currentConfig.testerHtmlPath,
|
|
8601
|
-
screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory,
|
|
8602
|
-
screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures,
|
|
8603
|
-
headless: headless ?? currentConfig.headless,
|
|
8604
|
-
name: browser,
|
|
8605
|
-
providerOptions: config,
|
|
8606
|
-
instances: undefined
|
|
8562
|
+
return;
|
|
8607
8563
|
}
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
const stats = await promises.stat(file);
|
|
8626
|
-
if (stats.isFile()) {
|
|
8627
|
-
workspaceConfigFiles.push(file);
|
|
8628
|
-
} else if (stats.isDirectory()) {
|
|
8629
|
-
const configFile = await resolveDirectoryConfig(file);
|
|
8630
|
-
if (configFile) {
|
|
8631
|
-
workspaceConfigFiles.push(configFile);
|
|
8632
|
-
} else {
|
|
8633
|
-
const directory = file[file.length - 1] === "/" ? file : `${file}/`;
|
|
8634
|
-
nonConfigProjectDirectories.push(directory);
|
|
8635
|
-
}
|
|
8636
|
-
} else {
|
|
8637
|
-
throw new TypeError(`Unexpected file type: ${file}`);
|
|
8638
|
-
}
|
|
8564
|
+
if (event === "test-prepare" && entity.type === "test") {
|
|
8565
|
+
return await this.vitest.report("onTestCaseReady", entity);
|
|
8566
|
+
}
|
|
8567
|
+
if (event === "test-finished" && entity.type === "test") {
|
|
8568
|
+
return await this.vitest.report("onTestCaseResult", entity);
|
|
8569
|
+
}
|
|
8570
|
+
if (event.startsWith("before-hook") || event.startsWith("after-hook")) {
|
|
8571
|
+
const isBefore = event.startsWith("before-hook");
|
|
8572
|
+
const hook = entity.type === "test" ? {
|
|
8573
|
+
name: isBefore ? "beforeEach" : "afterEach",
|
|
8574
|
+
entity
|
|
8575
|
+
} : {
|
|
8576
|
+
name: isBefore ? "beforeAll" : "afterAll",
|
|
8577
|
+
entity
|
|
8578
|
+
};
|
|
8579
|
+
if (event.endsWith("-start")) {
|
|
8580
|
+
await this.vitest.report("onHookStart", hook);
|
|
8639
8581
|
} else {
|
|
8640
|
-
|
|
8582
|
+
await this.vitest.report("onHookEnd", hook);
|
|
8641
8583
|
}
|
|
8642
|
-
} else if (typeof definition === "function") {
|
|
8643
|
-
projectsOptions.push(await definition({
|
|
8644
|
-
command: vitest.vite.config.command,
|
|
8645
|
-
mode: vitest.vite.config.mode,
|
|
8646
|
-
isPreview: false,
|
|
8647
|
-
isSsrBuild: false
|
|
8648
|
-
}));
|
|
8649
|
-
} else {
|
|
8650
|
-
projectsOptions.push(await definition);
|
|
8651
8584
|
}
|
|
8652
8585
|
}
|
|
8653
|
-
|
|
8654
|
-
const
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
|
|
8658
|
-
cwd: vitest.config.root,
|
|
8659
|
-
expandDirectories: false,
|
|
8660
|
-
ignore: [
|
|
8661
|
-
"**/node_modules/**",
|
|
8662
|
-
"**/*.timestamp-*",
|
|
8663
|
-
"**/.DS_Store"
|
|
8664
|
-
]
|
|
8665
|
-
};
|
|
8666
|
-
const workspacesFs = await glob(workspaceGlobMatches, globOptions);
|
|
8667
|
-
await Promise.all(workspacesFs.map(async (path) => {
|
|
8668
|
-
if (path.endsWith("/")) {
|
|
8669
|
-
const configFile = await resolveDirectoryConfig(path);
|
|
8670
|
-
if (configFile) {
|
|
8671
|
-
workspaceConfigFiles.push(configFile);
|
|
8672
|
-
} else {
|
|
8673
|
-
nonConfigProjectDirectories.push(path);
|
|
8674
|
-
}
|
|
8586
|
+
async reportChildren(children) {
|
|
8587
|
+
for (const child of children) {
|
|
8588
|
+
if (child.type === "test") {
|
|
8589
|
+
await this.vitest.report("onTestCaseReady", child);
|
|
8590
|
+
await this.vitest.report("onTestCaseResult", child);
|
|
8675
8591
|
} else {
|
|
8676
|
-
|
|
8592
|
+
await this.vitest.report("onTestSuiteReady", child);
|
|
8593
|
+
await this.reportChildren(child.children);
|
|
8594
|
+
await this.vitest.report("onTestSuiteResult", child);
|
|
8677
8595
|
}
|
|
8678
|
-
}
|
|
8596
|
+
}
|
|
8679
8597
|
}
|
|
8680
|
-
const projectConfigFiles = Array.from(new Set(workspaceConfigFiles));
|
|
8681
|
-
return {
|
|
8682
|
-
projectConfigs: projectsOptions,
|
|
8683
|
-
nonConfigDirectories: nonConfigProjectDirectories,
|
|
8684
|
-
configFiles: projectConfigFiles
|
|
8685
|
-
};
|
|
8686
8598
|
}
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8599
|
+
|
|
8600
|
+
class VitestWatcher {
|
|
8601
|
+
/**
|
|
8602
|
+
* Modules that will be invalidated on the next run.
|
|
8603
|
+
*/
|
|
8604
|
+
invalidates = new Set();
|
|
8605
|
+
/**
|
|
8606
|
+
* Test files that have changed and need to be rerun.
|
|
8607
|
+
*/
|
|
8608
|
+
changedTests = new Set();
|
|
8609
|
+
_onRerun = [];
|
|
8610
|
+
constructor(vitest) {
|
|
8611
|
+
this.vitest = vitest;
|
|
8692
8612
|
}
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8613
|
+
/**
|
|
8614
|
+
* Register a handler that will be called when test files need to be rerun.
|
|
8615
|
+
* The callback can receive several files in case the changed file is imported by several test files.
|
|
8616
|
+
* Several invocations of this method will add multiple handlers.
|
|
8617
|
+
* @internal
|
|
8618
|
+
*/
|
|
8619
|
+
onWatcherRerun(cb) {
|
|
8620
|
+
this._onRerun.push(cb);
|
|
8621
|
+
return this;
|
|
8700
8622
|
}
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8623
|
+
unregisterWatcher = noop;
|
|
8624
|
+
registerWatcher() {
|
|
8625
|
+
const watcher = this.vitest.vite.watcher;
|
|
8626
|
+
if (this.vitest.config.forceRerunTriggers.length) {
|
|
8627
|
+
watcher.add(this.vitest.config.forceRerunTriggers);
|
|
8628
|
+
}
|
|
8629
|
+
watcher.on("change", this.onChange);
|
|
8630
|
+
watcher.on("unlink", this.onUnlink);
|
|
8631
|
+
watcher.on("add", this.onAdd);
|
|
8632
|
+
this.unregisterWatcher = () => {
|
|
8633
|
+
watcher.off("change", this.onChange);
|
|
8634
|
+
watcher.off("unlink", this.onUnlink);
|
|
8635
|
+
watcher.off("add", this.onAdd);
|
|
8636
|
+
this.unregisterWatcher = noop;
|
|
8637
|
+
};
|
|
8638
|
+
return this;
|
|
8704
8639
|
}
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8640
|
+
scheduleRerun(file) {
|
|
8641
|
+
this._onRerun.forEach((cb) => cb(file));
|
|
8642
|
+
}
|
|
8643
|
+
getTestFilesFromWatcherTrigger(id) {
|
|
8644
|
+
if (!this.vitest.config.watchTriggerPatterns) {
|
|
8645
|
+
return false;
|
|
8646
|
+
}
|
|
8647
|
+
let triggered = false;
|
|
8648
|
+
this.vitest.config.watchTriggerPatterns.forEach((definition) => {
|
|
8649
|
+
const exec = definition.pattern.exec(id);
|
|
8650
|
+
if (exec) {
|
|
8651
|
+
const files = definition.testsToRun(id, exec);
|
|
8652
|
+
if (Array.isArray(files)) {
|
|
8653
|
+
triggered = true;
|
|
8654
|
+
files.forEach((file) => this.changedTests.add(resolve(this.vitest.config.root, file)));
|
|
8655
|
+
} else if (typeof files === "string") {
|
|
8656
|
+
triggered = true;
|
|
8657
|
+
this.changedTests.add(resolve(this.vitest.config.root, files));
|
|
8658
|
+
}
|
|
8659
|
+
}
|
|
8660
|
+
});
|
|
8661
|
+
return triggered;
|
|
8662
|
+
}
|
|
8663
|
+
onChange = (id) => {
|
|
8664
|
+
id = slash(id);
|
|
8665
|
+
this.vitest.logger.clearHighlightCache(id);
|
|
8666
|
+
this.vitest.invalidateFile(id);
|
|
8667
|
+
const testFiles = this.getTestFilesFromWatcherTrigger(id);
|
|
8668
|
+
if (testFiles) {
|
|
8669
|
+
this.scheduleRerun(id);
|
|
8670
|
+
} else {
|
|
8671
|
+
const needsRerun = this.handleFileChanged(id);
|
|
8672
|
+
if (needsRerun) {
|
|
8673
|
+
this.scheduleRerun(id);
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
};
|
|
8677
|
+
onUnlink = (id) => {
|
|
8678
|
+
id = slash(id);
|
|
8679
|
+
this.vitest.logger.clearHighlightCache(id);
|
|
8680
|
+
this.invalidates.add(id);
|
|
8681
|
+
if (this.vitest.state.filesMap.has(id)) {
|
|
8682
|
+
this.vitest.projects.forEach((project) => project._removeCachedTestFile(id));
|
|
8683
|
+
this.vitest.state.filesMap.delete(id);
|
|
8684
|
+
this.vitest.cache.results.removeFromCache(id);
|
|
8685
|
+
this.vitest.cache.stats.removeStats(id);
|
|
8686
|
+
this.changedTests.delete(id);
|
|
8687
|
+
this.vitest.report("onTestRemoved", id);
|
|
8688
|
+
}
|
|
8689
|
+
};
|
|
8690
|
+
onAdd = (id) => {
|
|
8691
|
+
id = slash(id);
|
|
8692
|
+
this.vitest.invalidateFile(id);
|
|
8693
|
+
const testFiles = this.getTestFilesFromWatcherTrigger(id);
|
|
8694
|
+
if (testFiles) {
|
|
8695
|
+
this.scheduleRerun(id);
|
|
8696
|
+
return;
|
|
8697
|
+
}
|
|
8698
|
+
let fileContent;
|
|
8699
|
+
const matchingProjects = [];
|
|
8700
|
+
this.vitest.projects.forEach((project) => {
|
|
8701
|
+
if (project.matchesTestGlob(id, () => fileContent ??= readFileSync(id, "utf-8"))) {
|
|
8702
|
+
matchingProjects.push(project);
|
|
8703
|
+
}
|
|
8704
|
+
});
|
|
8705
|
+
if (matchingProjects.length > 0) {
|
|
8706
|
+
this.changedTests.add(id);
|
|
8707
|
+
this.scheduleRerun(id);
|
|
8708
|
+
} else {
|
|
8709
|
+
const needsRerun = this.handleFileChanged(id);
|
|
8710
|
+
if (needsRerun) {
|
|
8711
|
+
this.scheduleRerun(id);
|
|
8712
|
+
}
|
|
8713
|
+
}
|
|
8714
|
+
};
|
|
8715
|
+
/**
|
|
8716
|
+
* @returns A value indicating whether rerun is needed (changedTests was mutated)
|
|
8717
|
+
*/
|
|
8718
|
+
handleFileChanged(filepath) {
|
|
8719
|
+
if (this.changedTests.has(filepath) || this.invalidates.has(filepath)) {
|
|
8720
|
+
return false;
|
|
8721
|
+
}
|
|
8722
|
+
if (mm.isMatch(filepath, this.vitest.config.forceRerunTriggers)) {
|
|
8723
|
+
this.vitest.state.getFilepaths().forEach((file) => this.changedTests.add(file));
|
|
8724
|
+
return true;
|
|
8725
|
+
}
|
|
8726
|
+
const projects = this.vitest.projects.filter((project) => {
|
|
8727
|
+
const moduleGraph = project.browser?.vite.moduleGraph || project.vite.moduleGraph;
|
|
8728
|
+
return moduleGraph.getModulesByFile(filepath)?.size;
|
|
8729
|
+
});
|
|
8730
|
+
if (!projects.length) {
|
|
8731
|
+
if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath))) {
|
|
8732
|
+
this.changedTests.add(filepath);
|
|
8733
|
+
return true;
|
|
8734
|
+
}
|
|
8735
|
+
return false;
|
|
8736
|
+
}
|
|
8737
|
+
const files = [];
|
|
8738
|
+
for (const project of projects) {
|
|
8739
|
+
const mods = project.browser?.vite.moduleGraph.getModulesByFile(filepath) || project.vite.moduleGraph.getModulesByFile(filepath);
|
|
8740
|
+
if (!mods || !mods.size) {
|
|
8741
|
+
continue;
|
|
8742
|
+
}
|
|
8743
|
+
this.invalidates.add(filepath);
|
|
8744
|
+
if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
|
|
8745
|
+
this.changedTests.add(filepath);
|
|
8746
|
+
files.push(filepath);
|
|
8747
|
+
continue;
|
|
8748
|
+
}
|
|
8749
|
+
let rerun = false;
|
|
8750
|
+
for (const mod of mods) {
|
|
8751
|
+
mod.importers.forEach((i) => {
|
|
8752
|
+
if (!i.file) {
|
|
8753
|
+
return;
|
|
8754
|
+
}
|
|
8755
|
+
const needsRerun = this.handleFileChanged(i.file);
|
|
8756
|
+
if (needsRerun) {
|
|
8757
|
+
rerun = true;
|
|
8758
|
+
}
|
|
8759
|
+
});
|
|
8760
|
+
}
|
|
8761
|
+
if (rerun) {
|
|
8762
|
+
files.push(filepath);
|
|
8763
|
+
}
|
|
8764
|
+
}
|
|
8765
|
+
return !!files.length;
|
|
8713
8766
|
}
|
|
8714
|
-
return names;
|
|
8715
8767
|
}
|
|
8716
8768
|
|
|
8717
8769
|
const WATCHER_DEBOUNCE = 100;
|
|
@@ -8904,7 +8956,7 @@ class Vitest {
|
|
|
8904
8956
|
try {
|
|
8905
8957
|
await this.cache.results.readFromCache();
|
|
8906
8958
|
} catch {}
|
|
8907
|
-
const projects = await this.
|
|
8959
|
+
const projects = await this.resolveProjects(cliOptions);
|
|
8908
8960
|
this.resolvedProjects = projects;
|
|
8909
8961
|
this.projects = projects;
|
|
8910
8962
|
await Promise.all(projects.flatMap((project) => {
|
|
@@ -8945,9 +8997,9 @@ class Vitest {
|
|
|
8945
8997
|
*/
|
|
8946
8998
|
injectTestProject = async (config) => {
|
|
8947
8999
|
const currentNames = new Set(this.projects.map((p) => p.name));
|
|
8948
|
-
const
|
|
8949
|
-
this.projects.push(...
|
|
8950
|
-
return
|
|
9000
|
+
const projects = await resolveProjects(this, this._options, undefined, Array.isArray(config) ? config : [config], currentNames);
|
|
9001
|
+
this.projects.push(...projects);
|
|
9002
|
+
return projects;
|
|
8951
9003
|
};
|
|
8952
9004
|
/**
|
|
8953
9005
|
* Provide a value to the test context. This value will be available to all tests with `inject`.
|
|
@@ -9018,10 +9070,17 @@ class Vitest {
|
|
|
9018
9070
|
}
|
|
9019
9071
|
return join(configDir, workspaceConfigName);
|
|
9020
9072
|
}
|
|
9021
|
-
async
|
|
9073
|
+
async resolveProjects(cliOptions) {
|
|
9022
9074
|
const names = new Set();
|
|
9075
|
+
if (this.config.projects) {
|
|
9076
|
+
if (typeof this.config.workspace !== "undefined") {
|
|
9077
|
+
this.logger.warn("Both `config.projects` and `config.workspace` are defined. Ignoring the `workspace` option.");
|
|
9078
|
+
}
|
|
9079
|
+
return resolveProjects(this, cliOptions, undefined, this.config.projects, names);
|
|
9080
|
+
}
|
|
9023
9081
|
if (Array.isArray(this.config.workspace)) {
|
|
9024
|
-
|
|
9082
|
+
this.logger.deprecate("The `workspace` option is deprecated and will be removed in the next major. To hide this warning, rename `workspace` option to `projects`.");
|
|
9083
|
+
return resolveProjects(this, cliOptions, undefined, this.config.workspace, names);
|
|
9025
9084
|
}
|
|
9026
9085
|
const workspaceConfigPath = await this.resolveWorkspaceConfigPath();
|
|
9027
9086
|
this._workspaceConfigPath = workspaceConfigPath;
|
|
@@ -9030,13 +9089,15 @@ class Vitest {
|
|
|
9030
9089
|
if (!project) {
|
|
9031
9090
|
return [];
|
|
9032
9091
|
}
|
|
9033
|
-
return
|
|
9092
|
+
return resolveBrowserProjects(this, new Set([project.name]), [project]);
|
|
9034
9093
|
}
|
|
9094
|
+
const configFile = this.vite.config.configFile ? resolve(this.vite.config.root, this.vite.config.configFile) : "the root config file";
|
|
9095
|
+
this.logger.deprecate(`The workspace file is deprecated and will be removed in the next major. Please, use the \`projects\` field in ${configFile} instead.`);
|
|
9035
9096
|
const workspaceModule = await this.import(workspaceConfigPath);
|
|
9036
9097
|
if (!workspaceModule.default || !Array.isArray(workspaceModule.default)) {
|
|
9037
9098
|
throw new TypeError(`Workspace config file "${workspaceConfigPath}" must export a default array of project paths.`);
|
|
9038
9099
|
}
|
|
9039
|
-
return
|
|
9100
|
+
return resolveProjects(this, cliOptions, workspaceConfigPath, workspaceModule.default, names);
|
|
9040
9101
|
}
|
|
9041
9102
|
/**
|
|
9042
9103
|
* Glob test files in every project and create a TestSpecification for each file and pool.
|