vitest 3.1.2 → 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.k5EeHg0m.js → base.SfTiRNZf.js} +2 -2
- package/dist/chunks/{cac.C5_4mAsf.js → cac.TfX2-DVH.js} +9 -7
- package/dist/chunks/{cli-api.z029PxYZ.js → cli-api.2970Nj9J.js} +937 -852
- package/dist/chunks/{coverage.BUdIvXbr.js → coverage.z0LVMxgb.js} +12 -11
- package/dist/chunks/{environment.d.Dmw5ulng.d.ts → environment.d.D8YDy2v5.d.ts} +2 -1
- package/dist/chunks/{execute.CwmnH2oH.js → execute.BpmIjFTD.js} +22 -12
- 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._vwY_KdO.js → index.CUacZlWG.js} +42 -21
- package/dist/chunks/{index.Cu2UlluP.js → index.DbWBPwtH.js} +2 -2
- package/dist/chunks/{reporters.d.79o4mouw.d.ts → reporters.d.DGm4k1Wx.d.ts} +59 -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.BVr6Nvdj.d.ts → vite.d.DjP_ALCZ.d.ts} +1 -1
- package/dist/chunks/{vm.BmHENIuV.js → vm.CuLHT1BG.js} +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/execute.js +1 -1
- 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 +3 -3
- package/dist/workers/forks.js +2 -2
- package/dist/workers/runVmTests.js +5 -5
- package/dist/workers/threads.js +2 -2
- package/dist/workers/vmForks.js +2 -2
- package/dist/workers/vmThreads.js +2 -2
- package/dist/workers.d.ts +3 -3
- package/dist/workers.js +3 -3
- package/package.json +11 -11
|
@@ -5,15 +5,15 @@ import p, { resolve as resolve$1 } from 'node:path';
|
|
|
5
5
|
import { noop, isPrimitive, createDefer, highlight, toArray, deepMerge, nanoid, slash, deepClone, notNullish } from '@vitest/utils';
|
|
6
6
|
import { f as findUp, p as prompt } from './index.DBIGubLC.js';
|
|
7
7
|
import * as vite from 'vite';
|
|
8
|
-
import { searchForWorkspaceRoot, version,
|
|
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);
|
|
@@ -5252,6 +5258,7 @@ var setup$1 = /*#__PURE__*/Object.freeze({
|
|
|
5252
5258
|
|
|
5253
5259
|
class BrowserSessions {
|
|
5254
5260
|
sessions = new Map();
|
|
5261
|
+
sessionIds = new Set();
|
|
5255
5262
|
getSession(sessionId) {
|
|
5256
5263
|
return this.sessions.get(sessionId);
|
|
5257
5264
|
}
|
|
@@ -5408,6 +5415,9 @@ class Logger {
|
|
|
5408
5415
|
printError(err, options = {}) {
|
|
5409
5416
|
printError(err, this.ctx, this, options);
|
|
5410
5417
|
}
|
|
5418
|
+
deprecate(message) {
|
|
5419
|
+
this.log(c.bold(c.bgYellow(" DEPRECATED ")), c.yellow(message));
|
|
5420
|
+
}
|
|
5411
5421
|
clearHighlightCache(filename) {
|
|
5412
5422
|
if (filename) {
|
|
5413
5423
|
this._highlights.delete(filename);
|
|
@@ -5448,7 +5458,7 @@ class Logger {
|
|
|
5448
5458
|
const config = project.config;
|
|
5449
5459
|
const printConfig = !project.isRootProject() && project.name;
|
|
5450
5460
|
if (printConfig) {
|
|
5451
|
-
this.console.error(`\n${formatProjectName(project
|
|
5461
|
+
this.console.error(`\n${formatProjectName(project)}\n`);
|
|
5452
5462
|
}
|
|
5453
5463
|
if (config.include) {
|
|
5454
5464
|
this.console.error(c.dim("include: ") + c.yellow(config.include.join(comma)));
|
|
@@ -5500,7 +5510,7 @@ class Logger {
|
|
|
5500
5510
|
if (!origin) {
|
|
5501
5511
|
return;
|
|
5502
5512
|
}
|
|
5503
|
-
const output = project.isRootProject() ? "" : formatProjectName(project
|
|
5513
|
+
const output = project.isRootProject() ? "" : formatProjectName(project);
|
|
5504
5514
|
const provider = project.browser.provider.name;
|
|
5505
5515
|
const providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
|
|
5506
5516
|
this.log(c.dim(`${output}Browser runner started${providerString} ${c.dim("at")} ${c.blue(new URL("/", origin))}\n`));
|
|
@@ -6520,7 +6530,8 @@ function resolveOptimizerConfig(_testOptions, viteOptions, testConfig, viteCache
|
|
|
6520
6530
|
const runtime = currentInclude.filter((n) => n.endsWith("jsx-dev-runtime") || n.endsWith("jsx-runtime"));
|
|
6521
6531
|
exclude.push(...runtime);
|
|
6522
6532
|
const include = (testOptions.include || viteOptions?.include || []).filter((n) => !exclude.includes(n));
|
|
6523
|
-
|
|
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);
|
|
6524
6535
|
newConfig.optimizeDeps = {
|
|
6525
6536
|
...viteOptions,
|
|
6526
6537
|
...testOptions,
|
|
@@ -6687,7 +6698,10 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
6687
6698
|
const defines = deleteDefineConfig(viteConfig);
|
|
6688
6699
|
const testConfig = viteConfig.test || {};
|
|
6689
6700
|
const root = testConfig.root || viteConfig.root || options.root;
|
|
6690
|
-
let name = testConfig.name
|
|
6701
|
+
let { label: name, color } = typeof testConfig.name === "string" ? { label: testConfig.name } : {
|
|
6702
|
+
label: "",
|
|
6703
|
+
...testConfig.name
|
|
6704
|
+
};
|
|
6691
6705
|
if (!name) {
|
|
6692
6706
|
if (typeof options.workspacePath === "string") {
|
|
6693
6707
|
const dir = options.workspacePath.endsWith("/") ? options.workspacePath.slice(0, -1) : dirname(options.workspacePath);
|
|
@@ -6702,26 +6716,6 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
6702
6716
|
name = options.workspacePath.toString();
|
|
6703
6717
|
}
|
|
6704
6718
|
}
|
|
6705
|
-
const workspaceNames = [name];
|
|
6706
|
-
if (viteConfig.test?.browser?.enabled) {
|
|
6707
|
-
if (viteConfig.test.browser.name) {
|
|
6708
|
-
const browser = viteConfig.test.browser.name;
|
|
6709
|
-
workspaceNames.push(name ? `${name} (${browser})` : browser);
|
|
6710
|
-
}
|
|
6711
|
-
viteConfig.test.browser.instances?.forEach((instance) => {
|
|
6712
|
-
instance.name ??= name ? `${name} (${instance.browser})` : instance.browser;
|
|
6713
|
-
workspaceNames.push(instance.name);
|
|
6714
|
-
});
|
|
6715
|
-
}
|
|
6716
|
-
const filters = project.vitest.config.project;
|
|
6717
|
-
if (filters.length) {
|
|
6718
|
-
const hasProject = workspaceNames.some((name) => {
|
|
6719
|
-
return project.vitest.matchesProjectFilter(name);
|
|
6720
|
-
});
|
|
6721
|
-
if (!hasProject) {
|
|
6722
|
-
throw new VitestFilteredOutProjectError();
|
|
6723
|
-
}
|
|
6724
|
-
}
|
|
6725
6719
|
const resolveOptions = getDefaultResolveOptions();
|
|
6726
6720
|
const config = {
|
|
6727
6721
|
root,
|
|
@@ -6745,9 +6739,35 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
6745
6739
|
fs: { allow: resolveFsAllow(project.vitest.config.root, project.vitest.vite.config.configFile) }
|
|
6746
6740
|
},
|
|
6747
6741
|
environments: { ssr: { resolve: resolveOptions } },
|
|
6748
|
-
test: { name
|
|
6742
|
+
test: { name: {
|
|
6743
|
+
label: name,
|
|
6744
|
+
color
|
|
6745
|
+
} }
|
|
6749
6746
|
};
|
|
6747
|
+
if (project.vitest._options.browser && viteConfig.test?.browser) {
|
|
6748
|
+
viteConfig.test.browser = mergeConfig(viteConfig.test.browser, project.vitest._options.browser);
|
|
6749
|
+
}
|
|
6750
6750
|
config.test.defines = defines;
|
|
6751
|
+
const workspaceNames = [name];
|
|
6752
|
+
if (viteConfig.test?.browser?.enabled) {
|
|
6753
|
+
if (viteConfig.test.browser.name && !viteConfig.test.browser.instances?.length) {
|
|
6754
|
+
const browser = viteConfig.test.browser.name;
|
|
6755
|
+
workspaceNames.push(name ? `${name} (${browser})` : browser);
|
|
6756
|
+
}
|
|
6757
|
+
viteConfig.test.browser.instances?.forEach((instance) => {
|
|
6758
|
+
instance.name ??= name ? `${name} (${instance.browser})` : instance.browser;
|
|
6759
|
+
workspaceNames.push(instance.name);
|
|
6760
|
+
});
|
|
6761
|
+
}
|
|
6762
|
+
const filters = project.vitest.config.project;
|
|
6763
|
+
if (filters.length) {
|
|
6764
|
+
const hasProject = workspaceNames.some((name) => {
|
|
6765
|
+
return project.vitest.matchesProjectFilter(name);
|
|
6766
|
+
});
|
|
6767
|
+
if (!hasProject) {
|
|
6768
|
+
throw new VitestFilteredOutProjectError();
|
|
6769
|
+
}
|
|
6770
|
+
}
|
|
6751
6771
|
const classNameStrategy = typeof testConfig.css !== "boolean" && testConfig.css?.modules?.classNameStrategy || "stable";
|
|
6752
6772
|
if (classNameStrategy !== "scoped") {
|
|
6753
6773
|
config.css ??= {};
|
|
@@ -6976,6 +6996,12 @@ class TestProject {
|
|
|
6976
6996
|
return this.config.name || "";
|
|
6977
6997
|
}
|
|
6978
6998
|
/**
|
|
6999
|
+
* The color used when reporting tasks of this project.
|
|
7000
|
+
*/
|
|
7001
|
+
get color() {
|
|
7002
|
+
return this.config.color;
|
|
7003
|
+
}
|
|
7004
|
+
/**
|
|
6979
7005
|
* Serialized project configuration. This is the config that tests receive.
|
|
6980
7006
|
*/
|
|
6981
7007
|
get serializedConfig() {
|
|
@@ -7360,161 +7386,478 @@ async function initializeProject(workspacePath, ctx, options) {
|
|
|
7360
7386
|
return project;
|
|
7361
7387
|
}
|
|
7362
7388
|
|
|
7363
|
-
async function
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
const CustomReporter = await loadCustomReporterModule(reporterName, runner);
|
|
7389
|
-
return new CustomReporter(reporterOptions);
|
|
7390
|
-
}
|
|
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];
|
|
7391
7414
|
}
|
|
7392
|
-
return
|
|
7393
|
-
});
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
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
|
|
7405
7431
|
}
|
|
7406
|
-
}
|
|
7407
|
-
return referenceOrInstance;
|
|
7432
|
+
})));
|
|
7408
7433
|
});
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
if (colonIndex === -1) {
|
|
7415
|
-
return { filename: filter };
|
|
7416
|
-
}
|
|
7417
|
-
const [parsedFilename, lineNumber] = [filter.substring(0, colonIndex), filter.substring(colonIndex + 1)];
|
|
7418
|
-
if (lineNumber.match(/^\d+$/)) {
|
|
7419
|
-
return {
|
|
7420
|
-
filename: parsedFilename,
|
|
7421
|
-
lineNumber: Number.parseInt(lineNumber)
|
|
7422
|
-
};
|
|
7423
|
-
} else if (lineNumber.match(/^\d+-\d+$/)) {
|
|
7424
|
-
throw new RangeLocationFilterProvidedError(filter);
|
|
7425
|
-
} else {
|
|
7426
|
-
return { filename: filter };
|
|
7427
|
-
}
|
|
7428
|
-
}
|
|
7429
|
-
function groupFilters(filters) {
|
|
7430
|
-
const groupedFilters_ = groupBy(filters, (f) => f.filename);
|
|
7431
|
-
const groupedFilters = Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
|
|
7432
|
-
const [filename, filters] = entry;
|
|
7433
|
-
const testLocations = filters.map((f) => f.lineNumber);
|
|
7434
|
-
return [filename, testLocations.filter((l) => l !== undefined)];
|
|
7435
|
-
}));
|
|
7436
|
-
return groupedFilters;
|
|
7437
|
-
}
|
|
7438
|
-
|
|
7439
|
-
class VitestSpecifications {
|
|
7440
|
-
_cachedSpecs = new Map();
|
|
7441
|
-
constructor(vitest) {
|
|
7442
|
-
this.vitest = vitest;
|
|
7443
|
-
}
|
|
7444
|
-
getModuleSpecifications(moduleId) {
|
|
7445
|
-
const _cached = this.getCachedSpecifications(moduleId);
|
|
7446
|
-
if (_cached) {
|
|
7447
|
-
return _cached;
|
|
7448
|
-
}
|
|
7449
|
-
const specs = [];
|
|
7450
|
-
for (const project of this.vitest.projects) {
|
|
7451
|
-
if (project._isCachedTestFile(moduleId)) {
|
|
7452
|
-
specs.push(project.createSpecification(moduleId));
|
|
7453
|
-
}
|
|
7454
|
-
if (project._isCachedTypecheckFile(moduleId)) {
|
|
7455
|
-
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));
|
|
7456
7439
|
}
|
|
7440
|
+
continue;
|
|
7457
7441
|
}
|
|
7458
|
-
|
|
7459
|
-
|
|
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
|
+
})));
|
|
7460
7449
|
}
|
|
7461
|
-
|
|
7462
|
-
|
|
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(""));
|
|
7463
7456
|
}
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
if (
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
const testLines = groupFilters(parsedFilters.map((f) => ({
|
|
7472
|
-
...f,
|
|
7473
|
-
filename: resolve(dir, f.filename)
|
|
7474
|
-
})));
|
|
7475
|
-
const testLocHasMatch = {};
|
|
7476
|
-
await Promise.all(this.vitest.projects.map(async (project) => {
|
|
7477
|
-
const { testFiles, typecheckTestFiles } = await project.globTestFiles(parsedFilters.map((f) => f.filename));
|
|
7478
|
-
testFiles.forEach((file) => {
|
|
7479
|
-
const lines = testLines[file];
|
|
7480
|
-
testLocHasMatch[file] = true;
|
|
7481
|
-
const spec = project.createSpecification(file, lines);
|
|
7482
|
-
this.ensureSpecificationCached(spec);
|
|
7483
|
-
files.push(spec);
|
|
7484
|
-
});
|
|
7485
|
-
typecheckTestFiles.forEach((file) => {
|
|
7486
|
-
const lines = testLines[file];
|
|
7487
|
-
testLocHasMatch[file] = true;
|
|
7488
|
-
const spec = project.createSpecification(file, lines, "typescript");
|
|
7489
|
-
this.ensureSpecificationCached(spec);
|
|
7490
|
-
files.push(spec);
|
|
7491
|
-
});
|
|
7492
|
-
}));
|
|
7493
|
-
Object.entries(testLines).forEach(([filepath, loc]) => {
|
|
7494
|
-
if (loc.length !== 0 && !testLocHasMatch[filepath]) {
|
|
7495
|
-
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;
|
|
7496
7464
|
}
|
|
7497
|
-
|
|
7498
|
-
return files;
|
|
7499
|
-
}
|
|
7500
|
-
clearCache(moduleId) {
|
|
7501
|
-
if (moduleId) {
|
|
7502
|
-
this._cachedSpecs.delete(moduleId);
|
|
7465
|
+
errors.push(result.reason);
|
|
7503
7466
|
} else {
|
|
7504
|
-
|
|
7467
|
+
resolvedProjects.push(result.value);
|
|
7505
7468
|
}
|
|
7506
7469
|
}
|
|
7507
|
-
|
|
7508
|
-
|
|
7470
|
+
if (errors.length) {
|
|
7471
|
+
throw new AggregateError(errors, "Failed to initialize projects. There were errors during projects setup. See below for more details.");
|
|
7509
7472
|
}
|
|
7510
|
-
|
|
7511
|
-
const
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
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(""));
|
|
7490
|
+
}
|
|
7491
|
+
names.add(name);
|
|
7492
|
+
}
|
|
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;
|
|
7500
|
+
}
|
|
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 {
|
|
7518
7861
|
specs.splice(index, 1, spec);
|
|
7519
7862
|
}
|
|
7520
7863
|
return specs;
|
|
@@ -7992,721 +8335,435 @@ class StateManager {
|
|
|
7992
8335
|
idMap = new Map();
|
|
7993
8336
|
taskFileMap = new WeakMap();
|
|
7994
8337
|
errorsSet = new Set();
|
|
7995
|
-
processTimeoutCauses = new Set();
|
|
7996
|
-
reportedTasksMap = new WeakMap();
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
}
|
|
8009
|
-
const _err = err;
|
|
8010
|
-
if (_err && typeof _err === "object" && _err.code === "VITEST_PENDING") {
|
|
8011
|
-
const task = this.idMap.get(_err.taskId);
|
|
8012
|
-
if (task) {
|
|
8013
|
-
task.mode = "skip";
|
|
8014
|
-
task.result ??= { state: "skip" };
|
|
8015
|
-
task.result.state = "skip";
|
|
8016
|
-
task.result.note = _err.note;
|
|
8017
|
-
}
|
|
8018
|
-
return;
|
|
8019
|
-
}
|
|
8020
|
-
this.errorsSet.add(err);
|
|
8021
|
-
}
|
|
8022
|
-
clearErrors() {
|
|
8023
|
-
this.errorsSet.clear();
|
|
8024
|
-
}
|
|
8025
|
-
getUnhandledErrors() {
|
|
8026
|
-
return Array.from(this.errorsSet.values());
|
|
8027
|
-
}
|
|
8028
|
-
addProcessTimeoutCause(cause) {
|
|
8029
|
-
this.processTimeoutCauses.add(cause);
|
|
8030
|
-
}
|
|
8031
|
-
getProcessTimeoutCauses() {
|
|
8032
|
-
return Array.from(this.processTimeoutCauses.values());
|
|
8033
|
-
}
|
|
8034
|
-
getPaths() {
|
|
8035
|
-
return Array.from(this.pathsSet);
|
|
8036
|
-
}
|
|
8037
|
-
/**
|
|
8038
|
-
* Return files that were running or collected.
|
|
8039
|
-
*/
|
|
8040
|
-
getFiles(keys) {
|
|
8041
|
-
if (keys) {
|
|
8042
|
-
return keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local);
|
|
8043
|
-
}
|
|
8044
|
-
return Array.from(this.filesMap.values()).flat().filter((file) => !file.local).sort((f1, f2) => {
|
|
8045
|
-
if (f1.meta?.typecheck && f2.meta?.typecheck) {
|
|
8046
|
-
return 0;
|
|
8047
|
-
}
|
|
8048
|
-
if (f1.meta?.typecheck) {
|
|
8049
|
-
return -1;
|
|
8050
|
-
}
|
|
8051
|
-
return 1;
|
|
8052
|
-
});
|
|
8053
|
-
}
|
|
8054
|
-
getTestModules(keys) {
|
|
8055
|
-
return this.getFiles(keys).map((file) => this.getReportedEntity(file));
|
|
8056
|
-
}
|
|
8057
|
-
getFilepaths() {
|
|
8058
|
-
return Array.from(this.filesMap.keys());
|
|
8059
|
-
}
|
|
8060
|
-
getFailedFilepaths() {
|
|
8061
|
-
return this.getFiles().filter((i) => i.result?.state === "fail").map((i) => i.filepath);
|
|
8062
|
-
}
|
|
8063
|
-
collectPaths(paths = []) {
|
|
8064
|
-
paths.forEach((path) => {
|
|
8065
|
-
this.pathsSet.add(path);
|
|
8066
|
-
});
|
|
8067
|
-
}
|
|
8068
|
-
collectFiles(project, files = []) {
|
|
8069
|
-
files.forEach((file) => {
|
|
8070
|
-
const existing = this.filesMap.get(file.filepath) || [];
|
|
8071
|
-
const otherFiles = existing.filter((i) => i.projectName !== file.projectName || i.meta.typecheck !== file.meta.typecheck);
|
|
8072
|
-
const currentFile = existing.find((i) => i.projectName === file.projectName);
|
|
8073
|
-
if (currentFile) {
|
|
8074
|
-
file.logs = currentFile.logs;
|
|
8075
|
-
}
|
|
8076
|
-
otherFiles.push(file);
|
|
8077
|
-
this.filesMap.set(file.filepath, otherFiles);
|
|
8078
|
-
this.updateId(file, project);
|
|
8079
|
-
});
|
|
8080
|
-
}
|
|
8081
|
-
clearFiles(project, paths = []) {
|
|
8082
|
-
paths.forEach((path) => {
|
|
8083
|
-
const files = this.filesMap.get(path);
|
|
8084
|
-
const fileTask = createFileTask(path, project.config.root, project.config.name);
|
|
8085
|
-
fileTask.local = true;
|
|
8086
|
-
TestModule.register(fileTask, project);
|
|
8087
|
-
this.idMap.set(fileTask.id, fileTask);
|
|
8088
|
-
if (!files) {
|
|
8089
|
-
this.filesMap.set(path, [fileTask]);
|
|
8090
|
-
return;
|
|
8091
|
-
}
|
|
8092
|
-
const filtered = files.filter((file) => file.projectName !== project.config.name);
|
|
8093
|
-
if (!filtered.length) {
|
|
8094
|
-
this.filesMap.set(path, [fileTask]);
|
|
8095
|
-
} else {
|
|
8096
|
-
this.filesMap.set(path, [...filtered, fileTask]);
|
|
8097
|
-
}
|
|
8098
|
-
});
|
|
8099
|
-
}
|
|
8100
|
-
updateId(task, project) {
|
|
8101
|
-
if (this.idMap.get(task.id) === task) {
|
|
8102
|
-
return;
|
|
8103
|
-
}
|
|
8104
|
-
if (task.type === "suite" && "filepath" in task) {
|
|
8105
|
-
TestModule.register(task, project);
|
|
8106
|
-
} else if (task.type === "suite") {
|
|
8107
|
-
TestSuite.register(task, project);
|
|
8108
|
-
} else {
|
|
8109
|
-
TestCase.register(task, project);
|
|
8110
|
-
}
|
|
8111
|
-
this.idMap.set(task.id, task);
|
|
8112
|
-
if (task.type === "suite") {
|
|
8113
|
-
task.tasks.forEach((task) => {
|
|
8114
|
-
this.updateId(task, project);
|
|
8115
|
-
});
|
|
8116
|
-
}
|
|
8117
|
-
}
|
|
8118
|
-
getReportedEntity(task) {
|
|
8119
|
-
return this.reportedTasksMap.get(task);
|
|
8120
|
-
}
|
|
8121
|
-
updateTasks(packs) {
|
|
8122
|
-
for (const [id, result, meta] of packs) {
|
|
8123
|
-
const task = this.idMap.get(id);
|
|
8124
|
-
if (task) {
|
|
8125
|
-
task.result = result;
|
|
8126
|
-
task.meta = meta;
|
|
8127
|
-
if (result?.state === "skip") {
|
|
8128
|
-
task.mode = "skip";
|
|
8129
|
-
}
|
|
8130
|
-
}
|
|
8131
|
-
}
|
|
8132
|
-
}
|
|
8133
|
-
updateUserLog(log) {
|
|
8134
|
-
const task = log.taskId && this.idMap.get(log.taskId);
|
|
8135
|
-
if (task) {
|
|
8136
|
-
if (!task.logs) {
|
|
8137
|
-
task.logs = [];
|
|
8138
|
-
}
|
|
8139
|
-
task.logs.push(log);
|
|
8140
|
-
}
|
|
8141
|
-
}
|
|
8142
|
-
getCountOfFailedTests() {
|
|
8143
|
-
return Array.from(this.idMap.values()).filter((t) => t.result?.state === "fail").length;
|
|
8144
|
-
}
|
|
8145
|
-
cancelFiles(files, project) {
|
|
8146
|
-
this.collectFiles(project, files.map((filepath) => createFileTask(filepath, project.config.root, project.config.name)));
|
|
8147
|
-
}
|
|
8148
|
-
}
|
|
8149
|
-
|
|
8150
|
-
class TestRun {
|
|
8151
|
-
constructor(vitest) {
|
|
8152
|
-
this.vitest = vitest;
|
|
8153
|
-
}
|
|
8154
|
-
async start(specifications) {
|
|
8155
|
-
const filepaths = specifications.map((spec) => spec.moduleId);
|
|
8156
|
-
this.vitest.state.collectPaths(filepaths);
|
|
8157
|
-
await this.vitest.report("onPathsCollected", Array.from(new Set(filepaths)));
|
|
8158
|
-
await this.vitest.report("onSpecsCollected", specifications.map((spec) => spec.toJSON()));
|
|
8159
|
-
await this.vitest.report("onTestRunStart", [...specifications]);
|
|
8160
|
-
}
|
|
8161
|
-
async enqueued(project, file) {
|
|
8162
|
-
this.vitest.state.collectFiles(project, [file]);
|
|
8163
|
-
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8164
|
-
await this.vitest.report("onTestModuleQueued", testModule);
|
|
8165
|
-
}
|
|
8166
|
-
async collected(project, files) {
|
|
8167
|
-
this.vitest.state.collectFiles(project, files);
|
|
8168
|
-
await Promise.all([this.vitest.report("onCollected", files), ...files.map((file) => {
|
|
8169
|
-
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8170
|
-
return this.vitest.report("onTestModuleCollected", testModule);
|
|
8171
|
-
})]);
|
|
8172
|
-
}
|
|
8173
|
-
async log(log) {
|
|
8174
|
-
this.vitest.state.updateUserLog(log);
|
|
8175
|
-
await this.vitest.report("onUserConsoleLog", log);
|
|
8176
|
-
}
|
|
8177
|
-
async updated(update, events) {
|
|
8178
|
-
this.vitest.state.updateTasks(update);
|
|
8179
|
-
await this.vitest.report("onTaskUpdate", update);
|
|
8180
|
-
for (const [id, event] of events) {
|
|
8181
|
-
await this.reportEvent(id, event).catch((error) => {
|
|
8182
|
-
this.vitest.state.catchError(serializeError(error), "Unhandled Reporter Error");
|
|
8183
|
-
});
|
|
8184
|
-
}
|
|
8185
|
-
}
|
|
8186
|
-
async end(specifications, errors, coverage) {
|
|
8187
|
-
const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null);
|
|
8188
|
-
const files = modules.map((m) => m.task);
|
|
8189
|
-
const state = this.vitest.isCancelling ? "interrupted" : process.exitCode ? "failed" : "passed";
|
|
8190
|
-
try {
|
|
8191
|
-
await Promise.all([this.vitest.report("onTestRunEnd", modules, [...errors], state), this.vitest.report("onFinished", files, errors, coverage)]);
|
|
8192
|
-
} finally {
|
|
8193
|
-
if (coverage) {
|
|
8194
|
-
await this.vitest.report("onCoverage", coverage);
|
|
8195
|
-
}
|
|
8196
|
-
}
|
|
8197
|
-
}
|
|
8198
|
-
async reportEvent(id, event) {
|
|
8199
|
-
const task = this.vitest.state.idMap.get(id);
|
|
8200
|
-
const entity = task && this.vitest.state.getReportedEntity(task);
|
|
8201
|
-
assert$1(task && entity, `Entity must be found for task ${task?.name || id}`);
|
|
8202
|
-
if (event === "suite-prepare" && entity.type === "suite") {
|
|
8203
|
-
return await this.vitest.report("onTestSuiteReady", entity);
|
|
8204
|
-
}
|
|
8205
|
-
if (event === "suite-prepare" && entity.type === "module") {
|
|
8206
|
-
return await this.vitest.report("onTestModuleStart", entity);
|
|
8207
|
-
}
|
|
8208
|
-
if (event === "suite-finished") {
|
|
8209
|
-
assert$1(entity.type === "suite" || entity.type === "module", "Entity type must be suite or module");
|
|
8210
|
-
if (entity.state() === "skipped") {
|
|
8211
|
-
await this.reportChildren(entity.children);
|
|
8212
|
-
}
|
|
8213
|
-
if (entity.type === "module") {
|
|
8214
|
-
await this.vitest.report("onTestModuleEnd", entity);
|
|
8215
|
-
} else {
|
|
8216
|
-
await this.vitest.report("onTestSuiteResult", entity);
|
|
8217
|
-
}
|
|
8218
|
-
return;
|
|
8219
|
-
}
|
|
8220
|
-
if (event === "test-prepare" && entity.type === "test") {
|
|
8221
|
-
return await this.vitest.report("onTestCaseReady", entity);
|
|
8222
|
-
}
|
|
8223
|
-
if (event === "test-finished" && entity.type === "test") {
|
|
8224
|
-
return await this.vitest.report("onTestCaseResult", entity);
|
|
8225
|
-
}
|
|
8226
|
-
if (event.startsWith("before-hook") || event.startsWith("after-hook")) {
|
|
8227
|
-
const isBefore = event.startsWith("before-hook");
|
|
8228
|
-
const hook = entity.type === "test" ? {
|
|
8229
|
-
name: isBefore ? "beforeEach" : "afterEach",
|
|
8230
|
-
entity
|
|
8231
|
-
} : {
|
|
8232
|
-
name: isBefore ? "beforeAll" : "afterAll",
|
|
8233
|
-
entity
|
|
8338
|
+
processTimeoutCauses = new Set();
|
|
8339
|
+
reportedTasksMap = new WeakMap();
|
|
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
|
|
8234
8351
|
};
|
|
8235
|
-
if (event.endsWith("-start")) {
|
|
8236
|
-
await this.vitest.report("onHookStart", hook);
|
|
8237
|
-
} else {
|
|
8238
|
-
await this.vitest.report("onHookEnd", hook);
|
|
8239
|
-
}
|
|
8240
8352
|
}
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
if (
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
await this.reportChildren(child.children);
|
|
8250
|
-
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;
|
|
8251
8361
|
}
|
|
8362
|
+
return;
|
|
8252
8363
|
}
|
|
8364
|
+
this.errorsSet.add(err);
|
|
8253
8365
|
}
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
class VitestWatcher {
|
|
8257
|
-
/**
|
|
8258
|
-
* Modules that will be invalidated on the next run.
|
|
8259
|
-
*/
|
|
8260
|
-
invalidates = new Set();
|
|
8261
|
-
/**
|
|
8262
|
-
* Test files that have changed and need to be rerun.
|
|
8263
|
-
*/
|
|
8264
|
-
changedTests = new Set();
|
|
8265
|
-
_onRerun = [];
|
|
8266
|
-
constructor(vitest) {
|
|
8267
|
-
this.vitest = vitest;
|
|
8366
|
+
clearErrors() {
|
|
8367
|
+
this.errorsSet.clear();
|
|
8268
8368
|
}
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
* The callback can receive several files in case the changed file is imported by several test files.
|
|
8272
|
-
* Several invocations of this method will add multiple handlers.
|
|
8273
|
-
* @internal
|
|
8274
|
-
*/
|
|
8275
|
-
onWatcherRerun(cb) {
|
|
8276
|
-
this._onRerun.push(cb);
|
|
8277
|
-
return this;
|
|
8369
|
+
getUnhandledErrors() {
|
|
8370
|
+
return Array.from(this.errorsSet.values());
|
|
8278
8371
|
}
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
const watcher = this.vitest.vite.watcher;
|
|
8282
|
-
if (this.vitest.config.forceRerunTriggers.length) {
|
|
8283
|
-
watcher.add(this.vitest.config.forceRerunTriggers);
|
|
8284
|
-
}
|
|
8285
|
-
watcher.on("change", this.onChange);
|
|
8286
|
-
watcher.on("unlink", this.onUnlink);
|
|
8287
|
-
watcher.on("add", this.onAdd);
|
|
8288
|
-
this.unregisterWatcher = () => {
|
|
8289
|
-
watcher.off("change", this.onChange);
|
|
8290
|
-
watcher.off("unlink", this.onUnlink);
|
|
8291
|
-
watcher.off("add", this.onAdd);
|
|
8292
|
-
this.unregisterWatcher = noop;
|
|
8293
|
-
};
|
|
8294
|
-
return this;
|
|
8372
|
+
addProcessTimeoutCause(cause) {
|
|
8373
|
+
this.processTimeoutCauses.add(cause);
|
|
8295
8374
|
}
|
|
8296
|
-
|
|
8297
|
-
this.
|
|
8375
|
+
getProcessTimeoutCauses() {
|
|
8376
|
+
return Array.from(this.processTimeoutCauses.values());
|
|
8377
|
+
}
|
|
8378
|
+
getPaths() {
|
|
8379
|
+
return Array.from(this.pathsSet);
|
|
8298
8380
|
}
|
|
8299
|
-
onChange = (id) => {
|
|
8300
|
-
id = slash(id);
|
|
8301
|
-
this.vitest.logger.clearHighlightCache(id);
|
|
8302
|
-
this.vitest.invalidateFile(id);
|
|
8303
|
-
const needsRerun = this.handleFileChanged(id);
|
|
8304
|
-
if (needsRerun) {
|
|
8305
|
-
this.scheduleRerun(id);
|
|
8306
|
-
}
|
|
8307
|
-
};
|
|
8308
|
-
onUnlink = (id) => {
|
|
8309
|
-
id = slash(id);
|
|
8310
|
-
this.vitest.logger.clearHighlightCache(id);
|
|
8311
|
-
this.invalidates.add(id);
|
|
8312
|
-
if (this.vitest.state.filesMap.has(id)) {
|
|
8313
|
-
this.vitest.projects.forEach((project) => project._removeCachedTestFile(id));
|
|
8314
|
-
this.vitest.state.filesMap.delete(id);
|
|
8315
|
-
this.vitest.cache.results.removeFromCache(id);
|
|
8316
|
-
this.vitest.cache.stats.removeStats(id);
|
|
8317
|
-
this.changedTests.delete(id);
|
|
8318
|
-
this.vitest.report("onTestRemoved", id);
|
|
8319
|
-
}
|
|
8320
|
-
};
|
|
8321
|
-
onAdd = (id) => {
|
|
8322
|
-
id = slash(id);
|
|
8323
|
-
this.vitest.invalidateFile(id);
|
|
8324
|
-
let fileContent;
|
|
8325
|
-
const matchingProjects = [];
|
|
8326
|
-
this.vitest.projects.forEach((project) => {
|
|
8327
|
-
if (project.matchesTestGlob(id, () => fileContent ??= readFileSync(id, "utf-8"))) {
|
|
8328
|
-
matchingProjects.push(project);
|
|
8329
|
-
}
|
|
8330
|
-
});
|
|
8331
|
-
if (matchingProjects.length > 0) {
|
|
8332
|
-
this.changedTests.add(id);
|
|
8333
|
-
this.scheduleRerun(id);
|
|
8334
|
-
} else {
|
|
8335
|
-
const needsRerun = this.handleFileChanged(id);
|
|
8336
|
-
if (needsRerun) {
|
|
8337
|
-
this.scheduleRerun(id);
|
|
8338
|
-
}
|
|
8339
|
-
}
|
|
8340
|
-
};
|
|
8341
8381
|
/**
|
|
8342
|
-
*
|
|
8382
|
+
* Return files that were running or collected.
|
|
8343
8383
|
*/
|
|
8344
|
-
|
|
8345
|
-
if (
|
|
8346
|
-
return
|
|
8347
|
-
}
|
|
8348
|
-
if (mm.isMatch(filepath, this.vitest.config.forceRerunTriggers)) {
|
|
8349
|
-
this.vitest.state.getFilepaths().forEach((file) => this.changedTests.add(file));
|
|
8350
|
-
return true;
|
|
8351
|
-
}
|
|
8352
|
-
const projects = this.vitest.projects.filter((project) => {
|
|
8353
|
-
const moduleGraph = project.browser?.vite.moduleGraph || project.vite.moduleGraph;
|
|
8354
|
-
return moduleGraph.getModulesByFile(filepath)?.size;
|
|
8355
|
-
});
|
|
8356
|
-
if (!projects.length) {
|
|
8357
|
-
if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath))) {
|
|
8358
|
-
this.changedTests.add(filepath);
|
|
8359
|
-
return true;
|
|
8360
|
-
}
|
|
8361
|
-
return false;
|
|
8384
|
+
getFiles(keys) {
|
|
8385
|
+
if (keys) {
|
|
8386
|
+
return keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local);
|
|
8362
8387
|
}
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
if (!mods || !mods.size) {
|
|
8367
|
-
continue;
|
|
8368
|
-
}
|
|
8369
|
-
this.invalidates.add(filepath);
|
|
8370
|
-
if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
|
|
8371
|
-
this.changedTests.add(filepath);
|
|
8372
|
-
files.push(filepath);
|
|
8373
|
-
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;
|
|
8374
8391
|
}
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
mod.importers.forEach((i) => {
|
|
8378
|
-
if (!i.file) {
|
|
8379
|
-
return;
|
|
8380
|
-
}
|
|
8381
|
-
const needsRerun = this.handleFileChanged(i.file);
|
|
8382
|
-
if (needsRerun) {
|
|
8383
|
-
rerun = true;
|
|
8384
|
-
}
|
|
8385
|
-
});
|
|
8392
|
+
if (f1.meta?.typecheck) {
|
|
8393
|
+
return -1;
|
|
8386
8394
|
}
|
|
8387
|
-
|
|
8388
|
-
|
|
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;
|
|
8389
8419
|
}
|
|
8390
|
-
|
|
8391
|
-
|
|
8420
|
+
otherFiles.push(file);
|
|
8421
|
+
this.filesMap.set(file.filepath, otherFiles);
|
|
8422
|
+
this.updateId(file, project);
|
|
8423
|
+
});
|
|
8392
8424
|
}
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
"update",
|
|
8404
|
-
"globals",
|
|
8405
|
-
"expandSnapshotDiff",
|
|
8406
|
-
"disableConsoleIntercept",
|
|
8407
|
-
"retry",
|
|
8408
|
-
"testNamePattern",
|
|
8409
|
-
"passWithNoTests",
|
|
8410
|
-
"bail",
|
|
8411
|
-
"isolate",
|
|
8412
|
-
"printConsoleTrace",
|
|
8413
|
-
"inspect",
|
|
8414
|
-
"inspectBrk",
|
|
8415
|
-
"fileParallelism"
|
|
8416
|
-
];
|
|
8417
|
-
const cliOverrides = overridesOptions.reduce((acc, name) => {
|
|
8418
|
-
if (name in cliOptions) {
|
|
8419
|
-
acc[name] = cliOptions[name];
|
|
8420
|
-
}
|
|
8421
|
-
return acc;
|
|
8422
|
-
}, {});
|
|
8423
|
-
const projectPromises = [];
|
|
8424
|
-
const fileProjects = [...configFiles, ...nonConfigDirectories];
|
|
8425
|
-
const concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
|
|
8426
|
-
projectConfigs.forEach((options, index) => {
|
|
8427
|
-
const configRoot = workspaceConfigPath ? dirname(workspaceConfigPath) : vitest.config.root;
|
|
8428
|
-
const configFile = typeof options.extends === "string" ? resolve(configRoot, options.extends) : options.extends === true ? vitest.vite.config.configFile || false : false;
|
|
8429
|
-
const root = options.root ? resolve(configRoot, options.root) : vitest.config.root;
|
|
8430
|
-
projectPromises.push(concurrent(() => initializeProject(index, vitest, {
|
|
8431
|
-
...options,
|
|
8432
|
-
root,
|
|
8433
|
-
configFile,
|
|
8434
|
-
test: {
|
|
8435
|
-
...options.test,
|
|
8436
|
-
...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;
|
|
8437
8435
|
}
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
if (project) {
|
|
8444
|
-
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]);
|
|
8445
8441
|
}
|
|
8446
|
-
|
|
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
|
+
});
|
|
8447
8460
|
}
|
|
8448
|
-
const configFile = path.endsWith("/") ? false : path;
|
|
8449
|
-
const root = path.endsWith("/") ? path : dirname(path);
|
|
8450
|
-
projectPromises.push(concurrent(() => initializeProject(path, vitest, {
|
|
8451
|
-
root,
|
|
8452
|
-
configFile,
|
|
8453
|
-
test: cliOverrides
|
|
8454
|
-
})));
|
|
8455
8461
|
}
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
"No projects were found. Make sure your configuration is correct. ",
|
|
8459
|
-
vitest.config.project.length ? `The filter matched no projects: ${vitest.config.project.join(", ")}. ` : "",
|
|
8460
|
-
`The workspace: ${JSON.stringify(workspaceDefinition, null, 4)}.`
|
|
8461
|
-
].join(""));
|
|
8462
|
+
getReportedEntity(task) {
|
|
8463
|
+
return this.reportedTasksMap.get(task);
|
|
8462
8464
|
}
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
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
|
+
}
|
|
8470
8474
|
}
|
|
8471
|
-
errors.push(result.reason);
|
|
8472
|
-
} else {
|
|
8473
|
-
resolvedProjects.push(result.value);
|
|
8474
8475
|
}
|
|
8475
8476
|
}
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
const filesError = fileProjects.length ? [
|
|
8484
|
-
"\n\nYour config matched these files:\n",
|
|
8485
|
-
fileProjects.map((p) => ` - ${relative(vitest.config.root, p)}`).join("\n"),
|
|
8486
|
-
"\n\n"
|
|
8487
|
-
].join("") : [" "];
|
|
8488
|
-
throw new Error([
|
|
8489
|
-
`Project name "${name}"`,
|
|
8490
|
-
project.vite.config.configFile ? ` from "${relative(vitest.config.root, project.vite.config.configFile)}"` : "",
|
|
8491
|
-
" is not unique.",
|
|
8492
|
-
duplicate?.vite.config.configFile ? ` The project is already defined by "${relative(vitest.config.root, duplicate.vite.config.configFile)}".` : "",
|
|
8493
|
-
filesError,
|
|
8494
|
-
"All projects in a workspace should have unique names. Make sure your configuration is correct."
|
|
8495
|
-
].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);
|
|
8496
8484
|
}
|
|
8497
|
-
names.add(name);
|
|
8498
8485
|
}
|
|
8499
|
-
|
|
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
|
+
}
|
|
8500
8492
|
}
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
const
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
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");
|
|
8513
8527
|
});
|
|
8514
|
-
console.warn(withLabel("yellow", "Vitest", [
|
|
8515
|
-
`No browser "instances" were defined`,
|
|
8516
|
-
project.name ? ` for the "${project.name}" project. ` : ". ",
|
|
8517
|
-
`Running tests in "${project.config.browser.name}" browser. `,
|
|
8518
|
-
"The \"browser.name\" field is deprecated since Vitest 3. ",
|
|
8519
|
-
"Read more: https://vitest.dev/guide/browser/config#browser-instances"
|
|
8520
|
-
].filter(Boolean).join("")));
|
|
8521
8528
|
}
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
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
|
+
}
|
|
8530
8540
|
}
|
|
8531
|
-
|
|
8532
|
-
|
|
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);
|
|
8533
8548
|
}
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
const name = config.name;
|
|
8542
|
-
if (name == null) {
|
|
8543
|
-
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);
|
|
8544
8556
|
}
|
|
8545
|
-
if (
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
"All projects in a workspace should have unique names. Make sure your configuration is correct."
|
|
8550
|
-
].join(""));
|
|
8557
|
+
if (entity.type === "module") {
|
|
8558
|
+
await this.vitest.report("onTestModuleEnd", entity);
|
|
8559
|
+
} else {
|
|
8560
|
+
await this.vitest.report("onTestSuiteResult", entity);
|
|
8551
8561
|
}
|
|
8552
|
-
|
|
8553
|
-
const clonedConfig = cloneConfig(project, config);
|
|
8554
|
-
clonedConfig.name = name;
|
|
8555
|
-
const clone = TestProject._cloneBrowserProject(project, clonedConfig);
|
|
8556
|
-
resolvedProjects.push(clone);
|
|
8557
|
-
});
|
|
8558
|
-
removeProjects.add(project);
|
|
8559
|
-
});
|
|
8560
|
-
resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
|
|
8561
|
-
const headedBrowserProjects = resolvedProjects.filter((project) => {
|
|
8562
|
-
return project.config.browser.enabled && !project.config.browser.headless;
|
|
8563
|
-
});
|
|
8564
|
-
if (headedBrowserProjects.length > 1) {
|
|
8565
|
-
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("");
|
|
8566
|
-
if (!isTTY) {
|
|
8567
|
-
throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
|
|
8562
|
+
return;
|
|
8568
8563
|
}
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
type: "select",
|
|
8572
|
-
name: "projectName",
|
|
8573
|
-
choices: headedBrowserProjects.map((project) => ({
|
|
8574
|
-
title: project.name,
|
|
8575
|
-
value: project.name
|
|
8576
|
-
})),
|
|
8577
|
-
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.`
|
|
8578
|
-
});
|
|
8579
|
-
if (!projectName) {
|
|
8580
|
-
throw new Error("The test run was aborted.");
|
|
8564
|
+
if (event === "test-prepare" && entity.type === "test") {
|
|
8565
|
+
return await this.vitest.report("onTestCaseReady", entity);
|
|
8581
8566
|
}
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
return resolvedProjects;
|
|
8585
|
-
}
|
|
8586
|
-
function cloneConfig(project, { browser,...config }) {
|
|
8587
|
-
const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name,...overrideConfig } = config;
|
|
8588
|
-
const currentConfig = project.config.browser;
|
|
8589
|
-
return mergeConfig({
|
|
8590
|
-
...deepClone(project.config),
|
|
8591
|
-
browser: {
|
|
8592
|
-
...project.config.browser,
|
|
8593
|
-
locators: locators ? { testIdAttribute: locators.testIdAttribute ?? currentConfig.locators.testIdAttribute } : project.config.browser.locators,
|
|
8594
|
-
viewport: viewport ?? currentConfig.viewport,
|
|
8595
|
-
testerHtmlPath: testerHtmlPath ?? currentConfig.testerHtmlPath,
|
|
8596
|
-
screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory,
|
|
8597
|
-
screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures,
|
|
8598
|
-
headless: project.vitest._options?.browser?.headless ?? headless ?? currentConfig.headless,
|
|
8599
|
-
name: browser,
|
|
8600
|
-
providerOptions: config,
|
|
8601
|
-
instances: undefined
|
|
8567
|
+
if (event === "test-finished" && entity.type === "test") {
|
|
8568
|
+
return await this.vitest.report("onTestCaseResult", entity);
|
|
8602
8569
|
}
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
const file = resolve(vitest.config.root, stringOption);
|
|
8615
|
-
if (!existsSync(file)) {
|
|
8616
|
-
const relativeWorkSpaceConfigPath = workspaceConfigPath ? relative(vitest.config.root, workspaceConfigPath) : undefined;
|
|
8617
|
-
const note = workspaceConfigPath ? `Workspace config file "${relativeWorkSpaceConfigPath}"` : "Inline workspace";
|
|
8618
|
-
throw new Error(`${note} references a non-existing file or a directory: ${file}`);
|
|
8619
|
-
}
|
|
8620
|
-
const stats = await promises.stat(file);
|
|
8621
|
-
if (stats.isFile()) {
|
|
8622
|
-
workspaceConfigFiles.push(file);
|
|
8623
|
-
} else if (stats.isDirectory()) {
|
|
8624
|
-
const configFile = await resolveDirectoryConfig(file);
|
|
8625
|
-
if (configFile) {
|
|
8626
|
-
workspaceConfigFiles.push(configFile);
|
|
8627
|
-
} else {
|
|
8628
|
-
const directory = file[file.length - 1] === "/" ? file : `${file}/`;
|
|
8629
|
-
nonConfigProjectDirectories.push(directory);
|
|
8630
|
-
}
|
|
8631
|
-
} else {
|
|
8632
|
-
throw new TypeError(`Unexpected file type: ${file}`);
|
|
8633
|
-
}
|
|
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);
|
|
8634
8581
|
} else {
|
|
8635
|
-
|
|
8582
|
+
await this.vitest.report("onHookEnd", hook);
|
|
8636
8583
|
}
|
|
8637
|
-
} else if (typeof definition === "function") {
|
|
8638
|
-
projectsOptions.push(await definition({
|
|
8639
|
-
command: vitest.vite.config.command,
|
|
8640
|
-
mode: vitest.vite.config.mode,
|
|
8641
|
-
isPreview: false,
|
|
8642
|
-
isSsrBuild: false
|
|
8643
|
-
}));
|
|
8644
|
-
} else {
|
|
8645
|
-
projectsOptions.push(await definition);
|
|
8646
8584
|
}
|
|
8647
8585
|
}
|
|
8648
|
-
|
|
8649
|
-
const
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
cwd: vitest.config.root,
|
|
8654
|
-
expandDirectories: false,
|
|
8655
|
-
ignore: [
|
|
8656
|
-
"**/node_modules/**",
|
|
8657
|
-
"**/*.timestamp-*",
|
|
8658
|
-
"**/.DS_Store"
|
|
8659
|
-
]
|
|
8660
|
-
};
|
|
8661
|
-
const workspacesFs = await glob(workspaceGlobMatches, globOptions);
|
|
8662
|
-
await Promise.all(workspacesFs.map(async (path) => {
|
|
8663
|
-
if (path.endsWith("/")) {
|
|
8664
|
-
const configFile = await resolveDirectoryConfig(path);
|
|
8665
|
-
if (configFile) {
|
|
8666
|
-
workspaceConfigFiles.push(configFile);
|
|
8667
|
-
} else {
|
|
8668
|
-
nonConfigProjectDirectories.push(path);
|
|
8669
|
-
}
|
|
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);
|
|
8670
8591
|
} else {
|
|
8671
|
-
|
|
8592
|
+
await this.vitest.report("onTestSuiteReady", child);
|
|
8593
|
+
await this.reportChildren(child.children);
|
|
8594
|
+
await this.vitest.report("onTestSuiteResult", child);
|
|
8672
8595
|
}
|
|
8673
|
-
}
|
|
8596
|
+
}
|
|
8674
8597
|
}
|
|
8675
|
-
const projectConfigFiles = Array.from(new Set(workspaceConfigFiles));
|
|
8676
|
-
return {
|
|
8677
|
-
projectConfigs: projectsOptions,
|
|
8678
|
-
nonConfigDirectories: nonConfigProjectDirectories,
|
|
8679
|
-
configFiles: projectConfigFiles
|
|
8680
|
-
};
|
|
8681
8598
|
}
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
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;
|
|
8687
8612
|
}
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
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;
|
|
8695
8622
|
}
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
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;
|
|
8699
8639
|
}
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
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;
|
|
8708
8766
|
}
|
|
8709
|
-
return names;
|
|
8710
8767
|
}
|
|
8711
8768
|
|
|
8712
8769
|
const WATCHER_DEBOUNCE = 100;
|
|
@@ -8899,7 +8956,7 @@ class Vitest {
|
|
|
8899
8956
|
try {
|
|
8900
8957
|
await this.cache.results.readFromCache();
|
|
8901
8958
|
} catch {}
|
|
8902
|
-
const projects = await this.
|
|
8959
|
+
const projects = await this.resolveProjects(cliOptions);
|
|
8903
8960
|
this.resolvedProjects = projects;
|
|
8904
8961
|
this.projects = projects;
|
|
8905
8962
|
await Promise.all(projects.flatMap((project) => {
|
|
@@ -8910,8 +8967,19 @@ class Vitest {
|
|
|
8910
8967
|
injectTestProjects: this.injectTestProject
|
|
8911
8968
|
}));
|
|
8912
8969
|
}));
|
|
8970
|
+
if (options.browser?.enabled) {
|
|
8971
|
+
const browserProjects = this.projects.filter((p) => p.config.browser.enabled);
|
|
8972
|
+
if (!browserProjects.length) {
|
|
8973
|
+
throw new Error(`Vitest received --browser flag, but no project had a browser configuration.`);
|
|
8974
|
+
}
|
|
8975
|
+
}
|
|
8913
8976
|
if (!this.projects.length) {
|
|
8914
|
-
|
|
8977
|
+
const filter = toArray(resolved.project).join("\", \"");
|
|
8978
|
+
if (filter) {
|
|
8979
|
+
throw new Error(`No projects matched the filter "${filter}".`);
|
|
8980
|
+
} else {
|
|
8981
|
+
throw new Error(`Vitest wasn't able to resolve any project.`);
|
|
8982
|
+
}
|
|
8915
8983
|
}
|
|
8916
8984
|
if (!this.coreWorkspaceProject) {
|
|
8917
8985
|
this.coreWorkspaceProject = TestProject._createBasicProject(this);
|
|
@@ -8929,9 +8997,9 @@ class Vitest {
|
|
|
8929
8997
|
*/
|
|
8930
8998
|
injectTestProject = async (config) => {
|
|
8931
8999
|
const currentNames = new Set(this.projects.map((p) => p.name));
|
|
8932
|
-
const
|
|
8933
|
-
this.projects.push(...
|
|
8934
|
-
return
|
|
9000
|
+
const projects = await resolveProjects(this, this._options, undefined, Array.isArray(config) ? config : [config], currentNames);
|
|
9001
|
+
this.projects.push(...projects);
|
|
9002
|
+
return projects;
|
|
8935
9003
|
};
|
|
8936
9004
|
/**
|
|
8937
9005
|
* Provide a value to the test context. This value will be available to all tests with `inject`.
|
|
@@ -9002,10 +9070,17 @@ class Vitest {
|
|
|
9002
9070
|
}
|
|
9003
9071
|
return join(configDir, workspaceConfigName);
|
|
9004
9072
|
}
|
|
9005
|
-
async
|
|
9073
|
+
async resolveProjects(cliOptions) {
|
|
9006
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
|
+
}
|
|
9007
9081
|
if (Array.isArray(this.config.workspace)) {
|
|
9008
|
-
|
|
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);
|
|
9009
9084
|
}
|
|
9010
9085
|
const workspaceConfigPath = await this.resolveWorkspaceConfigPath();
|
|
9011
9086
|
this._workspaceConfigPath = workspaceConfigPath;
|
|
@@ -9014,13 +9089,15 @@ class Vitest {
|
|
|
9014
9089
|
if (!project) {
|
|
9015
9090
|
return [];
|
|
9016
9091
|
}
|
|
9017
|
-
return
|
|
9092
|
+
return resolveBrowserProjects(this, new Set([project.name]), [project]);
|
|
9018
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.`);
|
|
9019
9096
|
const workspaceModule = await this.import(workspaceConfigPath);
|
|
9020
9097
|
if (!workspaceModule.default || !Array.isArray(workspaceModule.default)) {
|
|
9021
9098
|
throw new TypeError(`Workspace config file "${workspaceConfigPath}" must export a default array of project paths.`);
|
|
9022
9099
|
}
|
|
9023
|
-
return
|
|
9100
|
+
return resolveProjects(this, cliOptions, workspaceConfigPath, workspaceModule.default, names);
|
|
9024
9101
|
}
|
|
9025
9102
|
/**
|
|
9026
9103
|
* Glob test files in every project and create a TestSpecification for each file and pool.
|
|
@@ -9047,7 +9124,13 @@ class Vitest {
|
|
|
9047
9124
|
if (this.reporters.some((r) => r instanceof BlobReporter)) {
|
|
9048
9125
|
throw new Error("Cannot merge reports when `--reporter=blob` is used. Remove blob reporter from the config first.");
|
|
9049
9126
|
}
|
|
9050
|
-
const { files, errors, coverages } = await readBlobs(this.version, directory || this.config.mergeReports, this.projects);
|
|
9127
|
+
const { files, errors, coverages, executionTimes } = await readBlobs(this.version, directory || this.config.mergeReports, this.projects);
|
|
9128
|
+
this.state.blobs = {
|
|
9129
|
+
files,
|
|
9130
|
+
errors,
|
|
9131
|
+
coverages,
|
|
9132
|
+
executionTimes
|
|
9133
|
+
};
|
|
9051
9134
|
await this.report("onInit", this);
|
|
9052
9135
|
await this.report("onPathsCollected", files.flatMap((f) => f.filepath));
|
|
9053
9136
|
const specifications = [];
|
|
@@ -9246,7 +9329,9 @@ class Vitest {
|
|
|
9246
9329
|
process.exitCode = 1;
|
|
9247
9330
|
}
|
|
9248
9331
|
this.cache.results.updateResults(files);
|
|
9249
|
-
|
|
9332
|
+
try {
|
|
9333
|
+
await this.cache.results.writeToCache();
|
|
9334
|
+
} catch {}
|
|
9250
9335
|
return {
|
|
9251
9336
|
testModules: this.state.getTestModules(),
|
|
9252
9337
|
unhandledErrors: this.state.getUnhandledErrors()
|