vitest 3.1.3 → 3.2.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +0 -232
- package/dist/browser.d.ts +4 -2
- package/dist/browser.js +3 -4
- package/dist/chunks/{base.DslwPSCy.js → base.DwtwORaC.js} +2 -2
- package/dist/chunks/{cac.BN2e7cE1.js → cac.I9MLYfT-.js} +14 -10
- package/dist/chunks/{cli-api.Bti1vevt.js → cli-api.d6IK1pnk.js} +945 -852
- package/dist/chunks/{coverage.87S59-Sl.js → coverage.OGU09Jbh.js} +129 -4216
- package/dist/chunks/{creator.CuL7xDWI.js → creator.DGAdZ4Hj.js} +18 -39
- package/dist/chunks/{environment.d.Dmw5ulng.d.ts → environment.d.D8YDy2v5.d.ts} +2 -1
- package/dist/chunks/{execute.BpmIjFTD.js → execute.JlGHLJZT.js} +3 -5
- package/dist/chunks/{global.d.CXRAxnWc.d.ts → global.d.BPa1eL3O.d.ts} +17 -12
- package/dist/chunks/{globals.CZAEe_Gf.js → globals.CpxW8ccg.js} +2 -3
- package/dist/chunks/{index.Bw6JxgX8.js → index.CK1YOQaa.js} +7 -7
- package/dist/chunks/{index.De2FqGmR.js → index.CV36oG_L.js} +896 -957
- package/dist/chunks/{index.B0uVAVvx.js → index.CfXMNXHg.js} +2 -14
- package/dist/chunks/index.CmC5OK9L.js +275 -0
- package/dist/chunks/{index.Cu2UlluP.js → index.DswW_LEs.js} +2 -2
- package/dist/chunks/{index.DBIGubLC.js → index.X0nbfr6-.js} +7 -7
- package/dist/chunks/{reporters.d.DG9VKi4m.d.ts → reporters.d.CLC9rhKy.d.ts} +68 -11
- package/dist/chunks/{runBaseTests.BV8m0B-u.js → runBaseTests.Dn2vyej_.js} +5 -6
- package/dist/chunks/{setup-common.AQcDs321.js → setup-common.CYo3Y0dD.js} +1 -3
- package/dist/chunks/typechecker.DnTrplSJ.js +897 -0
- package/dist/chunks/{vi.ClIskdbk.js → vi.BFR5YIgu.js} +3 -0
- package/dist/chunks/{vite.d.D3ndlJcw.d.ts → vite.d.CBZ3M_ru.d.ts} +1 -1
- package/dist/chunks/{vm.CuLHT1BG.js → vm.C1HHjtNS.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 +20 -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 +4 -7
- 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 -32
- package/dist/index.js +2 -3
- package/dist/node.d.ts +8 -8
- package/dist/node.js +16 -18
- package/dist/reporters.d.ts +3 -3
- package/dist/reporters.js +14 -14
- package/dist/runners.d.ts +1 -1
- package/dist/runners.js +2 -2
- package/dist/workers/forks.js +2 -2
- package/dist/workers/runVmTests.js +5 -6
- 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 +15 -19
- package/dist/chunks/run-once.Dimr7O9f.js +0 -47
- package/dist/chunks/typechecker.DYQbn8uK.js +0 -956
- package/dist/chunks/utils.Cc45eY3L.js +0 -200
- package/dist/utils.d.ts +0 -3
- package/dist/utils.js +0 -2
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { promises, existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { extname, normalize, relative, dirname, resolve, join, basename, isAbsolute } from 'pathe';
|
|
3
3
|
import { C as CoverageProviderMap } from './coverage.0iPg4Wrz.js';
|
|
4
|
-
import
|
|
4
|
+
import path, { resolve as resolve$1 } from 'node:path';
|
|
5
5
|
import { noop, isPrimitive, createDefer, highlight, toArray, deepMerge, nanoid, slash, deepClone, notNullish } from '@vitest/utils';
|
|
6
|
-
import { f as findUp, p as prompt } from './index.
|
|
6
|
+
import { f as findUp, p as prompt } from './index.X0nbfr6-.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.I9MLYfT-.js';
|
|
15
15
|
import { c as createBirpc } from './index.CJ0plNrh.js';
|
|
16
|
-
import { p as parse, s as stringify,
|
|
16
|
+
import { p as parse, s as stringify, d as printError, f as formatProjectName, w as withLabel, e as errorBanner, h as divider, i as generateCodeFrame, R as ReportersMap, j as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.CV36oG_L.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,21 +28,22 @@ 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,
|
|
32
|
-
import { c as convertTasksToEvents } from './typechecker.
|
|
31
|
+
import { R as RandomSequencer, i as isPackageExists, h as hash, V as VitestCache, g as getFilePoolName, d as isBrowserEnabled, r as resolveConfig, e as groupBy, f as getCoverageProvider, j as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.OGU09Jbh.js';
|
|
32
|
+
import { c as convertTasksToEvents } from './typechecker.DnTrplSJ.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.Cc45eY3L.js';
|
|
36
35
|
import { createRequire } from 'node:module';
|
|
37
36
|
import url from 'node:url';
|
|
38
37
|
import { i as isTTY, a as isWindows } from './env.Dq0hM4Xv.js';
|
|
39
38
|
import { rm } from 'node:fs/promises';
|
|
40
39
|
import nodeos__default, { tmpdir } from 'node:os';
|
|
40
|
+
import pm from 'picomatch';
|
|
41
41
|
import { glob, isDynamicPattern } from 'tinyglobby';
|
|
42
42
|
import { normalizeRequestId, cleanUrl } from 'vite-node/utils';
|
|
43
43
|
import { hoistMocksPlugin, automockPlugin } from '@vitest/mocker/node';
|
|
44
44
|
import { c as configDefaults } from './defaults.DSxsTG0h.js';
|
|
45
45
|
import MagicString from 'magic-string';
|
|
46
|
+
import { a as BenchmarkReportsMap } from './index.CmC5OK9L.js';
|
|
46
47
|
import assert$1 from 'node:assert';
|
|
47
48
|
import { serializeError } from '@vitest/utils/error';
|
|
48
49
|
import readline from 'node:readline';
|
|
@@ -5125,6 +5126,12 @@ function setup(ctx, _server) {
|
|
|
5125
5126
|
getResolvedProjectNames() {
|
|
5126
5127
|
return ctx.projects.map((p) => p.name);
|
|
5127
5128
|
},
|
|
5129
|
+
getResolvedProjectLabels() {
|
|
5130
|
+
return ctx.projects.map((p) => ({
|
|
5131
|
+
name: p.name,
|
|
5132
|
+
color: p.color
|
|
5133
|
+
}));
|
|
5134
|
+
},
|
|
5128
5135
|
async getTransformResult(projectName, id, browser = false) {
|
|
5129
5136
|
const project = ctx.getProjectByName(projectName);
|
|
5130
5137
|
const result = browser ? await project.browser.vite.transformRequest(id) : await project.vitenode.transformRequest(id);
|
|
@@ -5409,6 +5416,9 @@ class Logger {
|
|
|
5409
5416
|
printError(err, options = {}) {
|
|
5410
5417
|
printError(err, this.ctx, this, options);
|
|
5411
5418
|
}
|
|
5419
|
+
deprecate(message) {
|
|
5420
|
+
this.log(c.bold(c.bgYellow(" DEPRECATED ")), c.yellow(message));
|
|
5421
|
+
}
|
|
5412
5422
|
clearHighlightCache(filename) {
|
|
5413
5423
|
if (filename) {
|
|
5414
5424
|
this._highlights.delete(filename);
|
|
@@ -5449,7 +5459,7 @@ class Logger {
|
|
|
5449
5459
|
const config = project.config;
|
|
5450
5460
|
const printConfig = !project.isRootProject() && project.name;
|
|
5451
5461
|
if (printConfig) {
|
|
5452
|
-
this.console.error(`\n${formatProjectName(project
|
|
5462
|
+
this.console.error(`\n${formatProjectName(project)}\n`);
|
|
5453
5463
|
}
|
|
5454
5464
|
if (config.include) {
|
|
5455
5465
|
this.console.error(c.dim("include: ") + c.yellow(config.include.join(comma)));
|
|
@@ -5501,7 +5511,7 @@ class Logger {
|
|
|
5501
5511
|
if (!origin) {
|
|
5502
5512
|
return;
|
|
5503
5513
|
}
|
|
5504
|
-
const output = project.isRootProject() ? "" : formatProjectName(project
|
|
5514
|
+
const output = project.isRootProject() ? "" : formatProjectName(project);
|
|
5505
5515
|
const provider = project.browser.provider.name;
|
|
5506
5516
|
const providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
|
|
5507
5517
|
this.log(c.dim(`${output}Browser runner started${providerString} ${c.dim("at")} ${c.blue(new URL("/", origin))}\n`));
|
|
@@ -5597,7 +5607,7 @@ class VitestPackageInstaller {
|
|
|
5597
5607
|
if (!isTTY) {
|
|
5598
5608
|
return false;
|
|
5599
5609
|
}
|
|
5600
|
-
const prompts = await import('./index.
|
|
5610
|
+
const prompts = await import('./index.X0nbfr6-.js').then(function (n) { return n.i; });
|
|
5601
5611
|
const { install } = await prompts.default({
|
|
5602
5612
|
type: "confirm",
|
|
5603
5613
|
name: "install",
|
|
@@ -5605,7 +5615,7 @@ class VitestPackageInstaller {
|
|
|
5605
5615
|
});
|
|
5606
5616
|
if (install) {
|
|
5607
5617
|
const packageName = version ? `${dependency}@${version}` : dependency;
|
|
5608
|
-
await (await import('./index.
|
|
5618
|
+
await (await import('./index.CK1YOQaa.js')).installPackage(packageName, { dev: true });
|
|
5609
5619
|
process.stderr.write(c.yellow(`\nPackage ${packageName} installed, re-run the command to start.\n`));
|
|
5610
5620
|
process.exit();
|
|
5611
5621
|
return true;
|
|
@@ -6521,7 +6531,8 @@ function resolveOptimizerConfig(_testOptions, viteOptions, testConfig, viteCache
|
|
|
6521
6531
|
const runtime = currentInclude.filter((n) => n.endsWith("jsx-dev-runtime") || n.endsWith("jsx-runtime"));
|
|
6522
6532
|
exclude.push(...runtime);
|
|
6523
6533
|
const include = (testOptions.include || viteOptions?.include || []).filter((n) => !exclude.includes(n));
|
|
6524
|
-
|
|
6534
|
+
const projectName = typeof testConfig.name === "string" ? testConfig.name : testConfig.name?.label;
|
|
6535
|
+
newConfig.cacheDir = testConfig.cache !== false && testConfig.cache?.dir || VitestCache.resolveCacheDir(root, viteCacheDir, projectName);
|
|
6525
6536
|
newConfig.optimizeDeps = {
|
|
6526
6537
|
...viteOptions,
|
|
6527
6538
|
...testOptions,
|
|
@@ -6688,7 +6699,10 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
6688
6699
|
const defines = deleteDefineConfig(viteConfig);
|
|
6689
6700
|
const testConfig = viteConfig.test || {};
|
|
6690
6701
|
const root = testConfig.root || viteConfig.root || options.root;
|
|
6691
|
-
let name = testConfig.name
|
|
6702
|
+
let { label: name, color } = typeof testConfig.name === "string" ? { label: testConfig.name } : {
|
|
6703
|
+
label: "",
|
|
6704
|
+
...testConfig.name
|
|
6705
|
+
};
|
|
6692
6706
|
if (!name) {
|
|
6693
6707
|
if (typeof options.workspacePath === "string") {
|
|
6694
6708
|
const dir = options.workspacePath.endsWith("/") ? options.workspacePath.slice(0, -1) : dirname(options.workspacePath);
|
|
@@ -6726,7 +6740,10 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
6726
6740
|
fs: { allow: resolveFsAllow(project.vitest.config.root, project.vitest.vite.config.configFile) }
|
|
6727
6741
|
},
|
|
6728
6742
|
environments: { ssr: { resolve: resolveOptions } },
|
|
6729
|
-
test: { name
|
|
6743
|
+
test: { name: {
|
|
6744
|
+
label: name,
|
|
6745
|
+
color
|
|
6746
|
+
} }
|
|
6730
6747
|
};
|
|
6731
6748
|
if (project.vitest._options.browser && viteConfig.test?.browser) {
|
|
6732
6749
|
viteConfig.test.browser = mergeConfig(viteConfig.test.browser, project.vitest._options.browser);
|
|
@@ -6900,6 +6917,7 @@ class TestProject {
|
|
|
6900
6917
|
/** @internal */ typechecker;
|
|
6901
6918
|
/** @internal */ _config;
|
|
6902
6919
|
/** @internal */ _vite;
|
|
6920
|
+
/** @internal */ _hash;
|
|
6903
6921
|
runner;
|
|
6904
6922
|
closingPromise;
|
|
6905
6923
|
testFilesList = null;
|
|
@@ -6914,6 +6932,17 @@ class TestProject {
|
|
|
6914
6932
|
this.globalConfig = vitest.config;
|
|
6915
6933
|
}
|
|
6916
6934
|
/**
|
|
6935
|
+
* The unique hash of this project. This value is consistent between the reruns.
|
|
6936
|
+
*
|
|
6937
|
+
* It is based on the root of the project (not consistent between OS) and its name.
|
|
6938
|
+
*/
|
|
6939
|
+
get hash() {
|
|
6940
|
+
if (!this._hash) {
|
|
6941
|
+
throw new Error("The server was not set. It means that `project.hash` was called before the Vite server was established.");
|
|
6942
|
+
}
|
|
6943
|
+
return this._hash;
|
|
6944
|
+
}
|
|
6945
|
+
/**
|
|
6917
6946
|
* Provide a value to the test context. This value will be available to all tests with `inject`.
|
|
6918
6947
|
*/
|
|
6919
6948
|
provide = (key, value) => {
|
|
@@ -6980,6 +7009,12 @@ class TestProject {
|
|
|
6980
7009
|
return this.config.name || "";
|
|
6981
7010
|
}
|
|
6982
7011
|
/**
|
|
7012
|
+
* The color used when reporting tasks of this project.
|
|
7013
|
+
*/
|
|
7014
|
+
get color() {
|
|
7015
|
+
return this.config.color;
|
|
7016
|
+
}
|
|
7017
|
+
/**
|
|
6983
7018
|
* Serialized project configuration. This is the config that tests receive.
|
|
6984
7019
|
*/
|
|
6985
7020
|
get serializedConfig() {
|
|
@@ -7137,7 +7172,7 @@ class TestProject {
|
|
|
7137
7172
|
expandDirectories: false
|
|
7138
7173
|
};
|
|
7139
7174
|
const files = await glob(include, globOptions);
|
|
7140
|
-
return files.map((file) => slash(
|
|
7175
|
+
return files.map((file) => slash(path.resolve(cwd, file)));
|
|
7141
7176
|
}
|
|
7142
7177
|
/**
|
|
7143
7178
|
* Test if a file matches the test globs. This does the actual glob matching if the test is not cached, unlike `isCachedTestFile`.
|
|
@@ -7147,14 +7182,14 @@ class TestProject {
|
|
|
7147
7182
|
return true;
|
|
7148
7183
|
}
|
|
7149
7184
|
const relativeId = relative(this.config.dir || this.config.root, moduleId);
|
|
7150
|
-
if (
|
|
7185
|
+
if (pm.isMatch(relativeId, this.config.exclude)) {
|
|
7151
7186
|
return false;
|
|
7152
7187
|
}
|
|
7153
|
-
if (
|
|
7188
|
+
if (pm.isMatch(relativeId, this.config.include)) {
|
|
7154
7189
|
this.markTestFile(moduleId);
|
|
7155
7190
|
return true;
|
|
7156
7191
|
}
|
|
7157
|
-
if (this.config.includeSource?.length &&
|
|
7192
|
+
if (this.config.includeSource?.length && pm.isMatch(relativeId, this.config.includeSource)) {
|
|
7158
7193
|
const code = source?.() || readFileSync(moduleId, "utf-8");
|
|
7159
7194
|
if (this.isInSourceTestCode(code)) {
|
|
7160
7195
|
this.markTestFile(moduleId);
|
|
@@ -7256,12 +7291,16 @@ class TestProject {
|
|
|
7256
7291
|
setServer(options, server) {
|
|
7257
7292
|
return this._configureServer(options, server);
|
|
7258
7293
|
}
|
|
7294
|
+
_setHash() {
|
|
7295
|
+
this._hash = generateHash(this._config.root + this._config.name);
|
|
7296
|
+
}
|
|
7259
7297
|
/** @internal */
|
|
7260
7298
|
async _configureServer(options, server) {
|
|
7261
7299
|
this._config = resolveConfig(this.vitest, {
|
|
7262
7300
|
...options,
|
|
7263
7301
|
coverage: this.vitest.config.coverage
|
|
7264
7302
|
}, server.config);
|
|
7303
|
+
this._setHash();
|
|
7265
7304
|
for (const _providedKey in this.config.provide) {
|
|
7266
7305
|
const providedKey = _providedKey;
|
|
7267
7306
|
this.provide(providedKey, this.config.provide[providedKey]);
|
|
@@ -7321,6 +7360,7 @@ class TestProject {
|
|
|
7321
7360
|
project.runner = vitest.runner;
|
|
7322
7361
|
project._vite = vitest.server;
|
|
7323
7362
|
project._config = vitest.config;
|
|
7363
|
+
project._setHash();
|
|
7324
7364
|
project._provideObject(vitest.config.provide);
|
|
7325
7365
|
return project;
|
|
7326
7366
|
}
|
|
@@ -7331,6 +7371,7 @@ class TestProject {
|
|
|
7331
7371
|
clone.runner = parent.runner;
|
|
7332
7372
|
clone._vite = parent._vite;
|
|
7333
7373
|
clone._config = config;
|
|
7374
|
+
clone._setHash();
|
|
7334
7375
|
clone._parent = parent;
|
|
7335
7376
|
clone._provideObject(config.provide);
|
|
7336
7377
|
return clone;
|
|
@@ -7363,183 +7404,513 @@ async function initializeProject(workspacePath, ctx, options) {
|
|
|
7363
7404
|
await createViteServer(config);
|
|
7364
7405
|
return project;
|
|
7365
7406
|
}
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
customReporterModule = await runner.executeId(path);
|
|
7371
|
-
} catch (customReporterModuleError) {
|
|
7372
|
-
throw new Error(`Failed to load custom Reporter from ${path}`, { cause: customReporterModuleError });
|
|
7373
|
-
}
|
|
7374
|
-
if (customReporterModule.default === null || customReporterModule.default === undefined) {
|
|
7375
|
-
throw new Error(`Custom reporter loaded from ${path} was not the default export`);
|
|
7376
|
-
}
|
|
7377
|
-
return customReporterModule.default;
|
|
7378
|
-
}
|
|
7379
|
-
function createReporters(reporterReferences, ctx) {
|
|
7380
|
-
const runner = ctx.runner;
|
|
7381
|
-
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
|
7382
|
-
if (Array.isArray(referenceOrInstance)) {
|
|
7383
|
-
const [reporterName, reporterOptions] = referenceOrInstance;
|
|
7384
|
-
if (reporterName === "html") {
|
|
7385
|
-
await ctx.packageInstaller.ensureInstalled("@vitest/ui", runner.root, ctx.version);
|
|
7386
|
-
const CustomReporter = await loadCustomReporterModule("@vitest/ui/reporter", runner);
|
|
7387
|
-
return new CustomReporter(reporterOptions);
|
|
7388
|
-
} else if (reporterName in ReportersMap) {
|
|
7389
|
-
const BuiltinReporter = ReportersMap[reporterName];
|
|
7390
|
-
return new BuiltinReporter(reporterOptions);
|
|
7391
|
-
} else {
|
|
7392
|
-
const CustomReporter = await loadCustomReporterModule(reporterName, runner);
|
|
7393
|
-
return new CustomReporter(reporterOptions);
|
|
7394
|
-
}
|
|
7395
|
-
}
|
|
7396
|
-
return referenceOrInstance;
|
|
7397
|
-
});
|
|
7398
|
-
return Promise.all(promisedReporters);
|
|
7399
|
-
}
|
|
7400
|
-
function createBenchmarkReporters(reporterReferences, runner) {
|
|
7401
|
-
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
|
7402
|
-
if (typeof referenceOrInstance === "string") {
|
|
7403
|
-
if (referenceOrInstance in BenchmarkReportsMap) {
|
|
7404
|
-
const BuiltinReporter = BenchmarkReportsMap[referenceOrInstance];
|
|
7405
|
-
return new BuiltinReporter();
|
|
7406
|
-
} else {
|
|
7407
|
-
const CustomReporter = await loadCustomReporterModule(referenceOrInstance, runner);
|
|
7408
|
-
return new CustomReporter();
|
|
7409
|
-
}
|
|
7410
|
-
}
|
|
7411
|
-
return referenceOrInstance;
|
|
7412
|
-
});
|
|
7413
|
-
return Promise.all(promisedReporters);
|
|
7414
|
-
}
|
|
7415
|
-
|
|
7416
|
-
function parseFilter(filter) {
|
|
7417
|
-
const colonIndex = filter.lastIndexOf(":");
|
|
7418
|
-
if (colonIndex === -1) {
|
|
7419
|
-
return { filename: filter };
|
|
7407
|
+
function generateHash(str) {
|
|
7408
|
+
let hash = 0;
|
|
7409
|
+
if (str.length === 0) {
|
|
7410
|
+
return `${hash}`;
|
|
7420
7411
|
}
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
lineNumber: Number.parseInt(lineNumber)
|
|
7426
|
-
};
|
|
7427
|
-
} else if (lineNumber.match(/^\d+-\d+$/)) {
|
|
7428
|
-
throw new RangeLocationFilterProvidedError(filter);
|
|
7429
|
-
} else {
|
|
7430
|
-
return { filename: filter };
|
|
7412
|
+
for (let i = 0; i < str.length; i++) {
|
|
7413
|
+
const char = str.charCodeAt(i);
|
|
7414
|
+
hash = (hash << 5) - hash + char;
|
|
7415
|
+
hash = hash & hash;
|
|
7431
7416
|
}
|
|
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;
|
|
7417
|
+
return `${hash}`;
|
|
7441
7418
|
}
|
|
7442
7419
|
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7420
|
+
async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projectsDefinition, names) {
|
|
7421
|
+
const { configFiles, projectConfigs, nonConfigDirectories } = await resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition);
|
|
7422
|
+
const overridesOptions = [
|
|
7423
|
+
"logHeapUsage",
|
|
7424
|
+
"allowOnly",
|
|
7425
|
+
"sequence",
|
|
7426
|
+
"testTimeout",
|
|
7427
|
+
"pool",
|
|
7428
|
+
"update",
|
|
7429
|
+
"globals",
|
|
7430
|
+
"expandSnapshotDiff",
|
|
7431
|
+
"disableConsoleIntercept",
|
|
7432
|
+
"retry",
|
|
7433
|
+
"testNamePattern",
|
|
7434
|
+
"passWithNoTests",
|
|
7435
|
+
"bail",
|
|
7436
|
+
"isolate",
|
|
7437
|
+
"printConsoleTrace",
|
|
7438
|
+
"inspect",
|
|
7439
|
+
"inspectBrk",
|
|
7440
|
+
"fileParallelism"
|
|
7441
|
+
];
|
|
7442
|
+
const cliOverrides = overridesOptions.reduce((acc, name) => {
|
|
7443
|
+
if (name in cliOptions) {
|
|
7444
|
+
acc[name] = cliOptions[name];
|
|
7452
7445
|
}
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7446
|
+
return acc;
|
|
7447
|
+
}, {});
|
|
7448
|
+
const projectPromises = [];
|
|
7449
|
+
const fileProjects = [...configFiles, ...nonConfigDirectories];
|
|
7450
|
+
const concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
|
|
7451
|
+
projectConfigs.forEach((options, index) => {
|
|
7452
|
+
const configRoot = workspaceConfigPath ? dirname(workspaceConfigPath) : vitest.config.root;
|
|
7453
|
+
const configFile = typeof options.extends === "string" ? resolve(configRoot, options.extends) : options.extends === true ? vitest.vite.config.configFile || false : false;
|
|
7454
|
+
const root = options.root ? resolve(configRoot, options.root) : vitest.config.root;
|
|
7455
|
+
projectPromises.push(concurrent(() => initializeProject(index, vitest, {
|
|
7456
|
+
...options,
|
|
7457
|
+
root,
|
|
7458
|
+
configFile,
|
|
7459
|
+
test: {
|
|
7460
|
+
...options.test,
|
|
7461
|
+
...cliOverrides
|
|
7457
7462
|
}
|
|
7458
|
-
|
|
7459
|
-
|
|
7463
|
+
})));
|
|
7464
|
+
});
|
|
7465
|
+
for (const path of fileProjects) {
|
|
7466
|
+
if (vitest.vite.config.configFile === path) {
|
|
7467
|
+
const project = getDefaultTestProject(vitest);
|
|
7468
|
+
if (project) {
|
|
7469
|
+
projectPromises.push(Promise.resolve(project));
|
|
7460
7470
|
}
|
|
7471
|
+
continue;
|
|
7461
7472
|
}
|
|
7462
|
-
|
|
7463
|
-
|
|
7473
|
+
const configFile = path.endsWith("/") ? false : path;
|
|
7474
|
+
const root = path.endsWith("/") ? path : dirname(path);
|
|
7475
|
+
projectPromises.push(concurrent(() => initializeProject(path, vitest, {
|
|
7476
|
+
root,
|
|
7477
|
+
configFile,
|
|
7478
|
+
test: cliOverrides
|
|
7479
|
+
})));
|
|
7464
7480
|
}
|
|
7465
|
-
|
|
7466
|
-
|
|
7481
|
+
if (!projectPromises.length) {
|
|
7482
|
+
throw new Error([
|
|
7483
|
+
"No projects were found. Make sure your configuration is correct. ",
|
|
7484
|
+
vitest.config.project.length ? `The filter matched no projects: ${vitest.config.project.join(", ")}. ` : "",
|
|
7485
|
+
`The projects definition: ${JSON.stringify(projectsDefinition, null, 4)}.`
|
|
7486
|
+
].join(""));
|
|
7467
7487
|
}
|
|
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));
|
|
7488
|
+
const resolvedProjectsPromises = await Promise.allSettled(projectPromises);
|
|
7489
|
+
const errors = [];
|
|
7490
|
+
const resolvedProjects = [];
|
|
7491
|
+
for (const result of resolvedProjectsPromises) {
|
|
7492
|
+
if (result.status === "rejected") {
|
|
7493
|
+
if (result.reason instanceof VitestFilteredOutProjectError) {
|
|
7494
|
+
continue;
|
|
7500
7495
|
}
|
|
7501
|
-
|
|
7502
|
-
return files;
|
|
7503
|
-
}
|
|
7504
|
-
clearCache(moduleId) {
|
|
7505
|
-
if (moduleId) {
|
|
7506
|
-
this._cachedSpecs.delete(moduleId);
|
|
7496
|
+
errors.push(result.reason);
|
|
7507
7497
|
} else {
|
|
7508
|
-
|
|
7498
|
+
resolvedProjects.push(result.value);
|
|
7509
7499
|
}
|
|
7510
7500
|
}
|
|
7511
|
-
|
|
7512
|
-
|
|
7501
|
+
if (errors.length) {
|
|
7502
|
+
throw new AggregateError(errors, "Failed to initialize projects. There were errors during projects setup. See below for more details.");
|
|
7513
7503
|
}
|
|
7514
|
-
|
|
7515
|
-
const
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7504
|
+
for (const project of resolvedProjects) {
|
|
7505
|
+
const name = project.name;
|
|
7506
|
+
if (names.has(name)) {
|
|
7507
|
+
const duplicate = resolvedProjects.find((p) => p.name === name && p !== project);
|
|
7508
|
+
const filesError = fileProjects.length ? [
|
|
7509
|
+
"\n\nYour config matched these files:\n",
|
|
7510
|
+
fileProjects.map((p) => ` - ${relative(vitest.config.root, p)}`).join("\n"),
|
|
7511
|
+
"\n\n"
|
|
7512
|
+
].join("") : " ";
|
|
7513
|
+
throw new Error([
|
|
7514
|
+
`Project name "${name}"`,
|
|
7515
|
+
project.vite.config.configFile ? ` from "${relative(vitest.config.root, project.vite.config.configFile)}"` : "",
|
|
7516
|
+
" is not unique.",
|
|
7517
|
+
duplicate?.vite.config.configFile ? ` The project is already defined by "${relative(vitest.config.root, duplicate.vite.config.configFile)}".` : "",
|
|
7518
|
+
filesError,
|
|
7519
|
+
"All projects should have unique names. Make sure your configuration is correct."
|
|
7520
|
+
].join(""));
|
|
7523
7521
|
}
|
|
7524
|
-
|
|
7522
|
+
names.add(name);
|
|
7525
7523
|
}
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
throw new GitNotFoundError();
|
|
7534
|
-
}
|
|
7535
|
-
this.vitest.config.related = Array.from(new Set(related));
|
|
7524
|
+
return resolveBrowserProjects(vitest, names, resolvedProjects);
|
|
7525
|
+
}
|
|
7526
|
+
async function resolveBrowserProjects(vitest, names, resolvedProjects) {
|
|
7527
|
+
const removeProjects = new Set();
|
|
7528
|
+
resolvedProjects.forEach((project) => {
|
|
7529
|
+
if (!project.config.browser.enabled) {
|
|
7530
|
+
return;
|
|
7536
7531
|
}
|
|
7537
|
-
const
|
|
7532
|
+
const instances = project.config.browser.instances || [];
|
|
7533
|
+
const browser = project.config.browser.name;
|
|
7534
|
+
if (instances.length === 0 && browser) {
|
|
7535
|
+
instances.push({
|
|
7536
|
+
browser,
|
|
7537
|
+
name: project.name ? `${project.name} (${browser})` : browser
|
|
7538
|
+
});
|
|
7539
|
+
vitest.logger.warn(withLabel("yellow", "Vitest", [
|
|
7540
|
+
`No browser "instances" were defined`,
|
|
7541
|
+
project.name ? ` for the "${project.name}" project. ` : ". ",
|
|
7542
|
+
`Running tests in "${project.config.browser.name}" browser. `,
|
|
7543
|
+
"The \"browser.name\" field is deprecated since Vitest 3. ",
|
|
7544
|
+
"Read more: https://vitest.dev/guide/browser/config#browser-instances"
|
|
7545
|
+
].filter(Boolean).join("")));
|
|
7546
|
+
}
|
|
7547
|
+
const originalName = project.config.name;
|
|
7548
|
+
const filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
|
|
7549
|
+
const newName = instance.name;
|
|
7550
|
+
return vitest.matchesProjectFilter(newName);
|
|
7551
|
+
});
|
|
7552
|
+
if (!filteredInstances.length) {
|
|
7553
|
+
removeProjects.add(project);
|
|
7554
|
+
return;
|
|
7555
|
+
}
|
|
7556
|
+
if (project.config.browser.providerOptions) {
|
|
7557
|
+
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.`));
|
|
7558
|
+
}
|
|
7559
|
+
filteredInstances.forEach((config, index) => {
|
|
7560
|
+
const browser = config.browser;
|
|
7561
|
+
if (!browser) {
|
|
7562
|
+
const nth = index + 1;
|
|
7563
|
+
const ending = nth === 2 ? "nd" : nth === 3 ? "rd" : "th";
|
|
7564
|
+
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.`);
|
|
7565
|
+
}
|
|
7566
|
+
const name = config.name;
|
|
7567
|
+
if (name == null) {
|
|
7568
|
+
throw new Error(`The browser configuration must have a "name" property. This is a bug in Vitest. Please, open a new issue with reproduction`);
|
|
7569
|
+
}
|
|
7570
|
+
if (names.has(name)) {
|
|
7571
|
+
throw new Error([
|
|
7572
|
+
`Cannot define a nested project for a ${browser} browser. The project name "${name}" was already defined. `,
|
|
7573
|
+
"If you have multiple instances for the same browser, make sure to define a custom \"name\". ",
|
|
7574
|
+
"All projects should have unique names. Make sure your configuration is correct."
|
|
7575
|
+
].join(""));
|
|
7576
|
+
}
|
|
7577
|
+
names.add(name);
|
|
7578
|
+
const clonedConfig = cloneConfig(project, config);
|
|
7579
|
+
clonedConfig.name = name;
|
|
7580
|
+
const clone = TestProject._cloneBrowserProject(project, clonedConfig);
|
|
7581
|
+
resolvedProjects.push(clone);
|
|
7582
|
+
});
|
|
7583
|
+
removeProjects.add(project);
|
|
7584
|
+
});
|
|
7585
|
+
resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
|
|
7586
|
+
const headedBrowserProjects = resolvedProjects.filter((project) => {
|
|
7587
|
+
return project.config.browser.enabled && !project.config.browser.headless;
|
|
7588
|
+
});
|
|
7589
|
+
if (headedBrowserProjects.length > 1) {
|
|
7590
|
+
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("");
|
|
7591
|
+
if (!isTTY) {
|
|
7592
|
+
throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
|
|
7593
|
+
}
|
|
7594
|
+
const prompts = await import('./index.X0nbfr6-.js').then(function (n) { return n.i; });
|
|
7595
|
+
const { projectName } = await prompts.default({
|
|
7596
|
+
type: "select",
|
|
7597
|
+
name: "projectName",
|
|
7598
|
+
choices: headedBrowserProjects.map((project) => ({
|
|
7599
|
+
title: project.name,
|
|
7600
|
+
value: project.name
|
|
7601
|
+
})),
|
|
7602
|
+
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.`
|
|
7603
|
+
});
|
|
7604
|
+
if (!projectName) {
|
|
7605
|
+
throw new Error("The test run was aborted.");
|
|
7606
|
+
}
|
|
7607
|
+
return resolvedProjects.filter((project) => project.name === projectName);
|
|
7608
|
+
}
|
|
7609
|
+
return resolvedProjects;
|
|
7610
|
+
}
|
|
7611
|
+
function cloneConfig(project, { browser,...config }) {
|
|
7612
|
+
const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name,...overrideConfig } = config;
|
|
7613
|
+
const currentConfig = project.config.browser;
|
|
7614
|
+
return mergeConfig({
|
|
7615
|
+
...deepClone(project.config),
|
|
7616
|
+
browser: {
|
|
7617
|
+
...project.config.browser,
|
|
7618
|
+
locators: locators ? { testIdAttribute: locators.testIdAttribute ?? currentConfig.locators.testIdAttribute } : project.config.browser.locators,
|
|
7619
|
+
viewport: viewport ?? currentConfig.viewport,
|
|
7620
|
+
testerHtmlPath: testerHtmlPath ?? currentConfig.testerHtmlPath,
|
|
7621
|
+
screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory,
|
|
7622
|
+
screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures,
|
|
7623
|
+
headless: headless ?? currentConfig.headless,
|
|
7624
|
+
name: browser,
|
|
7625
|
+
providerOptions: config,
|
|
7626
|
+
instances: undefined
|
|
7627
|
+
}
|
|
7628
|
+
}, overrideConfig);
|
|
7629
|
+
}
|
|
7630
|
+
async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition) {
|
|
7631
|
+
const projectsOptions = [];
|
|
7632
|
+
const projectsConfigFiles = [];
|
|
7633
|
+
const projectsGlobMatches = [];
|
|
7634
|
+
const nonConfigProjectDirectories = [];
|
|
7635
|
+
for (const definition of projectsDefinition) {
|
|
7636
|
+
if (typeof definition === "string") {
|
|
7637
|
+
const stringOption = definition.replace("<rootDir>", vitest.config.root);
|
|
7638
|
+
if (!isDynamicPattern(stringOption)) {
|
|
7639
|
+
const file = resolve(vitest.config.root, stringOption);
|
|
7640
|
+
if (!existsSync(file)) {
|
|
7641
|
+
const relativeWorkspaceConfigPath = workspaceConfigPath ? relative(vitest.config.root, workspaceConfigPath) : undefined;
|
|
7642
|
+
const note = workspaceConfigPath ? `Workspace config file "${relativeWorkspaceConfigPath}"` : "Projects definition";
|
|
7643
|
+
throw new Error(`${note} references a non-existing file or a directory: ${file}`);
|
|
7644
|
+
}
|
|
7645
|
+
const stats = await promises.stat(file);
|
|
7646
|
+
if (stats.isFile()) {
|
|
7647
|
+
projectsConfigFiles.push(file);
|
|
7648
|
+
} else if (stats.isDirectory()) {
|
|
7649
|
+
const configFile = await resolveDirectoryConfig(file);
|
|
7650
|
+
if (configFile) {
|
|
7651
|
+
projectsConfigFiles.push(configFile);
|
|
7652
|
+
} else {
|
|
7653
|
+
const directory = file[file.length - 1] === "/" ? file : `${file}/`;
|
|
7654
|
+
nonConfigProjectDirectories.push(directory);
|
|
7655
|
+
}
|
|
7656
|
+
} else {
|
|
7657
|
+
throw new TypeError(`Unexpected file type: ${file}`);
|
|
7658
|
+
}
|
|
7659
|
+
} else {
|
|
7660
|
+
projectsGlobMatches.push(stringOption);
|
|
7661
|
+
}
|
|
7662
|
+
} else if (typeof definition === "function") {
|
|
7663
|
+
projectsOptions.push(await definition({
|
|
7664
|
+
command: vitest.vite.config.command,
|
|
7665
|
+
mode: vitest.vite.config.mode,
|
|
7666
|
+
isPreview: false,
|
|
7667
|
+
isSsrBuild: false
|
|
7668
|
+
}));
|
|
7669
|
+
} else {
|
|
7670
|
+
projectsOptions.push(await definition);
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
if (projectsGlobMatches.length) {
|
|
7674
|
+
const globOptions = {
|
|
7675
|
+
absolute: true,
|
|
7676
|
+
dot: true,
|
|
7677
|
+
onlyFiles: false,
|
|
7678
|
+
cwd: vitest.config.root,
|
|
7679
|
+
expandDirectories: false,
|
|
7680
|
+
ignore: [
|
|
7681
|
+
"**/node_modules/**",
|
|
7682
|
+
"**/*.timestamp-*",
|
|
7683
|
+
"**/.DS_Store"
|
|
7684
|
+
]
|
|
7685
|
+
};
|
|
7686
|
+
const projectsFs = await glob(projectsGlobMatches, globOptions);
|
|
7687
|
+
await Promise.all(projectsFs.map(async (path) => {
|
|
7688
|
+
if (path.endsWith("/")) {
|
|
7689
|
+
const configFile = await resolveDirectoryConfig(path);
|
|
7690
|
+
if (configFile) {
|
|
7691
|
+
projectsConfigFiles.push(configFile);
|
|
7692
|
+
} else {
|
|
7693
|
+
nonConfigProjectDirectories.push(path);
|
|
7694
|
+
}
|
|
7695
|
+
} else {
|
|
7696
|
+
projectsConfigFiles.push(path);
|
|
7697
|
+
}
|
|
7698
|
+
}));
|
|
7699
|
+
}
|
|
7700
|
+
const projectConfigFiles = Array.from(new Set(projectsConfigFiles));
|
|
7701
|
+
return {
|
|
7702
|
+
projectConfigs: projectsOptions,
|
|
7703
|
+
nonConfigDirectories: nonConfigProjectDirectories,
|
|
7704
|
+
configFiles: projectConfigFiles
|
|
7705
|
+
};
|
|
7706
|
+
}
|
|
7707
|
+
async function resolveDirectoryConfig(directory) {
|
|
7708
|
+
const files = new Set(await promises.readdir(directory));
|
|
7709
|
+
const configFile = configFiles.find((file) => files.has(file));
|
|
7710
|
+
if (configFile) {
|
|
7711
|
+
return resolve(directory, configFile);
|
|
7712
|
+
}
|
|
7713
|
+
return null;
|
|
7714
|
+
}
|
|
7715
|
+
function getDefaultTestProject(vitest) {
|
|
7716
|
+
const filter = vitest.config.project;
|
|
7717
|
+
const project = vitest._ensureRootProject();
|
|
7718
|
+
if (!filter.length) {
|
|
7719
|
+
return project;
|
|
7720
|
+
}
|
|
7721
|
+
const hasProjects = getPotentialProjectNames(project).some((p) => vitest.matchesProjectFilter(p));
|
|
7722
|
+
if (hasProjects) {
|
|
7723
|
+
return project;
|
|
7724
|
+
}
|
|
7725
|
+
return null;
|
|
7726
|
+
}
|
|
7727
|
+
function getPotentialProjectNames(project) {
|
|
7728
|
+
const names = [project.name];
|
|
7729
|
+
if (project.config.browser.instances) {
|
|
7730
|
+
names.push(...project.config.browser.instances.map((i) => i.name));
|
|
7731
|
+
} else if (project.config.browser.name) {
|
|
7732
|
+
names.push(project.config.browser.name);
|
|
7733
|
+
}
|
|
7734
|
+
return names;
|
|
7735
|
+
}
|
|
7736
|
+
|
|
7737
|
+
async function loadCustomReporterModule(path, runner) {
|
|
7738
|
+
let customReporterModule;
|
|
7739
|
+
try {
|
|
7740
|
+
customReporterModule = await runner.executeId(path);
|
|
7741
|
+
} catch (customReporterModuleError) {
|
|
7742
|
+
throw new Error(`Failed to load custom Reporter from ${path}`, { cause: customReporterModuleError });
|
|
7743
|
+
}
|
|
7744
|
+
if (customReporterModule.default === null || customReporterModule.default === undefined) {
|
|
7745
|
+
throw new Error(`Custom reporter loaded from ${path} was not the default export`);
|
|
7746
|
+
}
|
|
7747
|
+
return customReporterModule.default;
|
|
7748
|
+
}
|
|
7749
|
+
function createReporters(reporterReferences, ctx) {
|
|
7750
|
+
const runner = ctx.runner;
|
|
7751
|
+
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
|
7752
|
+
if (Array.isArray(referenceOrInstance)) {
|
|
7753
|
+
const [reporterName, reporterOptions] = referenceOrInstance;
|
|
7754
|
+
if (reporterName === "html") {
|
|
7755
|
+
await ctx.packageInstaller.ensureInstalled("@vitest/ui", runner.root, ctx.version);
|
|
7756
|
+
const CustomReporter = await loadCustomReporterModule("@vitest/ui/reporter", runner);
|
|
7757
|
+
return new CustomReporter(reporterOptions);
|
|
7758
|
+
} else if (reporterName in ReportersMap) {
|
|
7759
|
+
const BuiltinReporter = ReportersMap[reporterName];
|
|
7760
|
+
return new BuiltinReporter(reporterOptions);
|
|
7761
|
+
} else {
|
|
7762
|
+
const CustomReporter = await loadCustomReporterModule(reporterName, runner);
|
|
7763
|
+
return new CustomReporter(reporterOptions);
|
|
7764
|
+
}
|
|
7765
|
+
}
|
|
7766
|
+
return referenceOrInstance;
|
|
7767
|
+
});
|
|
7768
|
+
return Promise.all(promisedReporters);
|
|
7769
|
+
}
|
|
7770
|
+
function createBenchmarkReporters(reporterReferences, runner) {
|
|
7771
|
+
const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
|
|
7772
|
+
if (typeof referenceOrInstance === "string") {
|
|
7773
|
+
if (referenceOrInstance in BenchmarkReportsMap) {
|
|
7774
|
+
const BuiltinReporter = BenchmarkReportsMap[referenceOrInstance];
|
|
7775
|
+
return new BuiltinReporter();
|
|
7776
|
+
} else {
|
|
7777
|
+
const CustomReporter = await loadCustomReporterModule(referenceOrInstance, runner);
|
|
7778
|
+
return new CustomReporter();
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7781
|
+
return referenceOrInstance;
|
|
7782
|
+
});
|
|
7783
|
+
return Promise.all(promisedReporters);
|
|
7784
|
+
}
|
|
7785
|
+
|
|
7786
|
+
function parseFilter(filter) {
|
|
7787
|
+
const colonIndex = filter.lastIndexOf(":");
|
|
7788
|
+
if (colonIndex === -1) {
|
|
7789
|
+
return { filename: filter };
|
|
7790
|
+
}
|
|
7791
|
+
const [parsedFilename, lineNumber] = [filter.substring(0, colonIndex), filter.substring(colonIndex + 1)];
|
|
7792
|
+
if (lineNumber.match(/^\d+$/)) {
|
|
7793
|
+
return {
|
|
7794
|
+
filename: parsedFilename,
|
|
7795
|
+
lineNumber: Number.parseInt(lineNumber)
|
|
7796
|
+
};
|
|
7797
|
+
} else if (lineNumber.match(/^\d+-\d+$/)) {
|
|
7798
|
+
throw new RangeLocationFilterProvidedError(filter);
|
|
7799
|
+
} else {
|
|
7800
|
+
return { filename: filter };
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
7803
|
+
function groupFilters(filters) {
|
|
7804
|
+
const groupedFilters_ = groupBy(filters, (f) => f.filename);
|
|
7805
|
+
const groupedFilters = Object.fromEntries(Object.entries(groupedFilters_).map((entry) => {
|
|
7806
|
+
const [filename, filters] = entry;
|
|
7807
|
+
const testLocations = filters.map((f) => f.lineNumber);
|
|
7808
|
+
return [filename, testLocations.filter((l) => l !== undefined)];
|
|
7809
|
+
}));
|
|
7810
|
+
return groupedFilters;
|
|
7811
|
+
}
|
|
7812
|
+
|
|
7813
|
+
class VitestSpecifications {
|
|
7814
|
+
_cachedSpecs = new Map();
|
|
7815
|
+
constructor(vitest) {
|
|
7816
|
+
this.vitest = vitest;
|
|
7817
|
+
}
|
|
7818
|
+
getModuleSpecifications(moduleId) {
|
|
7819
|
+
const _cached = this.getCachedSpecifications(moduleId);
|
|
7820
|
+
if (_cached) {
|
|
7821
|
+
return _cached;
|
|
7822
|
+
}
|
|
7823
|
+
const specs = [];
|
|
7824
|
+
for (const project of this.vitest.projects) {
|
|
7825
|
+
if (project._isCachedTestFile(moduleId)) {
|
|
7826
|
+
specs.push(project.createSpecification(moduleId));
|
|
7827
|
+
}
|
|
7828
|
+
if (project._isCachedTypecheckFile(moduleId)) {
|
|
7829
|
+
specs.push(project.createSpecification(moduleId, [], "typescript"));
|
|
7830
|
+
}
|
|
7831
|
+
}
|
|
7832
|
+
specs.forEach((spec) => this.ensureSpecificationCached(spec));
|
|
7833
|
+
return specs;
|
|
7834
|
+
}
|
|
7835
|
+
async getRelevantTestSpecifications(filters = []) {
|
|
7836
|
+
return this.filterTestsBySource(await this.globTestSpecifications(filters));
|
|
7837
|
+
}
|
|
7838
|
+
async globTestSpecifications(filters = []) {
|
|
7839
|
+
const files = [];
|
|
7840
|
+
const dir = process.cwd();
|
|
7841
|
+
const parsedFilters = filters.map((f) => parseFilter(f));
|
|
7842
|
+
if (!this.vitest.config.includeTaskLocation && parsedFilters.some((f) => f.lineNumber !== undefined)) {
|
|
7843
|
+
throw new IncludeTaskLocationDisabledError();
|
|
7844
|
+
}
|
|
7845
|
+
const testLines = groupFilters(parsedFilters.map((f) => ({
|
|
7846
|
+
...f,
|
|
7847
|
+
filename: resolve(dir, f.filename)
|
|
7848
|
+
})));
|
|
7849
|
+
const testLocHasMatch = {};
|
|
7850
|
+
await Promise.all(this.vitest.projects.map(async (project) => {
|
|
7851
|
+
const { testFiles, typecheckTestFiles } = await project.globTestFiles(parsedFilters.map((f) => f.filename));
|
|
7852
|
+
testFiles.forEach((file) => {
|
|
7853
|
+
const lines = testLines[file];
|
|
7854
|
+
testLocHasMatch[file] = true;
|
|
7855
|
+
const spec = project.createSpecification(file, lines);
|
|
7856
|
+
this.ensureSpecificationCached(spec);
|
|
7857
|
+
files.push(spec);
|
|
7858
|
+
});
|
|
7859
|
+
typecheckTestFiles.forEach((file) => {
|
|
7860
|
+
const lines = testLines[file];
|
|
7861
|
+
testLocHasMatch[file] = true;
|
|
7862
|
+
const spec = project.createSpecification(file, lines, "typescript");
|
|
7863
|
+
this.ensureSpecificationCached(spec);
|
|
7864
|
+
files.push(spec);
|
|
7865
|
+
});
|
|
7866
|
+
}));
|
|
7867
|
+
Object.entries(testLines).forEach(([filepath, loc]) => {
|
|
7868
|
+
if (loc.length !== 0 && !testLocHasMatch[filepath]) {
|
|
7869
|
+
throw new LocationFilterFileNotFoundError(relative(dir, filepath));
|
|
7870
|
+
}
|
|
7871
|
+
});
|
|
7872
|
+
return files;
|
|
7873
|
+
}
|
|
7874
|
+
clearCache(moduleId) {
|
|
7875
|
+
if (moduleId) {
|
|
7876
|
+
this._cachedSpecs.delete(moduleId);
|
|
7877
|
+
} else {
|
|
7878
|
+
this._cachedSpecs.clear();
|
|
7879
|
+
}
|
|
7880
|
+
}
|
|
7881
|
+
getCachedSpecifications(moduleId) {
|
|
7882
|
+
return this._cachedSpecs.get(moduleId);
|
|
7883
|
+
}
|
|
7884
|
+
ensureSpecificationCached(spec) {
|
|
7885
|
+
const file = spec.moduleId;
|
|
7886
|
+
const specs = this._cachedSpecs.get(file) || [];
|
|
7887
|
+
const index = specs.findIndex((_s) => _s.project === spec.project && _s.pool === spec.pool);
|
|
7888
|
+
if (index === -1) {
|
|
7889
|
+
specs.push(spec);
|
|
7890
|
+
this._cachedSpecs.set(file, specs);
|
|
7891
|
+
} else {
|
|
7892
|
+
specs.splice(index, 1, spec);
|
|
7893
|
+
}
|
|
7894
|
+
return specs;
|
|
7895
|
+
}
|
|
7896
|
+
async filterTestsBySource(specs) {
|
|
7897
|
+
if (this.vitest.config.changed && !this.vitest.config.related) {
|
|
7898
|
+
const { VitestGit } = await import('./git.DXfdBEfR.js');
|
|
7899
|
+
const vitestGit = new VitestGit(this.vitest.config.root);
|
|
7900
|
+
const related = await vitestGit.findChangedFiles({ changedSince: this.vitest.config.changed });
|
|
7901
|
+
if (!related) {
|
|
7902
|
+
process.exitCode = 1;
|
|
7903
|
+
throw new GitNotFoundError();
|
|
7904
|
+
}
|
|
7905
|
+
this.vitest.config.related = Array.from(new Set(related));
|
|
7906
|
+
}
|
|
7907
|
+
const related = this.vitest.config.related;
|
|
7538
7908
|
if (!related) {
|
|
7539
7909
|
return specs;
|
|
7540
7910
|
}
|
|
7541
7911
|
const forceRerunTriggers = this.vitest.config.forceRerunTriggers;
|
|
7542
|
-
|
|
7912
|
+
const matcher = forceRerunTriggers.length ? pm(forceRerunTriggers) : undefined;
|
|
7913
|
+
if (matcher && related.some((file) => matcher(file))) {
|
|
7543
7914
|
return specs;
|
|
7544
7915
|
}
|
|
7545
7916
|
if (!this.vitest.config.watch && !related.length) {
|
|
@@ -7997,721 +8368,434 @@ class StateManager {
|
|
|
7997
8368
|
taskFileMap = new WeakMap();
|
|
7998
8369
|
errorsSet = new Set();
|
|
7999
8370
|
processTimeoutCauses = new Set();
|
|
8000
|
-
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
|
|
8371
|
+
reportedTasksMap = new WeakMap();
|
|
8372
|
+
blobs;
|
|
8373
|
+
catchError(err, type) {
|
|
8374
|
+
if (isAggregateError(err)) {
|
|
8375
|
+
return err.errors.forEach((error) => this.catchError(error, type));
|
|
8376
|
+
}
|
|
8377
|
+
if (err === Object(err)) {
|
|
8378
|
+
err.type = type;
|
|
8379
|
+
} else {
|
|
8380
|
+
err = {
|
|
8381
|
+
type,
|
|
8382
|
+
message: err
|
|
8239
8383
|
};
|
|
8240
|
-
if (event.endsWith("-start")) {
|
|
8241
|
-
await this.vitest.report("onHookStart", hook);
|
|
8242
|
-
} else {
|
|
8243
|
-
await this.vitest.report("onHookEnd", hook);
|
|
8244
|
-
}
|
|
8245
8384
|
}
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
if (
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
await this.reportChildren(child.children);
|
|
8255
|
-
await this.vitest.report("onTestSuiteResult", child);
|
|
8385
|
+
const _err = err;
|
|
8386
|
+
if (_err && typeof _err === "object" && _err.code === "VITEST_PENDING") {
|
|
8387
|
+
const task = this.idMap.get(_err.taskId);
|
|
8388
|
+
if (task) {
|
|
8389
|
+
task.mode = "skip";
|
|
8390
|
+
task.result ??= { state: "skip" };
|
|
8391
|
+
task.result.state = "skip";
|
|
8392
|
+
task.result.note = _err.note;
|
|
8256
8393
|
}
|
|
8394
|
+
return;
|
|
8257
8395
|
}
|
|
8396
|
+
this.errorsSet.add(err);
|
|
8258
8397
|
}
|
|
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;
|
|
8398
|
+
clearErrors() {
|
|
8399
|
+
this.errorsSet.clear();
|
|
8273
8400
|
}
|
|
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;
|
|
8401
|
+
getUnhandledErrors() {
|
|
8402
|
+
return Array.from(this.errorsSet.values());
|
|
8283
8403
|
}
|
|
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;
|
|
8404
|
+
addProcessTimeoutCause(cause) {
|
|
8405
|
+
this.processTimeoutCauses.add(cause);
|
|
8300
8406
|
}
|
|
8301
|
-
|
|
8302
|
-
this.
|
|
8407
|
+
getProcessTimeoutCauses() {
|
|
8408
|
+
return Array.from(this.processTimeoutCauses.values());
|
|
8409
|
+
}
|
|
8410
|
+
getPaths() {
|
|
8411
|
+
return Array.from(this.pathsSet);
|
|
8303
8412
|
}
|
|
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
8413
|
/**
|
|
8347
|
-
*
|
|
8414
|
+
* Return files that were running or collected.
|
|
8348
8415
|
*/
|
|
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;
|
|
8416
|
+
getFiles(keys) {
|
|
8417
|
+
if (keys) {
|
|
8418
|
+
return keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local);
|
|
8367
8419
|
}
|
|
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;
|
|
8420
|
+
return Array.from(this.filesMap.values()).flat().filter((file) => !file.local).sort((f1, f2) => {
|
|
8421
|
+
if (f1.meta?.typecheck && f2.meta?.typecheck) {
|
|
8422
|
+
return 0;
|
|
8379
8423
|
}
|
|
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
|
-
});
|
|
8424
|
+
if (f1.meta?.typecheck) {
|
|
8425
|
+
return -1;
|
|
8391
8426
|
}
|
|
8392
|
-
|
|
8393
|
-
|
|
8427
|
+
return 1;
|
|
8428
|
+
});
|
|
8429
|
+
}
|
|
8430
|
+
getTestModules(keys) {
|
|
8431
|
+
return this.getFiles(keys).map((file) => this.getReportedEntity(file));
|
|
8432
|
+
}
|
|
8433
|
+
getFilepaths() {
|
|
8434
|
+
return Array.from(this.filesMap.keys());
|
|
8435
|
+
}
|
|
8436
|
+
getFailedFilepaths() {
|
|
8437
|
+
return this.getFiles().filter((i) => i.result?.state === "fail").map((i) => i.filepath);
|
|
8438
|
+
}
|
|
8439
|
+
collectPaths(paths = []) {
|
|
8440
|
+
paths.forEach((path) => {
|
|
8441
|
+
this.pathsSet.add(path);
|
|
8442
|
+
});
|
|
8443
|
+
}
|
|
8444
|
+
collectFiles(project, files = []) {
|
|
8445
|
+
files.forEach((file) => {
|
|
8446
|
+
const existing = this.filesMap.get(file.filepath) || [];
|
|
8447
|
+
const otherFiles = existing.filter((i) => i.projectName !== file.projectName || i.meta.typecheck !== file.meta.typecheck);
|
|
8448
|
+
const currentFile = existing.find((i) => i.projectName === file.projectName);
|
|
8449
|
+
if (currentFile) {
|
|
8450
|
+
file.logs = currentFile.logs;
|
|
8394
8451
|
}
|
|
8395
|
-
|
|
8396
|
-
|
|
8452
|
+
otherFiles.push(file);
|
|
8453
|
+
this.filesMap.set(file.filepath, otherFiles);
|
|
8454
|
+
this.updateId(file, project);
|
|
8455
|
+
});
|
|
8397
8456
|
}
|
|
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
|
|
8457
|
+
clearFiles(project, paths = []) {
|
|
8458
|
+
paths.forEach((path) => {
|
|
8459
|
+
const files = this.filesMap.get(path);
|
|
8460
|
+
const fileTask = createFileTask(path, project.config.root, project.config.name);
|
|
8461
|
+
fileTask.local = true;
|
|
8462
|
+
TestModule.register(fileTask, project);
|
|
8463
|
+
this.idMap.set(fileTask.id, fileTask);
|
|
8464
|
+
if (!files) {
|
|
8465
|
+
this.filesMap.set(path, [fileTask]);
|
|
8466
|
+
return;
|
|
8442
8467
|
}
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8448
|
-
if (project) {
|
|
8449
|
-
projectPromises.push(Promise.resolve(project));
|
|
8468
|
+
const filtered = files.filter((file) => file.projectName !== project.config.name);
|
|
8469
|
+
if (!filtered.length) {
|
|
8470
|
+
this.filesMap.set(path, [fileTask]);
|
|
8471
|
+
} else {
|
|
8472
|
+
this.filesMap.set(path, [...filtered, fileTask]);
|
|
8450
8473
|
}
|
|
8451
|
-
|
|
8474
|
+
});
|
|
8475
|
+
}
|
|
8476
|
+
updateId(task, project) {
|
|
8477
|
+
if (this.idMap.get(task.id) === task) {
|
|
8478
|
+
return;
|
|
8479
|
+
}
|
|
8480
|
+
if (task.type === "suite" && "filepath" in task) {
|
|
8481
|
+
TestModule.register(task, project);
|
|
8482
|
+
} else if (task.type === "suite") {
|
|
8483
|
+
TestSuite.register(task, project);
|
|
8484
|
+
} else {
|
|
8485
|
+
TestCase.register(task, project);
|
|
8486
|
+
}
|
|
8487
|
+
this.idMap.set(task.id, task);
|
|
8488
|
+
if (task.type === "suite") {
|
|
8489
|
+
task.tasks.forEach((task) => {
|
|
8490
|
+
this.updateId(task, project);
|
|
8491
|
+
});
|
|
8452
8492
|
}
|
|
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
8493
|
}
|
|
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(""));
|
|
8494
|
+
getReportedEntity(task) {
|
|
8495
|
+
return this.reportedTasksMap.get(task);
|
|
8467
8496
|
}
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8497
|
+
updateTasks(packs) {
|
|
8498
|
+
for (const [id, result, meta] of packs) {
|
|
8499
|
+
const task = this.idMap.get(id);
|
|
8500
|
+
if (task) {
|
|
8501
|
+
task.result = result;
|
|
8502
|
+
task.meta = meta;
|
|
8503
|
+
if (result?.state === "skip") {
|
|
8504
|
+
task.mode = "skip";
|
|
8505
|
+
}
|
|
8475
8506
|
}
|
|
8476
|
-
errors.push(result.reason);
|
|
8477
|
-
} else {
|
|
8478
|
-
resolvedProjects.push(result.value);
|
|
8479
8507
|
}
|
|
8480
8508
|
}
|
|
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(""));
|
|
8509
|
+
updateUserLog(log) {
|
|
8510
|
+
const task = log.taskId && this.idMap.get(log.taskId);
|
|
8511
|
+
if (task) {
|
|
8512
|
+
if (!task.logs) {
|
|
8513
|
+
task.logs = [];
|
|
8514
|
+
}
|
|
8515
|
+
task.logs.push(log);
|
|
8501
8516
|
}
|
|
8502
|
-
names.add(name);
|
|
8503
8517
|
}
|
|
8504
|
-
|
|
8518
|
+
getCountOfFailedTests() {
|
|
8519
|
+
return Array.from(this.idMap.values()).filter((t) => t.result?.state === "fail").length;
|
|
8520
|
+
}
|
|
8521
|
+
cancelFiles(files, project) {
|
|
8522
|
+
this.collectFiles(project, files.map((filepath) => createFileTask(filepath, project.config.root, project.config.name)));
|
|
8523
|
+
}
|
|
8505
8524
|
}
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
const
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8525
|
+
|
|
8526
|
+
class TestRun {
|
|
8527
|
+
constructor(vitest) {
|
|
8528
|
+
this.vitest = vitest;
|
|
8529
|
+
}
|
|
8530
|
+
async start(specifications) {
|
|
8531
|
+
const filepaths = specifications.map((spec) => spec.moduleId);
|
|
8532
|
+
this.vitest.state.collectPaths(filepaths);
|
|
8533
|
+
await this.vitest.report("onPathsCollected", Array.from(new Set(filepaths)));
|
|
8534
|
+
await this.vitest.report("onSpecsCollected", specifications.map((spec) => spec.toJSON()));
|
|
8535
|
+
await this.vitest.report("onTestRunStart", [...specifications]);
|
|
8536
|
+
}
|
|
8537
|
+
async enqueued(project, file) {
|
|
8538
|
+
this.vitest.state.collectFiles(project, [file]);
|
|
8539
|
+
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8540
|
+
await this.vitest.report("onTestModuleQueued", testModule);
|
|
8541
|
+
}
|
|
8542
|
+
async collected(project, files) {
|
|
8543
|
+
this.vitest.state.collectFiles(project, files);
|
|
8544
|
+
await Promise.all([this.vitest.report("onCollected", files), ...files.map((file) => {
|
|
8545
|
+
const testModule = this.vitest.state.getReportedEntity(file);
|
|
8546
|
+
return this.vitest.report("onTestModuleCollected", testModule);
|
|
8547
|
+
})]);
|
|
8548
|
+
}
|
|
8549
|
+
async log(log) {
|
|
8550
|
+
this.vitest.state.updateUserLog(log);
|
|
8551
|
+
await this.vitest.report("onUserConsoleLog", log);
|
|
8552
|
+
}
|
|
8553
|
+
async updated(update, events) {
|
|
8554
|
+
this.vitest.state.updateTasks(update);
|
|
8555
|
+
await this.vitest.report("onTaskUpdate", update);
|
|
8556
|
+
for (const [id, event] of events) {
|
|
8557
|
+
await this.reportEvent(id, event).catch((error) => {
|
|
8558
|
+
this.vitest.state.catchError(serializeError(error), "Unhandled Reporter Error");
|
|
8518
8559
|
});
|
|
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
|
-
}
|
|
8527
|
-
const originalName = project.config.name;
|
|
8528
|
-
const filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
|
|
8529
|
-
const newName = instance.name;
|
|
8530
|
-
return vitest.matchesProjectFilter(newName);
|
|
8531
|
-
});
|
|
8532
|
-
if (!filteredInstances.length) {
|
|
8533
|
-
removeProjects.add(project);
|
|
8534
|
-
return;
|
|
8535
|
-
}
|
|
8536
|
-
if (project.config.browser.providerOptions) {
|
|
8537
|
-
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.`));
|
|
8538
8560
|
}
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
if (
|
|
8548
|
-
|
|
8549
|
-
}
|
|
8550
|
-
if (names.has(name)) {
|
|
8551
|
-
throw new Error([
|
|
8552
|
-
`Cannot define a nested project for a ${browser} browser. The project name "${name}" was already defined. `,
|
|
8553
|
-
"If you have multiple instances for the same browser, make sure to define a custom \"name\". ",
|
|
8554
|
-
"All projects in a workspace should have unique names. Make sure your configuration is correct."
|
|
8555
|
-
].join(""));
|
|
8561
|
+
}
|
|
8562
|
+
async end(specifications, errors, coverage) {
|
|
8563
|
+
const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null);
|
|
8564
|
+
const files = modules.map((m) => m.task);
|
|
8565
|
+
const state = this.vitest.isCancelling ? "interrupted" : process.exitCode ? "failed" : "passed";
|
|
8566
|
+
try {
|
|
8567
|
+
await Promise.all([this.vitest.report("onTestRunEnd", modules, [...errors], state), this.vitest.report("onFinished", files, errors, coverage)]);
|
|
8568
|
+
} finally {
|
|
8569
|
+
if (coverage) {
|
|
8570
|
+
await this.vitest.report("onCoverage", coverage);
|
|
8556
8571
|
}
|
|
8557
|
-
names.add(name);
|
|
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
8572
|
}
|
|
8587
|
-
return resolvedProjects.filter((project) => project.name === projectName);
|
|
8588
8573
|
}
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8574
|
+
async reportEvent(id, event) {
|
|
8575
|
+
const task = this.vitest.state.idMap.get(id);
|
|
8576
|
+
const entity = task && this.vitest.state.getReportedEntity(task);
|
|
8577
|
+
assert$1(task && entity, `Entity must be found for task ${task?.name || id}`);
|
|
8578
|
+
if (event === "suite-prepare" && entity.type === "suite") {
|
|
8579
|
+
return await this.vitest.report("onTestSuiteReady", entity);
|
|
8580
|
+
}
|
|
8581
|
+
if (event === "suite-prepare" && entity.type === "module") {
|
|
8582
|
+
return await this.vitest.report("onTestModuleStart", entity);
|
|
8583
|
+
}
|
|
8584
|
+
if (event === "suite-finished") {
|
|
8585
|
+
assert$1(entity.type === "suite" || entity.type === "module", "Entity type must be suite or module");
|
|
8586
|
+
if (entity.state() === "skipped") {
|
|
8587
|
+
await this.reportChildren(entity.children);
|
|
8588
|
+
}
|
|
8589
|
+
if (entity.type === "module") {
|
|
8590
|
+
await this.vitest.report("onTestModuleEnd", entity);
|
|
8591
|
+
} else {
|
|
8592
|
+
await this.vitest.report("onTestSuiteResult", entity);
|
|
8593
|
+
}
|
|
8594
|
+
return;
|
|
8607
8595
|
}
|
|
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
|
-
}
|
|
8596
|
+
if (event === "test-prepare" && entity.type === "test") {
|
|
8597
|
+
return await this.vitest.report("onTestCaseReady", entity);
|
|
8598
|
+
}
|
|
8599
|
+
if (event === "test-finished" && entity.type === "test") {
|
|
8600
|
+
return await this.vitest.report("onTestCaseResult", entity);
|
|
8601
|
+
}
|
|
8602
|
+
if (event.startsWith("before-hook") || event.startsWith("after-hook")) {
|
|
8603
|
+
const isBefore = event.startsWith("before-hook");
|
|
8604
|
+
const hook = entity.type === "test" ? {
|
|
8605
|
+
name: isBefore ? "beforeEach" : "afterEach",
|
|
8606
|
+
entity
|
|
8607
|
+
} : {
|
|
8608
|
+
name: isBefore ? "beforeAll" : "afterAll",
|
|
8609
|
+
entity
|
|
8610
|
+
};
|
|
8611
|
+
if (event.endsWith("-start")) {
|
|
8612
|
+
await this.vitest.report("onHookStart", hook);
|
|
8639
8613
|
} else {
|
|
8640
|
-
|
|
8614
|
+
await this.vitest.report("onHookEnd", hook);
|
|
8641
8615
|
}
|
|
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
8616
|
}
|
|
8652
8617
|
}
|
|
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
|
-
}
|
|
8618
|
+
async reportChildren(children) {
|
|
8619
|
+
for (const child of children) {
|
|
8620
|
+
if (child.type === "test") {
|
|
8621
|
+
await this.vitest.report("onTestCaseReady", child);
|
|
8622
|
+
await this.vitest.report("onTestCaseResult", child);
|
|
8675
8623
|
} else {
|
|
8676
|
-
|
|
8624
|
+
await this.vitest.report("onTestSuiteReady", child);
|
|
8625
|
+
await this.reportChildren(child.children);
|
|
8626
|
+
await this.vitest.report("onTestSuiteResult", child);
|
|
8677
8627
|
}
|
|
8678
|
-
}
|
|
8628
|
+
}
|
|
8679
8629
|
}
|
|
8680
|
-
const projectConfigFiles = Array.from(new Set(workspaceConfigFiles));
|
|
8681
|
-
return {
|
|
8682
|
-
projectConfigs: projectsOptions,
|
|
8683
|
-
nonConfigDirectories: nonConfigProjectDirectories,
|
|
8684
|
-
configFiles: projectConfigFiles
|
|
8685
|
-
};
|
|
8686
8630
|
}
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8631
|
+
|
|
8632
|
+
class VitestWatcher {
|
|
8633
|
+
/**
|
|
8634
|
+
* Modules that will be invalidated on the next run.
|
|
8635
|
+
*/
|
|
8636
|
+
invalidates = new Set();
|
|
8637
|
+
/**
|
|
8638
|
+
* Test files that have changed and need to be rerun.
|
|
8639
|
+
*/
|
|
8640
|
+
changedTests = new Set();
|
|
8641
|
+
_onRerun = [];
|
|
8642
|
+
constructor(vitest) {
|
|
8643
|
+
this.vitest = vitest;
|
|
8692
8644
|
}
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8645
|
+
/**
|
|
8646
|
+
* Register a handler that will be called when test files need to be rerun.
|
|
8647
|
+
* The callback can receive several files in case the changed file is imported by several test files.
|
|
8648
|
+
* Several invocations of this method will add multiple handlers.
|
|
8649
|
+
* @internal
|
|
8650
|
+
*/
|
|
8651
|
+
onWatcherRerun(cb) {
|
|
8652
|
+
this._onRerun.push(cb);
|
|
8653
|
+
return this;
|
|
8700
8654
|
}
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8655
|
+
unregisterWatcher = noop;
|
|
8656
|
+
registerWatcher() {
|
|
8657
|
+
const watcher = this.vitest.vite.watcher;
|
|
8658
|
+
if (this.vitest.config.forceRerunTriggers.length) {
|
|
8659
|
+
watcher.add(this.vitest.config.forceRerunTriggers);
|
|
8660
|
+
}
|
|
8661
|
+
watcher.on("change", this.onChange);
|
|
8662
|
+
watcher.on("unlink", this.onUnlink);
|
|
8663
|
+
watcher.on("add", this.onAdd);
|
|
8664
|
+
this.unregisterWatcher = () => {
|
|
8665
|
+
watcher.off("change", this.onChange);
|
|
8666
|
+
watcher.off("unlink", this.onUnlink);
|
|
8667
|
+
watcher.off("add", this.onAdd);
|
|
8668
|
+
this.unregisterWatcher = noop;
|
|
8669
|
+
};
|
|
8670
|
+
return this;
|
|
8704
8671
|
}
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8672
|
+
scheduleRerun(file) {
|
|
8673
|
+
this._onRerun.forEach((cb) => cb(file));
|
|
8674
|
+
}
|
|
8675
|
+
getTestFilesFromWatcherTrigger(id) {
|
|
8676
|
+
if (!this.vitest.config.watchTriggerPatterns) {
|
|
8677
|
+
return false;
|
|
8678
|
+
}
|
|
8679
|
+
let triggered = false;
|
|
8680
|
+
this.vitest.config.watchTriggerPatterns.forEach((definition) => {
|
|
8681
|
+
const exec = definition.pattern.exec(id);
|
|
8682
|
+
if (exec) {
|
|
8683
|
+
const files = definition.testsToRun(id, exec);
|
|
8684
|
+
if (Array.isArray(files)) {
|
|
8685
|
+
triggered = true;
|
|
8686
|
+
files.forEach((file) => this.changedTests.add(resolve(this.vitest.config.root, file)));
|
|
8687
|
+
} else if (typeof files === "string") {
|
|
8688
|
+
triggered = true;
|
|
8689
|
+
this.changedTests.add(resolve(this.vitest.config.root, files));
|
|
8690
|
+
}
|
|
8691
|
+
}
|
|
8692
|
+
});
|
|
8693
|
+
return triggered;
|
|
8694
|
+
}
|
|
8695
|
+
onChange = (id) => {
|
|
8696
|
+
id = slash(id);
|
|
8697
|
+
this.vitest.logger.clearHighlightCache(id);
|
|
8698
|
+
this.vitest.invalidateFile(id);
|
|
8699
|
+
const testFiles = this.getTestFilesFromWatcherTrigger(id);
|
|
8700
|
+
if (testFiles) {
|
|
8701
|
+
this.scheduleRerun(id);
|
|
8702
|
+
} else {
|
|
8703
|
+
const needsRerun = this.handleFileChanged(id);
|
|
8704
|
+
if (needsRerun) {
|
|
8705
|
+
this.scheduleRerun(id);
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
};
|
|
8709
|
+
onUnlink = (id) => {
|
|
8710
|
+
id = slash(id);
|
|
8711
|
+
this.vitest.logger.clearHighlightCache(id);
|
|
8712
|
+
this.invalidates.add(id);
|
|
8713
|
+
if (this.vitest.state.filesMap.has(id)) {
|
|
8714
|
+
this.vitest.projects.forEach((project) => project._removeCachedTestFile(id));
|
|
8715
|
+
this.vitest.state.filesMap.delete(id);
|
|
8716
|
+
this.vitest.cache.results.removeFromCache(id);
|
|
8717
|
+
this.vitest.cache.stats.removeStats(id);
|
|
8718
|
+
this.changedTests.delete(id);
|
|
8719
|
+
this.vitest.report("onTestRemoved", id);
|
|
8720
|
+
}
|
|
8721
|
+
};
|
|
8722
|
+
onAdd = (id) => {
|
|
8723
|
+
id = slash(id);
|
|
8724
|
+
this.vitest.invalidateFile(id);
|
|
8725
|
+
const testFiles = this.getTestFilesFromWatcherTrigger(id);
|
|
8726
|
+
if (testFiles) {
|
|
8727
|
+
this.scheduleRerun(id);
|
|
8728
|
+
return;
|
|
8729
|
+
}
|
|
8730
|
+
let fileContent;
|
|
8731
|
+
const matchingProjects = [];
|
|
8732
|
+
this.vitest.projects.forEach((project) => {
|
|
8733
|
+
if (project.matchesTestGlob(id, () => fileContent ??= readFileSync(id, "utf-8"))) {
|
|
8734
|
+
matchingProjects.push(project);
|
|
8735
|
+
}
|
|
8736
|
+
});
|
|
8737
|
+
if (matchingProjects.length > 0) {
|
|
8738
|
+
this.changedTests.add(id);
|
|
8739
|
+
this.scheduleRerun(id);
|
|
8740
|
+
} else {
|
|
8741
|
+
const needsRerun = this.handleFileChanged(id);
|
|
8742
|
+
if (needsRerun) {
|
|
8743
|
+
this.scheduleRerun(id);
|
|
8744
|
+
}
|
|
8745
|
+
}
|
|
8746
|
+
};
|
|
8747
|
+
/**
|
|
8748
|
+
* @returns A value indicating whether rerun is needed (changedTests was mutated)
|
|
8749
|
+
*/
|
|
8750
|
+
handleFileChanged(filepath) {
|
|
8751
|
+
if (this.changedTests.has(filepath) || this.invalidates.has(filepath)) {
|
|
8752
|
+
return false;
|
|
8753
|
+
}
|
|
8754
|
+
if (pm.isMatch(filepath, this.vitest.config.forceRerunTriggers)) {
|
|
8755
|
+
this.vitest.state.getFilepaths().forEach((file) => this.changedTests.add(file));
|
|
8756
|
+
return true;
|
|
8757
|
+
}
|
|
8758
|
+
const projects = this.vitest.projects.filter((project) => {
|
|
8759
|
+
const moduleGraph = project.browser?.vite.moduleGraph || project.vite.moduleGraph;
|
|
8760
|
+
return moduleGraph.getModulesByFile(filepath)?.size;
|
|
8761
|
+
});
|
|
8762
|
+
if (!projects.length) {
|
|
8763
|
+
if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath))) {
|
|
8764
|
+
this.changedTests.add(filepath);
|
|
8765
|
+
return true;
|
|
8766
|
+
}
|
|
8767
|
+
return false;
|
|
8768
|
+
}
|
|
8769
|
+
const files = [];
|
|
8770
|
+
for (const project of projects) {
|
|
8771
|
+
const mods = project.browser?.vite.moduleGraph.getModulesByFile(filepath) || project.vite.moduleGraph.getModulesByFile(filepath);
|
|
8772
|
+
if (!mods || !mods.size) {
|
|
8773
|
+
continue;
|
|
8774
|
+
}
|
|
8775
|
+
this.invalidates.add(filepath);
|
|
8776
|
+
if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
|
|
8777
|
+
this.changedTests.add(filepath);
|
|
8778
|
+
files.push(filepath);
|
|
8779
|
+
continue;
|
|
8780
|
+
}
|
|
8781
|
+
let rerun = false;
|
|
8782
|
+
for (const mod of mods) {
|
|
8783
|
+
mod.importers.forEach((i) => {
|
|
8784
|
+
if (!i.file) {
|
|
8785
|
+
return;
|
|
8786
|
+
}
|
|
8787
|
+
const needsRerun = this.handleFileChanged(i.file);
|
|
8788
|
+
if (needsRerun) {
|
|
8789
|
+
rerun = true;
|
|
8790
|
+
}
|
|
8791
|
+
});
|
|
8792
|
+
}
|
|
8793
|
+
if (rerun) {
|
|
8794
|
+
files.push(filepath);
|
|
8795
|
+
}
|
|
8796
|
+
}
|
|
8797
|
+
return !!files.length;
|
|
8713
8798
|
}
|
|
8714
|
-
return names;
|
|
8715
8799
|
}
|
|
8716
8800
|
|
|
8717
8801
|
const WATCHER_DEBOUNCE = 100;
|
|
@@ -8904,7 +8988,7 @@ class Vitest {
|
|
|
8904
8988
|
try {
|
|
8905
8989
|
await this.cache.results.readFromCache();
|
|
8906
8990
|
} catch {}
|
|
8907
|
-
const projects = await this.
|
|
8991
|
+
const projects = await this.resolveProjects(cliOptions);
|
|
8908
8992
|
this.resolvedProjects = projects;
|
|
8909
8993
|
this.projects = projects;
|
|
8910
8994
|
await Promise.all(projects.flatMap((project) => {
|
|
@@ -8945,9 +9029,9 @@ class Vitest {
|
|
|
8945
9029
|
*/
|
|
8946
9030
|
injectTestProject = async (config) => {
|
|
8947
9031
|
const currentNames = new Set(this.projects.map((p) => p.name));
|
|
8948
|
-
const
|
|
8949
|
-
this.projects.push(...
|
|
8950
|
-
return
|
|
9032
|
+
const projects = await resolveProjects(this, this._options, undefined, Array.isArray(config) ? config : [config], currentNames);
|
|
9033
|
+
this.projects.push(...projects);
|
|
9034
|
+
return projects;
|
|
8951
9035
|
};
|
|
8952
9036
|
/**
|
|
8953
9037
|
* Provide a value to the test context. This value will be available to all tests with `inject`.
|
|
@@ -9018,10 +9102,17 @@ class Vitest {
|
|
|
9018
9102
|
}
|
|
9019
9103
|
return join(configDir, workspaceConfigName);
|
|
9020
9104
|
}
|
|
9021
|
-
async
|
|
9105
|
+
async resolveProjects(cliOptions) {
|
|
9022
9106
|
const names = new Set();
|
|
9107
|
+
if (this.config.projects) {
|
|
9108
|
+
if (typeof this.config.workspace !== "undefined") {
|
|
9109
|
+
this.logger.warn("Both `config.projects` and `config.workspace` are defined. Ignoring the `workspace` option.");
|
|
9110
|
+
}
|
|
9111
|
+
return resolveProjects(this, cliOptions, undefined, this.config.projects, names);
|
|
9112
|
+
}
|
|
9023
9113
|
if (Array.isArray(this.config.workspace)) {
|
|
9024
|
-
|
|
9114
|
+
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`.");
|
|
9115
|
+
return resolveProjects(this, cliOptions, undefined, this.config.workspace, names);
|
|
9025
9116
|
}
|
|
9026
9117
|
const workspaceConfigPath = await this.resolveWorkspaceConfigPath();
|
|
9027
9118
|
this._workspaceConfigPath = workspaceConfigPath;
|
|
@@ -9030,13 +9121,15 @@ class Vitest {
|
|
|
9030
9121
|
if (!project) {
|
|
9031
9122
|
return [];
|
|
9032
9123
|
}
|
|
9033
|
-
return
|
|
9124
|
+
return resolveBrowserProjects(this, new Set([project.name]), [project]);
|
|
9034
9125
|
}
|
|
9126
|
+
const configFile = this.vite.config.configFile ? resolve(this.vite.config.root, this.vite.config.configFile) : "the root config file";
|
|
9127
|
+
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
9128
|
const workspaceModule = await this.import(workspaceConfigPath);
|
|
9036
9129
|
if (!workspaceModule.default || !Array.isArray(workspaceModule.default)) {
|
|
9037
9130
|
throw new TypeError(`Workspace config file "${workspaceConfigPath}" must export a default array of project paths.`);
|
|
9038
9131
|
}
|
|
9039
|
-
return
|
|
9132
|
+
return resolveProjects(this, cliOptions, workspaceConfigPath, workspaceModule.default, names);
|
|
9040
9133
|
}
|
|
9041
9134
|
/**
|
|
9042
9135
|
* Glob test files in every project and create a TestSpecification for each file and pool.
|