vitest 4.0.17 → 4.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +36 -0
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -2
- package/dist/chunks/acorn.B2iPLyUM.js +5958 -0
- package/dist/chunks/{base.XJJQZiKB.js → base.DiopZV8F.js} +49 -14
- package/dist/chunks/{benchmark.B3N2zMcH.js → benchmark.BoqSLF53.js} +1 -1
- package/dist/chunks/{browser.d.ChKACdzH.d.ts → browser.d.BE4kbYok.d.ts} +4 -1
- package/dist/chunks/{cac.jRCLJDDc.js → cac.C4jjt2RX.js} +816 -14
- package/dist/chunks/{cli-api.Cx2DW4Bc.js → cli-api.ChbI1JU9.js} +412 -166
- package/dist/chunks/{config.d.Cy95HiCx.d.ts → config.d.Cr1Ep39N.d.ts} +13 -11
- package/dist/chunks/{console.Cf-YriPC.js → console.CNlG1KsP.js} +3 -2
- package/dist/chunks/{constants.D_Q9UYh-.js → constants.B63TT-Bl.js} +1 -1
- package/dist/chunks/coverage.tyqbzn4W.js +1001 -0
- package/dist/chunks/{creator.DAmOKTvJ.js → creator.yyCHuw5R.js} +33 -2
- package/dist/chunks/{global.d.B15mdLcR.d.ts → global.d.JeWMqlOm.d.ts} +1 -1
- package/dist/chunks/{globals.DOayXfHP.js → globals.C6Ecf1TO.js} +11 -10
- package/dist/chunks/{index.M8mOzt4Y.js → index.B-iBE_Gx.js} +21 -5
- package/dist/chunks/{coverage.AVPTjMgw.js → index.BCY_7LL2.js} +5 -959
- package/dist/chunks/{index.6Qv1eEA6.js → index.CAN630q3.js} +20 -8
- package/dist/chunks/{index.C5r1PdPD.js → index.CFulQRmC.js} +1 -1
- package/dist/chunks/{index.Z5E_ObnR.js → index.CouFDptX.js} +4 -2
- package/dist/chunks/{init-forks.BC6ZwHQN.js → init-forks.BnCXPazU.js} +1 -1
- package/dist/chunks/{init-threads.CxSxLC0N.js → init-threads.Cyh2PqXi.js} +1 -1
- package/dist/chunks/{init.C9kljSTm.js → init.B95Mm0Iz.js} +65 -12
- package/dist/chunks/native.mV0-490A.js +148 -0
- package/dist/chunks/nativeModuleMocker.D_q5sFv6.js +206 -0
- package/dist/chunks/nativeModuleRunner.BIakptoF.js +36 -0
- package/dist/chunks/{node.Ce0vMQM7.js → node.CrSEwhm4.js} +1 -1
- package/dist/chunks/{plugin.d.CtqpEehP.d.ts → plugin.d.C9o5bttz.d.ts} +1 -1
- package/dist/chunks/{reporters.d.CWXNI2jG.d.ts → reporters.d.7faYdkxy.d.ts} +146 -49
- package/dist/chunks/rpc.DcRWTy5G.js +148 -0
- package/dist/chunks/{rpc.d.RH3apGEf.d.ts → rpc.d.CM7x9-sm.d.ts} +1 -0
- package/dist/chunks/{setup-common.Cm-kSBVi.js → setup-common.cvFp-ao9.js} +2 -2
- package/dist/chunks/{startModuleRunner.DEj0jb3e.js → startVitestModuleRunner.BK-u7y4N.js} +182 -391
- package/dist/chunks/{vi.2VT5v0um.js → test.G82XYNFk.js} +505 -119
- package/dist/chunks/{utils.DvEY5TfP.js → utils.DT4VyRyl.js} +5 -1
- package/dist/chunks/{vm.CMjifoPa.js → vm.BdLtzhnj.js} +15 -11
- package/dist/chunks/{worker.d.Dyxm8DEL.d.ts → worker.d.CPzI2ZzJ.d.ts} +2 -2
- package/dist/cli.js +4 -3
- package/dist/config.d.ts +11 -11
- package/dist/config.js +1 -1
- package/dist/coverage.d.ts +10 -8
- package/dist/coverage.js +7 -4
- package/dist/environments.js +2 -0
- package/dist/index.d.ts +30 -23
- package/dist/index.js +9 -8
- package/dist/module-evaluator.d.ts +10 -1
- package/dist/module-evaluator.js +1 -5
- package/dist/node.d.ts +13 -12
- package/dist/node.js +27 -25
- package/dist/nodejs-worker-loader.js +41 -0
- package/dist/reporters.d.ts +8 -8
- package/dist/reporters.js +4 -2
- package/dist/runners.d.ts +24 -4
- package/dist/runners.js +6 -6
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.js +35 -0
- package/dist/snapshot.js +4 -2
- package/dist/suite.js +4 -2
- package/dist/worker.d.ts +8 -7
- package/dist/worker.js +25 -20
- package/dist/workers/forks.js +21 -16
- package/dist/workers/runVmTests.js +11 -13
- package/dist/workers/threads.js +21 -16
- package/dist/workers/vmForks.js +14 -11
- package/dist/workers/vmThreads.js +14 -11
- package/package.json +28 -29
- package/suppress-warnings.cjs +1 -0
- package/dist/chunks/date.Bq6ZW5rf.js +0 -73
- package/dist/chunks/rpc.BoxB0q7B.js +0 -76
- package/dist/chunks/test.B8ej_ZHS.js +0 -254
- package/dist/mocker.d.ts +0 -1
- package/dist/mocker.js +0 -1
- package/dist/module-runner.js +0 -17
|
@@ -2,20 +2,20 @@ import fs, { promises, existsSync, mkdirSync, readFileSync, statSync, readdirSyn
|
|
|
2
2
|
import { relative, resolve, dirname, join, extname, normalize, basename, isAbsolute } from 'pathe';
|
|
3
3
|
import { C as CoverageProviderMap } from './coverage.D_JHT54q.js';
|
|
4
4
|
import path, { resolve as resolve$1 } from 'node:path';
|
|
5
|
-
import { noop, createDefer, slash, withTrailingSlash,
|
|
5
|
+
import { cleanUrl, noop, unique, createDefer, slash, withTrailingSlash, wrapId, isExternalUrl, unwrapId, toArray, deepMerge, nanoid, deepClone, isPrimitive, notNullish } from '@vitest/utils/helpers';
|
|
6
6
|
import { a as any, p as prompt } from './index.D4KonVSU.js';
|
|
7
|
-
import {
|
|
7
|
+
import { i as isPackageExists, r as resolveModule } from './index.BCY_7LL2.js';
|
|
8
8
|
import * as vite from 'vite';
|
|
9
|
-
import {
|
|
10
|
-
import { A as API_PATH, c as configFiles, d as defaultBrowserPort,
|
|
9
|
+
import { createServer, isFileLoadingAllowed, normalizePath, parseAst, searchForWorkspaceRoot, fetchModule, version, mergeConfig } from 'vite';
|
|
10
|
+
import { A as API_PATH, c as configFiles, d as defaultBrowserPort, b as defaultPort } from './constants.B63TT-Bl.js';
|
|
11
11
|
import * as nodeos from 'node:os';
|
|
12
12
|
import nodeos__default, { tmpdir } from 'node:os';
|
|
13
|
-
import { generateHash as generateHash$1, createTaskName, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, hasFailed, generateFileHash, limitConcurrency, createFileTask as createFileTask$1, getTasks, isTestCase } from '@vitest/runner/utils';
|
|
13
|
+
import { generateHash as generateHash$1, createTaskName, validateTags, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, hasFailed, generateFileHash, limitConcurrency, createFileTask as createFileTask$1, getTasks, isTestCase } from '@vitest/runner/utils';
|
|
14
14
|
import { SnapshotManager } from '@vitest/snapshot/manager';
|
|
15
|
-
import { v as version$1 } from './cac.
|
|
15
|
+
import { v as version$1 } from './cac.C4jjt2RX.js';
|
|
16
16
|
import { performance as performance$1 } from 'node:perf_hooks';
|
|
17
17
|
import { c as createBirpc } from './index.Chj8NDwU.js';
|
|
18
|
-
import { p as parse, d as stringify, e as createIndexLocationsMap, h as TraceMap, o as originalPositionFor, i as ancestor, j as printError, f as formatProjectName, w as withLabel, k as errorBanner, l as divider, m as Typechecker, n as generateCodeFrame, q as escapeRegExp, r as createDefinesScript, R as ReportersMap, u as groupBy, B as BlobReporter, v as readBlobs, x as convertTasksToEvents, H as HangingProcessReporter, y as wildcardPatternToRegExp, z as stdout } from './index.
|
|
18
|
+
import { p as parse, d as stringify, e as createIndexLocationsMap, h as TraceMap, o as originalPositionFor, i as ancestor, j as printError, f as formatProjectName, w as withLabel, k as errorBanner, l as divider, m as Typechecker, n as generateCodeFrame, q as escapeRegExp, r as createDefinesScript, R as ReportersMap, u as groupBy, B as BlobReporter, v as readBlobs, x as convertTasksToEvents, H as HangingProcessReporter, y as wildcardPatternToRegExp, z as stdout } from './index.B-iBE_Gx.js';
|
|
19
19
|
import require$$0$3 from 'events';
|
|
20
20
|
import require$$1$1 from 'https';
|
|
21
21
|
import require$$2 from 'http';
|
|
@@ -29,8 +29,10 @@ import require$$0$1 from 'buffer';
|
|
|
29
29
|
import { g as getDefaultExportFromCjs } from './_commonjsHelpers.D26ty3Ew.js';
|
|
30
30
|
import crypto, { createHash } from 'node:crypto';
|
|
31
31
|
import { rootDir, distDir } from '../path.js';
|
|
32
|
+
import { N as NativeModuleRunner } from './nativeModuleRunner.BIakptoF.js';
|
|
32
33
|
import { T as Traces } from './traces.CCmnQaNT.js';
|
|
33
34
|
import { createDebug } from 'obug';
|
|
35
|
+
import { h as hash, R as RandomSequencer, i as isBrowserEnabled, r as resolveConfig, g as getCoverageProvider, a as resolveApiServerConfig } from './coverage.tyqbzn4W.js';
|
|
34
36
|
import { rm, readFile, writeFile, rename, stat, unlink, mkdir, copyFile } from 'node:fs/promises';
|
|
35
37
|
import c from 'tinyrainbow';
|
|
36
38
|
import { VitestModuleEvaluator } from '#module-evaluator';
|
|
@@ -53,7 +55,7 @@ import { c as configDefaults } from './defaults.BOqNVLsY.js';
|
|
|
53
55
|
import { KNOWN_ASSET_RE } from '@vitest/utils/constants';
|
|
54
56
|
import { findNearestPackageData } from '@vitest/utils/resolver';
|
|
55
57
|
import * as esModuleLexer from 'es-module-lexer';
|
|
56
|
-
import { a as BenchmarkReportsMap } from './index.
|
|
58
|
+
import { a as BenchmarkReportsMap } from './index.CFulQRmC.js';
|
|
57
59
|
import assert$1 from 'node:assert';
|
|
58
60
|
import { serializeValue } from '@vitest/utils/serialize';
|
|
59
61
|
import { parseErrorStacktrace } from '@vitest/utils/source-map';
|
|
@@ -76,6 +78,7 @@ function requireConstants () {
|
|
|
76
78
|
|
|
77
79
|
constants = {
|
|
78
80
|
BINARY_TYPES,
|
|
81
|
+
CLOSE_TIMEOUT: 30000,
|
|
79
82
|
EMPTY_BUFFER: Buffer.alloc(0),
|
|
80
83
|
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
|
81
84
|
hasBlob,
|
|
@@ -2846,6 +2849,7 @@ function requireWebsocket () {
|
|
|
2846
2849
|
|
|
2847
2850
|
const {
|
|
2848
2851
|
BINARY_TYPES,
|
|
2852
|
+
CLOSE_TIMEOUT,
|
|
2849
2853
|
EMPTY_BUFFER,
|
|
2850
2854
|
GUID,
|
|
2851
2855
|
kForOnEventAttribute,
|
|
@@ -2860,7 +2864,6 @@ function requireWebsocket () {
|
|
|
2860
2864
|
const { format, parse } = requireExtension();
|
|
2861
2865
|
const { toBuffer } = requireBufferUtil();
|
|
2862
2866
|
|
|
2863
|
-
const closeTimeout = 30 * 1000;
|
|
2864
2867
|
const kAborted = Symbol('kAborted');
|
|
2865
2868
|
const protocolVersions = [8, 13];
|
|
2866
2869
|
const readyStates = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
|
|
@@ -2916,6 +2919,7 @@ function requireWebsocket () {
|
|
|
2916
2919
|
initAsClient(this, address, protocols, options);
|
|
2917
2920
|
} else {
|
|
2918
2921
|
this._autoPong = options.autoPong;
|
|
2922
|
+
this._closeTimeout = options.closeTimeout;
|
|
2919
2923
|
this._isServer = true;
|
|
2920
2924
|
}
|
|
2921
2925
|
}
|
|
@@ -3457,6 +3461,8 @@ function requireWebsocket () {
|
|
|
3457
3461
|
* times in the same tick
|
|
3458
3462
|
* @param {Boolean} [options.autoPong=true] Specifies whether or not to
|
|
3459
3463
|
* automatically send a pong in response to a ping
|
|
3464
|
+
* @param {Number} [options.closeTimeout=30000] Duration in milliseconds to wait
|
|
3465
|
+
* for the closing handshake to finish after `websocket.close()` is called
|
|
3460
3466
|
* @param {Function} [options.finishRequest] A function which can be used to
|
|
3461
3467
|
* customize the headers of each http request before it is sent
|
|
3462
3468
|
* @param {Boolean} [options.followRedirects=false] Whether or not to follow
|
|
@@ -3483,6 +3489,7 @@ function requireWebsocket () {
|
|
|
3483
3489
|
const opts = {
|
|
3484
3490
|
allowSynchronousEvents: true,
|
|
3485
3491
|
autoPong: true,
|
|
3492
|
+
closeTimeout: CLOSE_TIMEOUT,
|
|
3486
3493
|
protocolVersion: protocolVersions[1],
|
|
3487
3494
|
maxPayload: 100 * 1024 * 1024,
|
|
3488
3495
|
skipUTF8Validation: false,
|
|
@@ -3501,6 +3508,7 @@ function requireWebsocket () {
|
|
|
3501
3508
|
};
|
|
3502
3509
|
|
|
3503
3510
|
websocket._autoPong = opts.autoPong;
|
|
3511
|
+
websocket._closeTimeout = opts.closeTimeout;
|
|
3504
3512
|
|
|
3505
3513
|
if (!protocolVersions.includes(opts.protocolVersion)) {
|
|
3506
3514
|
throw new RangeError(
|
|
@@ -4118,7 +4126,7 @@ function requireWebsocket () {
|
|
|
4118
4126
|
function setCloseTimer(websocket) {
|
|
4119
4127
|
websocket._closeTimer = setTimeout(
|
|
4120
4128
|
websocket._socket.destroy.bind(websocket._socket),
|
|
4121
|
-
|
|
4129
|
+
websocket._closeTimeout
|
|
4122
4130
|
);
|
|
4123
4131
|
}
|
|
4124
4132
|
|
|
@@ -4136,23 +4144,23 @@ function requireWebsocket () {
|
|
|
4136
4144
|
|
|
4137
4145
|
websocket._readyState = WebSocket.CLOSING;
|
|
4138
4146
|
|
|
4139
|
-
let chunk;
|
|
4140
|
-
|
|
4141
4147
|
//
|
|
4142
4148
|
// The close frame might not have been received or the `'end'` event emitted,
|
|
4143
4149
|
// for example, if the socket was destroyed due to an error. Ensure that the
|
|
4144
4150
|
// `receiver` stream is closed after writing any remaining buffered data to
|
|
4145
4151
|
// it. If the readable side of the socket is in flowing mode then there is no
|
|
4146
|
-
// buffered data as everything has been already written
|
|
4147
|
-
//
|
|
4148
|
-
//
|
|
4152
|
+
// buffered data as everything has been already written. If instead, the
|
|
4153
|
+
// socket is paused, any possible buffered data will be read as a single
|
|
4154
|
+
// chunk.
|
|
4149
4155
|
//
|
|
4150
4156
|
if (
|
|
4151
4157
|
!this._readableState.endEmitted &&
|
|
4152
4158
|
!websocket._closeFrameReceived &&
|
|
4153
4159
|
!websocket._receiver._writableState.errorEmitted &&
|
|
4154
|
-
|
|
4160
|
+
this._readableState.length !== 0
|
|
4155
4161
|
) {
|
|
4162
|
+
const chunk = this.read(this._readableState.length);
|
|
4163
|
+
|
|
4156
4164
|
websocket._receiver.write(chunk);
|
|
4157
4165
|
}
|
|
4158
4166
|
|
|
@@ -4483,7 +4491,7 @@ function requireWebsocketServer () {
|
|
|
4483
4491
|
const PerMessageDeflate = requirePermessageDeflate();
|
|
4484
4492
|
const subprotocol = requireSubprotocol();
|
|
4485
4493
|
const WebSocket = requireWebsocket();
|
|
4486
|
-
const { GUID, kWebSocket } = requireConstants();
|
|
4494
|
+
const { CLOSE_TIMEOUT, GUID, kWebSocket } = requireConstants();
|
|
4487
4495
|
|
|
4488
4496
|
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
|
4489
4497
|
|
|
@@ -4510,6 +4518,9 @@ function requireWebsocketServer () {
|
|
|
4510
4518
|
* pending connections
|
|
4511
4519
|
* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
|
|
4512
4520
|
* track clients
|
|
4521
|
+
* @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
|
|
4522
|
+
* wait for the closing handshake to finish after `websocket.close()` is
|
|
4523
|
+
* called
|
|
4513
4524
|
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
|
4514
4525
|
* @param {String} [options.host] The hostname where to bind the server
|
|
4515
4526
|
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
|
@@ -4539,6 +4550,7 @@ function requireWebsocketServer () {
|
|
|
4539
4550
|
perMessageDeflate: false,
|
|
4540
4551
|
handleProtocols: null,
|
|
4541
4552
|
clientTracking: true,
|
|
4553
|
+
closeTimeout: CLOSE_TIMEOUT,
|
|
4542
4554
|
verifyClient: null,
|
|
4543
4555
|
noServer: false,
|
|
4544
4556
|
backlog: null, // use default (511 as implemented in net.js)
|
|
@@ -5026,17 +5038,40 @@ function requireWebsocketServer () {
|
|
|
5026
5038
|
var websocketServerExports = requireWebsocketServer();
|
|
5027
5039
|
var WebSocketServer = /*@__PURE__*/getDefaultExportFromCjs(websocketServerExports);
|
|
5028
5040
|
|
|
5041
|
+
async function createViteServer(inlineConfig) {
|
|
5042
|
+
// Vite prints an error (https://github.com/vitejs/vite/issues/14328)
|
|
5043
|
+
// But Vitest works correctly either way
|
|
5044
|
+
const error = console.error;
|
|
5045
|
+
console.error = (...args) => {
|
|
5046
|
+
if (typeof args[0] === "string" && args[0].includes("WebSocket server error:")) return;
|
|
5047
|
+
error(...args);
|
|
5048
|
+
};
|
|
5049
|
+
const server = await createServer(inlineConfig);
|
|
5050
|
+
console.error = error;
|
|
5051
|
+
return server;
|
|
5052
|
+
}
|
|
5053
|
+
function isFileServingAllowed(configOrUrl, urlOrServer) {
|
|
5054
|
+
const config = typeof urlOrServer === "string" ? configOrUrl : urlOrServer.config;
|
|
5055
|
+
const url = typeof urlOrServer === "string" ? urlOrServer : configOrUrl;
|
|
5056
|
+
if (!config.server.fs.strict) return true;
|
|
5057
|
+
return isFileLoadingAllowed(config, fsPathFromUrl(url));
|
|
5058
|
+
}
|
|
5059
|
+
const FS_PREFIX = "/@fs/";
|
|
5060
|
+
const VOLUME_RE = /^[A-Z]:/i;
|
|
5061
|
+
function fsPathFromId(id) {
|
|
5062
|
+
const fsPath = normalizePath(id.startsWith(FS_PREFIX) ? id.slice(5) : id);
|
|
5063
|
+
return fsPath[0] === "/" || VOLUME_RE.test(fsPath) ? fsPath : `/${fsPath}`;
|
|
5064
|
+
}
|
|
5065
|
+
function fsPathFromUrl(url) {
|
|
5066
|
+
return fsPathFromId(cleanUrl(url));
|
|
5067
|
+
}
|
|
5068
|
+
|
|
5029
5069
|
function getTestFileEnvironment(project, testFile, browser = false) {
|
|
5030
|
-
|
|
5031
|
-
if (browser) environment = project.browser?.vite.environments.client;
|
|
5070
|
+
if (browser) return project.browser?.vite.environments.client;
|
|
5032
5071
|
else for (const name in project.vite.environments) {
|
|
5033
5072
|
const env = project.vite.environments[name];
|
|
5034
|
-
if (env.moduleGraph.getModuleById(testFile))
|
|
5035
|
-
environment = env;
|
|
5036
|
-
break;
|
|
5037
|
-
}
|
|
5073
|
+
if (env.moduleGraph.getModuleById(testFile)) return env;
|
|
5038
5074
|
}
|
|
5039
|
-
return environment;
|
|
5040
5075
|
}
|
|
5041
5076
|
|
|
5042
5077
|
async function getModuleGraph(ctx, projectName, testFilePath, browser = false) {
|
|
@@ -5044,7 +5079,7 @@ async function getModuleGraph(ctx, projectName, testFilePath, browser = false) {
|
|
|
5044
5079
|
const externalized = /* @__PURE__ */ new Set();
|
|
5045
5080
|
const inlined = /* @__PURE__ */ new Set();
|
|
5046
5081
|
const project = ctx.getProjectByName(projectName);
|
|
5047
|
-
const environment = getTestFileEnvironment(project, testFilePath, browser);
|
|
5082
|
+
const environment = project.config.experimental.viteModuleRunner === false ? project.vite.environments.__vitest__ : getTestFileEnvironment(project, testFilePath, browser);
|
|
5048
5083
|
if (!environment) throw new Error(`Cannot find environment for ${testFilePath}`);
|
|
5049
5084
|
const seen = /* @__PURE__ */ new Map();
|
|
5050
5085
|
function get(mod) {
|
|
@@ -5175,7 +5210,7 @@ function setup(ctx, _server) {
|
|
|
5175
5210
|
async getExternalResult(moduleId, testFileTaskId) {
|
|
5176
5211
|
const testModule = ctx.state.getReportedEntityById(testFileTaskId);
|
|
5177
5212
|
if (!testModule) return;
|
|
5178
|
-
if (!isFileServingAllowed
|
|
5213
|
+
if (!isFileServingAllowed(testModule.project.vite.config, moduleId)) return;
|
|
5179
5214
|
const result = {};
|
|
5180
5215
|
try {
|
|
5181
5216
|
result.source = await promises.readFile(moduleId, "utf-8");
|
|
@@ -5185,7 +5220,7 @@ function setup(ctx, _server) {
|
|
|
5185
5220
|
async getTransformResult(projectName, moduleId, testFileTaskId, browser = false) {
|
|
5186
5221
|
const project = ctx.getProjectByName(projectName);
|
|
5187
5222
|
const testModule = ctx.state.getReportedEntityById(testFileTaskId);
|
|
5188
|
-
if (!testModule || !isFileServingAllowed
|
|
5223
|
+
if (!testModule || !isFileServingAllowed(project.vite.config, moduleId)) return;
|
|
5189
5224
|
const environment = getTestFileEnvironment(project, testModule.moduleId, browser);
|
|
5190
5225
|
const moduleNode = environment?.moduleGraph.getModuleById(moduleId);
|
|
5191
5226
|
if (!environment || !moduleNode?.transformResult) return;
|
|
@@ -5325,8 +5360,62 @@ function createDebugger(namespace) {
|
|
|
5325
5360
|
if (debug.enabled) return debug;
|
|
5326
5361
|
}
|
|
5327
5362
|
|
|
5363
|
+
async function getSpecificationsOptions(specifications) {
|
|
5364
|
+
const environments = /* @__PURE__ */ new WeakMap();
|
|
5365
|
+
const cache = /* @__PURE__ */ new Map();
|
|
5366
|
+
const tags = /* @__PURE__ */ new WeakMap();
|
|
5367
|
+
await Promise.all(specifications.map(async (spec) => {
|
|
5368
|
+
const { moduleId: filepath, project, pool } = spec;
|
|
5369
|
+
// browser pool handles its own environment
|
|
5370
|
+
if (pool === "browser") return;
|
|
5371
|
+
// reuse if projects have the same test files
|
|
5372
|
+
let code = cache.get(filepath);
|
|
5373
|
+
if (!code) {
|
|
5374
|
+
code = await promises.readFile(filepath, "utf-8").catch(() => "");
|
|
5375
|
+
cache.set(filepath, code);
|
|
5376
|
+
}
|
|
5377
|
+
const { env = project.config.environment || "node", envOptions, tags: specTags = [] } = detectCodeBlock(code);
|
|
5378
|
+
tags.set(spec, specTags);
|
|
5379
|
+
const environment = {
|
|
5380
|
+
name: env,
|
|
5381
|
+
options: envOptions ? { [env === "happy-dom" ? "happyDOM" : env]: envOptions } : null
|
|
5382
|
+
};
|
|
5383
|
+
environments.set(spec, environment);
|
|
5384
|
+
}));
|
|
5385
|
+
return {
|
|
5386
|
+
environments,
|
|
5387
|
+
tags
|
|
5388
|
+
};
|
|
5389
|
+
}
|
|
5390
|
+
function detectCodeBlock(content) {
|
|
5391
|
+
const env = content.match(/@(?:vitest|jest)-environment\s+([\w-]+)\b/)?.[1];
|
|
5392
|
+
let envOptionsJson = content.match(/@(?:vitest|jest)-environment-options\s+(.+)/)?.[1];
|
|
5393
|
+
if (envOptionsJson?.endsWith("*/"))
|
|
5394
|
+
// Trim closing Docblock characters the above regex might have captured
|
|
5395
|
+
envOptionsJson = envOptionsJson.slice(0, -2);
|
|
5396
|
+
const envOptions = JSON.parse(envOptionsJson || "null");
|
|
5397
|
+
const tags = [];
|
|
5398
|
+
let tagMatch;
|
|
5399
|
+
// eslint-disable-next-line no-cond-assign
|
|
5400
|
+
while (tagMatch = content.match(/(\/\/|\*)\s*@module-tag\s+([\w\-/]+)\b/)) {
|
|
5401
|
+
tags.push(tagMatch[2]);
|
|
5402
|
+
content = content.slice(tagMatch.index + tagMatch[0].length);
|
|
5403
|
+
}
|
|
5404
|
+
return {
|
|
5405
|
+
env,
|
|
5406
|
+
envOptions,
|
|
5407
|
+
tags
|
|
5408
|
+
};
|
|
5409
|
+
}
|
|
5410
|
+
|
|
5328
5411
|
const debug$1 = createDebugger("vitest:ast-collect-info");
|
|
5329
5412
|
const verbose = createDebugger("vitest:ast-collect-verbose");
|
|
5413
|
+
function isTestFunctionName(name) {
|
|
5414
|
+
return name === "it" || name === "test" || name.startsWith("test") || name.endsWith("Test");
|
|
5415
|
+
}
|
|
5416
|
+
function isVitestFunctionName(name) {
|
|
5417
|
+
return name === "describe" || name === "suite" || isTestFunctionName(name);
|
|
5418
|
+
}
|
|
5330
5419
|
function astParseFile(filepath, code) {
|
|
5331
5420
|
const ast = parseAst(code);
|
|
5332
5421
|
if (verbose) verbose("Collecting", filepath, code);
|
|
@@ -5338,12 +5427,7 @@ function astParseFile(filepath, code) {
|
|
|
5338
5427
|
if (callee.type === "CallExpression") return getName(callee.callee);
|
|
5339
5428
|
if (callee.type === "TaggedTemplateExpression") return getName(callee.tag);
|
|
5340
5429
|
if (callee.type === "MemberExpression") {
|
|
5341
|
-
if (callee.object?.type === "Identifier" &&
|
|
5342
|
-
"it",
|
|
5343
|
-
"test",
|
|
5344
|
-
"describe",
|
|
5345
|
-
"suite"
|
|
5346
|
-
].includes(callee.object.name)) return callee.object?.name;
|
|
5430
|
+
if (callee.object?.type === "Identifier" && isVitestFunctionName(callee.object.name)) return callee.object?.name;
|
|
5347
5431
|
if (callee.object?.name?.startsWith("__vite_ssr_") || callee.object?.object?.name?.startsWith("__vite_ssr_") && callee.object?.property?.name === "Vitest") return getName(callee.property);
|
|
5348
5432
|
// call as `__vite_ssr__.test.skip()`
|
|
5349
5433
|
return getName(callee.object?.property);
|
|
@@ -5359,12 +5443,7 @@ function astParseFile(filepath, code) {
|
|
|
5359
5443
|
const { callee } = node;
|
|
5360
5444
|
const name = getName(callee);
|
|
5361
5445
|
if (!name) return;
|
|
5362
|
-
if (!
|
|
5363
|
-
"it",
|
|
5364
|
-
"test",
|
|
5365
|
-
"describe",
|
|
5366
|
-
"suite"
|
|
5367
|
-
].includes(name)) {
|
|
5446
|
+
if (!isVitestFunctionName(name)) {
|
|
5368
5447
|
verbose?.(`Skipping ${name} (unknown call)`);
|
|
5369
5448
|
return;
|
|
5370
5449
|
}
|
|
@@ -5375,7 +5454,9 @@ function astParseFile(filepath, code) {
|
|
|
5375
5454
|
"each",
|
|
5376
5455
|
"for",
|
|
5377
5456
|
"skipIf",
|
|
5378
|
-
"runIf"
|
|
5457
|
+
"runIf",
|
|
5458
|
+
"extend",
|
|
5459
|
+
"scoped"
|
|
5379
5460
|
].includes(mode)) return;
|
|
5380
5461
|
let start;
|
|
5381
5462
|
const end = node.end;
|
|
@@ -5400,15 +5481,32 @@ function astParseFile(filepath, code) {
|
|
|
5400
5481
|
const property = callee.tag?.property?.name;
|
|
5401
5482
|
isDynamicEach = property === "each" || property === "for";
|
|
5402
5483
|
}
|
|
5403
|
-
|
|
5484
|
+
// Extract tags from the second argument if it's an options object
|
|
5485
|
+
const tags = [];
|
|
5486
|
+
const secondArg = node.arguments?.[1];
|
|
5487
|
+
if (secondArg?.type === "ObjectExpression") {
|
|
5488
|
+
const tagsProperty = secondArg.properties?.find((p) => p.type === "Property" && p.key?.type === "Identifier" && p.key.name === "tags");
|
|
5489
|
+
if (tagsProperty) {
|
|
5490
|
+
const tagsValue = tagsProperty.value;
|
|
5491
|
+
if (tagsValue?.type === "Literal" && typeof tagsValue.value === "string")
|
|
5492
|
+
// tags: 'single-tag'
|
|
5493
|
+
tags.push(tagsValue.value);
|
|
5494
|
+
else if (tagsValue?.type === "ArrayExpression") {
|
|
5495
|
+
// tags: ['tag1', 'tag2']
|
|
5496
|
+
for (const element of tagsValue.elements || []) if (element?.type === "Literal" && typeof element.value === "string") tags.push(element.value);
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
}
|
|
5500
|
+
debug$1?.("Found", name, message, `(${mode})`, tags.length ? `[${tags.join(", ")}]` : "");
|
|
5404
5501
|
definitions.push({
|
|
5405
5502
|
start,
|
|
5406
5503
|
end,
|
|
5407
5504
|
name: message,
|
|
5408
|
-
type: name
|
|
5505
|
+
type: isTestFunctionName(name) ? "test" : "suite",
|
|
5409
5506
|
mode,
|
|
5410
5507
|
task: null,
|
|
5411
|
-
dynamic: isDynamicEach
|
|
5508
|
+
dynamic: isDynamicEach,
|
|
5509
|
+
tags
|
|
5412
5510
|
});
|
|
5413
5511
|
} });
|
|
5414
5512
|
return {
|
|
@@ -5454,22 +5552,23 @@ function serializeError(ctx, error) {
|
|
|
5454
5552
|
message: error.message
|
|
5455
5553
|
}];
|
|
5456
5554
|
}
|
|
5457
|
-
function createFileTask(testFilepath, code, requestMap,
|
|
5555
|
+
function createFileTask(testFilepath, code, requestMap, config, filepath, fileTags) {
|
|
5458
5556
|
const { definitions, ast } = astParseFile(testFilepath, code);
|
|
5459
5557
|
const file = {
|
|
5460
|
-
filepath
|
|
5558
|
+
filepath,
|
|
5461
5559
|
type: "suite",
|
|
5462
|
-
id: /* @__PURE__ */ generateHash$1(`${testFilepath}${
|
|
5560
|
+
id: /* @__PURE__ */ generateHash$1(`${testFilepath}${config.name || ""}`),
|
|
5463
5561
|
name: testFilepath,
|
|
5464
5562
|
fullName: testFilepath,
|
|
5465
5563
|
mode: "run",
|
|
5466
5564
|
tasks: [],
|
|
5467
5565
|
start: ast.start,
|
|
5468
5566
|
end: ast.end,
|
|
5469
|
-
projectName:
|
|
5567
|
+
projectName: config.name,
|
|
5470
5568
|
meta: {},
|
|
5471
5569
|
pool: "browser",
|
|
5472
|
-
file: null
|
|
5570
|
+
file: null,
|
|
5571
|
+
tags: fileTags || []
|
|
5473
5572
|
};
|
|
5474
5573
|
file.file = file;
|
|
5475
5574
|
const indexMap = createIndexLocationsMap(code);
|
|
@@ -5494,9 +5593,13 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5494
5593
|
});
|
|
5495
5594
|
if (originalLocation.column != null) {
|
|
5496
5595
|
verbose?.(`Found location for`, definition.type, definition.name, `${processedLocation.line}:${processedLocation.column}`, "->", `${originalLocation.line}:${originalLocation.column}`);
|
|
5497
|
-
location =
|
|
5596
|
+
location = {
|
|
5597
|
+
line: originalLocation.line,
|
|
5598
|
+
column: originalLocation.column
|
|
5599
|
+
};
|
|
5498
5600
|
} else debug$1?.("Cannot find original location for", definition.type, definition.name, `${processedLocation.column}:${processedLocation.line}`);
|
|
5499
5601
|
} else debug$1?.("Cannot find original location for", definition.type, definition.name, `${definition.start}`);
|
|
5602
|
+
const taskTags = unique([...latestSuite.tags || [], ...definition.tags]);
|
|
5500
5603
|
if (definition.type === "suite") {
|
|
5501
5604
|
const task = {
|
|
5502
5605
|
type: definition.type,
|
|
@@ -5505,6 +5608,7 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5505
5608
|
file,
|
|
5506
5609
|
tasks: [],
|
|
5507
5610
|
mode,
|
|
5611
|
+
each: definition.dynamic,
|
|
5508
5612
|
name: definition.name,
|
|
5509
5613
|
fullName: createTaskName([latestSuite.fullName, definition.name]),
|
|
5510
5614
|
fullTestName: createTaskName([latestSuite.fullTestName, definition.name]),
|
|
@@ -5512,18 +5616,21 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5512
5616
|
start: definition.start,
|
|
5513
5617
|
location,
|
|
5514
5618
|
dynamic: definition.dynamic,
|
|
5515
|
-
meta: {}
|
|
5619
|
+
meta: {},
|
|
5620
|
+
tags: taskTags
|
|
5516
5621
|
};
|
|
5517
5622
|
definition.task = task;
|
|
5518
5623
|
latestSuite.tasks.push(task);
|
|
5519
5624
|
lastSuite = task;
|
|
5520
5625
|
return;
|
|
5521
5626
|
}
|
|
5627
|
+
validateTags(config, taskTags);
|
|
5522
5628
|
const task = {
|
|
5523
5629
|
type: definition.type,
|
|
5524
5630
|
id: "",
|
|
5525
5631
|
suite: latestSuite,
|
|
5526
5632
|
file,
|
|
5633
|
+
each: definition.dynamic,
|
|
5527
5634
|
mode,
|
|
5528
5635
|
context: {},
|
|
5529
5636
|
name: definition.name,
|
|
@@ -5536,20 +5643,21 @@ function createFileTask(testFilepath, code, requestMap, options) {
|
|
|
5536
5643
|
meta: {},
|
|
5537
5644
|
timeout: 0,
|
|
5538
5645
|
annotations: [],
|
|
5539
|
-
artifacts: []
|
|
5646
|
+
artifacts: [],
|
|
5647
|
+
tags: taskTags
|
|
5540
5648
|
};
|
|
5541
5649
|
definition.task = task;
|
|
5542
5650
|
latestSuite.tasks.push(task);
|
|
5543
5651
|
});
|
|
5544
5652
|
calculateSuiteHash(file);
|
|
5545
5653
|
const hasOnly = someTasksAreOnly(file);
|
|
5546
|
-
interpretTaskModes(file,
|
|
5654
|
+
interpretTaskModes(file, config.testNamePattern, void 0, void 0, void 0, hasOnly, false, config.allowOnly);
|
|
5547
5655
|
markDynamicTests(file.tasks);
|
|
5548
5656
|
if (!file.tasks.length) file.result = {
|
|
5549
5657
|
state: "fail",
|
|
5550
5658
|
errors: [{
|
|
5551
5659
|
name: "Error",
|
|
5552
|
-
message: `No test suite found in file ${
|
|
5660
|
+
message: `No test suite found in file ${filepath}`
|
|
5553
5661
|
}]
|
|
5554
5662
|
};
|
|
5555
5663
|
return file;
|
|
@@ -5561,18 +5669,17 @@ async function astCollectTests(project, filepath) {
|
|
|
5561
5669
|
debug$1?.("Cannot parse", testFilepath, "(vite didn't return anything)");
|
|
5562
5670
|
return createFailedFileTask(project, filepath, /* @__PURE__ */ new Error(`Failed to parse ${testFilepath}. Vite didn't return anything.`));
|
|
5563
5671
|
}
|
|
5564
|
-
return createFileTask(testFilepath, request.code, request.map,
|
|
5565
|
-
name: project.config.name,
|
|
5566
|
-
filepath,
|
|
5567
|
-
allowOnly: project.config.allowOnly,
|
|
5568
|
-
testNamePattern: project.config.testNamePattern,
|
|
5569
|
-
pool: project.browser ? "browser" : project.config.pool
|
|
5570
|
-
});
|
|
5672
|
+
return createFileTask(testFilepath, request.code, request.map, project.serializedConfig, filepath, request.fileTags);
|
|
5571
5673
|
}
|
|
5572
5674
|
async function transformSSR(project, filepath) {
|
|
5573
|
-
const
|
|
5574
|
-
if
|
|
5575
|
-
|
|
5675
|
+
const { env: pragmaEnv, tags: fileTags } = detectCodeBlock(await promises.readFile(filepath, "utf-8").catch(() => ""));
|
|
5676
|
+
// Use environment from pragma if defined, otherwise fall back to config
|
|
5677
|
+
const environment = pragmaEnv || project.config.environment;
|
|
5678
|
+
const transformResult = await (environment === "jsdom" || environment === "happy-dom" ? project.vite.environments.client : project.vite.environments.ssr).transformRequest(filepath);
|
|
5679
|
+
return transformResult ? {
|
|
5680
|
+
...transformResult,
|
|
5681
|
+
fileTags
|
|
5682
|
+
} : null;
|
|
5576
5683
|
}
|
|
5577
5684
|
function markDynamicTests(tasks) {
|
|
5578
5685
|
for (const task of tasks) {
|
|
@@ -6560,6 +6667,25 @@ class Logger {
|
|
|
6560
6667
|
this._highlights.set(filename, code);
|
|
6561
6668
|
return code;
|
|
6562
6669
|
}
|
|
6670
|
+
printNoTestTagsFound() {
|
|
6671
|
+
this.error(c.bgRed(" ERROR "), c.red("No test tags found in any project. Exiting with code 1."));
|
|
6672
|
+
}
|
|
6673
|
+
printTags() {
|
|
6674
|
+
const vitest = this.ctx;
|
|
6675
|
+
const rootProject = vitest.getRootProject();
|
|
6676
|
+
const projects = [rootProject, ...vitest.projects.filter((p) => p !== rootProject)];
|
|
6677
|
+
if (!projects.some((p) => p.config.tags && p.config.tags.length > 0)) {
|
|
6678
|
+
process.exitCode = 1;
|
|
6679
|
+
return this.printNoTestTagsFound();
|
|
6680
|
+
}
|
|
6681
|
+
for (const project of projects) {
|
|
6682
|
+
if (project.name) this.log(formatProjectName(project, ""));
|
|
6683
|
+
project.config.tags.forEach((tag) => {
|
|
6684
|
+
const tagLog = `${tag.name}${tag.description ? `: ${tag.description}` : ""}`;
|
|
6685
|
+
this.log(` ${tagLog}`);
|
|
6686
|
+
});
|
|
6687
|
+
}
|
|
6688
|
+
}
|
|
6563
6689
|
printNoTestFound(filters) {
|
|
6564
6690
|
const config = this.ctx.config;
|
|
6565
6691
|
if (config.watch && (config.changed || config.related?.length)) this.log(`No affected ${config.mode} files found\n`);
|
|
@@ -6992,35 +7118,6 @@ function stringToBytes(input, percentageReference) {
|
|
|
6992
7118
|
return null;
|
|
6993
7119
|
}
|
|
6994
7120
|
|
|
6995
|
-
async function getSpecificationsEnvironments(specifications) {
|
|
6996
|
-
const environments = /* @__PURE__ */ new WeakMap();
|
|
6997
|
-
const cache = /* @__PURE__ */ new Map();
|
|
6998
|
-
await Promise.all(specifications.map(async (spec) => {
|
|
6999
|
-
const { moduleId: filepath, project } = spec;
|
|
7000
|
-
// reuse if projects have the same test files
|
|
7001
|
-
let code = cache.get(filepath);
|
|
7002
|
-
if (!code) {
|
|
7003
|
-
code = await promises.readFile(filepath, "utf-8");
|
|
7004
|
-
cache.set(filepath, code);
|
|
7005
|
-
}
|
|
7006
|
-
// 1. Check for control comments in the file
|
|
7007
|
-
let env = code.match(/@(?:vitest|jest)-environment\s+([\w-]+)\b/)?.[1];
|
|
7008
|
-
// 2. Fallback to global env
|
|
7009
|
-
env ||= project.config.environment || "node";
|
|
7010
|
-
let envOptionsJson = code.match(/@(?:vitest|jest)-environment-options\s+(.+)/)?.[1];
|
|
7011
|
-
if (envOptionsJson?.endsWith("*/"))
|
|
7012
|
-
// Trim closing Docblock characters the above regex might have captured
|
|
7013
|
-
envOptionsJson = envOptionsJson.slice(0, -2);
|
|
7014
|
-
const envOptions = JSON.parse(envOptionsJson || "null");
|
|
7015
|
-
const environment = {
|
|
7016
|
-
name: env,
|
|
7017
|
-
options: envOptions ? { [env === "happy-dom" ? "happyDOM" : env]: envOptions } : null
|
|
7018
|
-
};
|
|
7019
|
-
environments.set(spec, environment);
|
|
7020
|
-
}));
|
|
7021
|
-
return environments;
|
|
7022
|
-
}
|
|
7023
|
-
|
|
7024
7121
|
const debug = createDebugger("vitest:browser:pool");
|
|
7025
7122
|
function createBrowserPool(vitest) {
|
|
7026
7123
|
const providers = /* @__PURE__ */ new Set();
|
|
@@ -7048,11 +7145,29 @@ function createBrowserPool(vitest) {
|
|
|
7048
7145
|
};
|
|
7049
7146
|
const runWorkspaceTests = async (method, specs) => {
|
|
7050
7147
|
const groupedFiles = /* @__PURE__ */ new Map();
|
|
7051
|
-
|
|
7148
|
+
const testFilesCode = /* @__PURE__ */ new Map();
|
|
7149
|
+
const testFileTags = /* @__PURE__ */ new WeakMap();
|
|
7150
|
+
await Promise.all(specs.map(async (spec) => {
|
|
7151
|
+
let code = testFilesCode.get(spec.moduleId);
|
|
7152
|
+
// TODO: this really should be done only once when collecting specifications
|
|
7153
|
+
if (code == null) {
|
|
7154
|
+
code = await readFile(spec.moduleId, "utf-8").catch(() => "");
|
|
7155
|
+
testFilesCode.set(spec.moduleId, code);
|
|
7156
|
+
}
|
|
7157
|
+
const { tags } = detectCodeBlock(code);
|
|
7158
|
+
testFileTags.set(spec, tags);
|
|
7159
|
+
}));
|
|
7160
|
+
// to keep the sorting, we need to iterate over specs separately
|
|
7161
|
+
for (const spec of specs) {
|
|
7162
|
+
const { project, moduleId, testLines, testIds, testNamePattern, testTagsFilter } = spec;
|
|
7052
7163
|
const files = groupedFiles.get(project) || [];
|
|
7053
7164
|
files.push({
|
|
7054
7165
|
filepath: moduleId,
|
|
7055
|
-
testLocations: testLines
|
|
7166
|
+
testLocations: testLines,
|
|
7167
|
+
testIds,
|
|
7168
|
+
testNamePattern,
|
|
7169
|
+
testTagsFilter,
|
|
7170
|
+
fileTags: testFileTags.get(spec)
|
|
7056
7171
|
});
|
|
7057
7172
|
groupedFiles.set(project, files);
|
|
7058
7173
|
}
|
|
@@ -7176,7 +7291,7 @@ class BrowserPool {
|
|
|
7176
7291
|
let page = this._traces.$(`vitest.browser.open`, {
|
|
7177
7292
|
context: this._otel.context,
|
|
7178
7293
|
attributes: { "vitest.browser.session_id": sessionId }
|
|
7179
|
-
}, () => this.openPage(sessionId));
|
|
7294
|
+
}, () => this.openPage(sessionId, { parallel: workerCount > 1 }));
|
|
7180
7295
|
page = page.then(() => {
|
|
7181
7296
|
// start running tests on the page when it's ready
|
|
7182
7297
|
this.runNextTest(method, sessionId);
|
|
@@ -7187,14 +7302,14 @@ class BrowserPool {
|
|
|
7187
7302
|
debug?.("all sessions are created");
|
|
7188
7303
|
return this._promise;
|
|
7189
7304
|
}
|
|
7190
|
-
async openPage(sessionId) {
|
|
7305
|
+
async openPage(sessionId, options) {
|
|
7191
7306
|
const sessionPromise = this.project.vitest._browserSessions.createSession(sessionId, this.project, this);
|
|
7192
7307
|
const browser = this.project.browser;
|
|
7193
7308
|
const url = new URL("/__vitest_test__/", this.options.origin);
|
|
7194
7309
|
url.searchParams.set("sessionId", sessionId);
|
|
7195
7310
|
const otelCarrier = this._traces.getContextCarrier();
|
|
7196
7311
|
if (otelCarrier) url.searchParams.set("otelCarrier", JSON.stringify(otelCarrier));
|
|
7197
|
-
const pagePromise = browser.provider.openPage(sessionId, url.toString());
|
|
7312
|
+
const pagePromise = browser.provider.openPage(sessionId, url.toString(), options);
|
|
7198
7313
|
await Promise.all([sessionPromise, pagePromise]);
|
|
7199
7314
|
}
|
|
7200
7315
|
getOrchestrator(sessionId) {
|
|
@@ -7364,6 +7479,34 @@ function createMethodsRPC(project, methodsOptions = {}) {
|
|
|
7364
7479
|
},
|
|
7365
7480
|
getCountOfFailedTests() {
|
|
7366
7481
|
return vitest.state.getCountOfFailedTests();
|
|
7482
|
+
},
|
|
7483
|
+
ensureModuleGraphEntry(id, importer) {
|
|
7484
|
+
const filepath = id.startsWith("file:") ? fileURLToPath(id) : id;
|
|
7485
|
+
const importerPath = importer.startsWith("file:") ? fileURLToPath(importer) : importer;
|
|
7486
|
+
// environment itself doesn't matter
|
|
7487
|
+
const moduleGraph = project.vite.environments.__vitest__?.moduleGraph;
|
|
7488
|
+
if (!moduleGraph) {
|
|
7489
|
+
// TODO: is it possible?
|
|
7490
|
+
console.error("no module graph for", id);
|
|
7491
|
+
return;
|
|
7492
|
+
}
|
|
7493
|
+
const importerNode = moduleGraph.getModuleById(importerPath) || moduleGraph.createFileOnlyEntry(importerPath);
|
|
7494
|
+
const moduleNode = moduleGraph.getModuleById(filepath) || moduleGraph.createFileOnlyEntry(filepath);
|
|
7495
|
+
if (!moduleGraph.idToModuleMap.has(importerPath)) {
|
|
7496
|
+
importerNode.id = importerPath;
|
|
7497
|
+
moduleGraph.idToModuleMap.set(importerPath, importerNode);
|
|
7498
|
+
}
|
|
7499
|
+
if (!moduleGraph.idToModuleMap.has(filepath)) {
|
|
7500
|
+
moduleNode.id = filepath;
|
|
7501
|
+
moduleGraph.idToModuleMap.set(filepath, moduleNode);
|
|
7502
|
+
}
|
|
7503
|
+
// this is checked by the "printError" function - TODO: is there a better way?
|
|
7504
|
+
moduleNode.transformResult = {
|
|
7505
|
+
code: " ",
|
|
7506
|
+
map: null
|
|
7507
|
+
};
|
|
7508
|
+
importerNode.importedModules.add(moduleNode);
|
|
7509
|
+
moduleNode.importers.add(importerNode);
|
|
7367
7510
|
}
|
|
7368
7511
|
};
|
|
7369
7512
|
}
|
|
@@ -8205,7 +8348,7 @@ function createPool(ctx) {
|
|
|
8205
8348
|
const taskGroups = [];
|
|
8206
8349
|
let workerId = 0;
|
|
8207
8350
|
const sorted = await sequencer.sort(specs);
|
|
8208
|
-
const environments = await
|
|
8351
|
+
const { environments, tags } = await getSpecificationsOptions(specs);
|
|
8209
8352
|
const groups = groupSpecs(sorted, environments);
|
|
8210
8353
|
const projectEnvs = /* @__PURE__ */ new WeakMap();
|
|
8211
8354
|
const projectExecArgvs = /* @__PURE__ */ new WeakMap();
|
|
@@ -8247,7 +8390,11 @@ function createPool(ctx) {
|
|
|
8247
8390
|
context: {
|
|
8248
8391
|
files: specs.map((spec) => ({
|
|
8249
8392
|
filepath: spec.moduleId,
|
|
8250
|
-
|
|
8393
|
+
fileTags: tags.get(spec),
|
|
8394
|
+
testLocations: spec.testLines,
|
|
8395
|
+
testNamePattern: spec.testNamePattern,
|
|
8396
|
+
testIds: spec.testIds,
|
|
8397
|
+
testTagsFilter: spec.testTagsFilter
|
|
8251
8398
|
})),
|
|
8252
8399
|
invalidates,
|
|
8253
8400
|
providedContext: project.getProvidedContext(),
|
|
@@ -8521,8 +8668,13 @@ function serializeConfig(project) {
|
|
|
8521
8668
|
experimental: {
|
|
8522
8669
|
fsModuleCache: config.experimental.fsModuleCache ?? false,
|
|
8523
8670
|
printImportBreakdown: config.experimental.printImportBreakdown,
|
|
8671
|
+
viteModuleRunner: config.experimental.viteModuleRunner ?? true,
|
|
8672
|
+
nodeLoader: config.experimental.nodeLoader ?? true,
|
|
8524
8673
|
openTelemetry: config.experimental.openTelemetry
|
|
8525
|
-
}
|
|
8674
|
+
},
|
|
8675
|
+
tags: config.tags || [],
|
|
8676
|
+
tagsFilter: config.tagsFilter,
|
|
8677
|
+
strictTags: config.strictTags ?? true
|
|
8526
8678
|
};
|
|
8527
8679
|
}
|
|
8528
8680
|
|
|
@@ -9580,15 +9732,12 @@ function WorkspaceVitestPlugin(project, options) {
|
|
|
9580
9732
|
label: name,
|
|
9581
9733
|
color
|
|
9582
9734
|
} };
|
|
9735
|
+
vitestConfig.experimental ??= {};
|
|
9583
9736
|
// always inherit the global `fsModuleCache` value even without `extends: true`
|
|
9584
|
-
if (testConfig.experimental?.fsModuleCache == null && project.vitest.config.experimental?.fsModuleCache
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
if (testConfig.experimental?.fsModuleCachePath == null && project.vitest.config.experimental?.fsModuleCachePath !== null) {
|
|
9589
|
-
vitestConfig.experimental ??= {};
|
|
9590
|
-
vitestConfig.experimental.fsModuleCachePath = project.vitest.config.experimental.fsModuleCachePath;
|
|
9591
|
-
}
|
|
9737
|
+
if (testConfig.experimental?.fsModuleCache == null && project.vitest.config.experimental?.fsModuleCache != null) vitestConfig.experimental.fsModuleCache = project.vitest.config.experimental.fsModuleCache;
|
|
9738
|
+
if (testConfig.experimental?.fsModuleCachePath == null && project.vitest.config.experimental?.fsModuleCachePath != null) vitestConfig.experimental.fsModuleCachePath = project.vitest.config.experimental.fsModuleCachePath;
|
|
9739
|
+
if (testConfig.experimental?.viteModuleRunner == null && project.vitest.config.experimental?.viteModuleRunner != null) vitestConfig.experimental.viteModuleRunner = project.vitest.config.experimental.viteModuleRunner;
|
|
9740
|
+
if (testConfig.experimental?.nodeLoader == null && project.vitest.config.experimental?.nodeLoader != null) vitestConfig.experimental.nodeLoader = project.vitest.config.experimental.nodeLoader;
|
|
9592
9741
|
return {
|
|
9593
9742
|
base: "/",
|
|
9594
9743
|
environments: { __vitest__: { dev: {} } },
|
|
@@ -9807,7 +9956,7 @@ function matchPattern(id, moduleDirectories, patterns) {
|
|
|
9807
9956
|
|
|
9808
9957
|
class TestSpecification {
|
|
9809
9958
|
/**
|
|
9810
|
-
* The task
|
|
9959
|
+
* The task id associated with the test module.
|
|
9811
9960
|
*/
|
|
9812
9961
|
taskId;
|
|
9813
9962
|
/**
|
|
@@ -9815,29 +9964,51 @@ class TestSpecification {
|
|
|
9815
9964
|
*/
|
|
9816
9965
|
project;
|
|
9817
9966
|
/**
|
|
9818
|
-
* The
|
|
9967
|
+
* The id of the module in the Vite module graph. It is usually an absolute file path.
|
|
9819
9968
|
*/
|
|
9820
9969
|
moduleId;
|
|
9821
9970
|
/**
|
|
9822
|
-
* The current test pool. It's possible to have multiple pools in a single test project with `
|
|
9823
|
-
* @experimental In
|
|
9971
|
+
* The current test pool. It's possible to have multiple pools in a single test project with `typecheck.enabled`.
|
|
9972
|
+
* @experimental In later versions, the project will only support a single pool.
|
|
9824
9973
|
*/
|
|
9825
9974
|
pool;
|
|
9826
9975
|
/**
|
|
9827
9976
|
* Line numbers of the test locations to run.
|
|
9828
9977
|
*/
|
|
9829
9978
|
testLines;
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9979
|
+
/**
|
|
9980
|
+
* Regular expression pattern to filter test names.
|
|
9981
|
+
*/
|
|
9982
|
+
testNamePattern;
|
|
9983
|
+
/**
|
|
9984
|
+
* The ids of tasks inside of this specification to run.
|
|
9985
|
+
*/
|
|
9986
|
+
testIds;
|
|
9987
|
+
/**
|
|
9988
|
+
* The tags of tests to run.
|
|
9989
|
+
*/
|
|
9990
|
+
testTagsFilter;
|
|
9991
|
+
/**
|
|
9992
|
+
* This class represents a test suite for a test module within a single project.
|
|
9993
|
+
* @internal
|
|
9994
|
+
*/
|
|
9995
|
+
constructor(project, moduleId, pool, testLinesOrOptions) {
|
|
9996
|
+
const projectName = project.config.name;
|
|
9997
|
+
const hashName = pool !== "typescript" ? projectName : projectName ? `${projectName}:__typecheck__` : "__typecheck__";
|
|
9833
9998
|
this.taskId = generateFileHash(relative(project.config.root, moduleId), hashName);
|
|
9834
9999
|
this.project = project;
|
|
9835
10000
|
this.moduleId = moduleId;
|
|
9836
10001
|
this.pool = pool;
|
|
9837
|
-
this.testLines =
|
|
10002
|
+
if (Array.isArray(testLinesOrOptions)) this.testLines = testLinesOrOptions;
|
|
10003
|
+
else if (testLinesOrOptions && typeof testLinesOrOptions === "object") {
|
|
10004
|
+
this.testLines = testLinesOrOptions.testLines;
|
|
10005
|
+
this.testNamePattern = testLinesOrOptions.testNamePattern;
|
|
10006
|
+
this.testIds = testLinesOrOptions.testIds;
|
|
10007
|
+
this.testTagsFilter = testLinesOrOptions.testTagsFilter;
|
|
10008
|
+
}
|
|
9838
10009
|
}
|
|
9839
10010
|
/**
|
|
9840
|
-
* Test module associated with the specification.
|
|
10011
|
+
* Test module associated with the specification. This will be `undefined` if tests have not been run yet.
|
|
9841
10012
|
*/
|
|
9842
10013
|
get testModule() {
|
|
9843
10014
|
const task = this.project.vitest.state.idMap.get(this.taskId);
|
|
@@ -9853,40 +10024,15 @@ class TestSpecification {
|
|
|
9853
10024
|
this.moduleId,
|
|
9854
10025
|
{
|
|
9855
10026
|
pool: this.pool,
|
|
9856
|
-
testLines: this.testLines
|
|
10027
|
+
testLines: this.testLines,
|
|
10028
|
+
testIds: this.testIds,
|
|
10029
|
+
testNamePattern: this.testNamePattern,
|
|
10030
|
+
testTagsFilter: this.testTagsFilter
|
|
9857
10031
|
}
|
|
9858
10032
|
];
|
|
9859
10033
|
}
|
|
9860
10034
|
}
|
|
9861
10035
|
|
|
9862
|
-
async function createViteServer(inlineConfig) {
|
|
9863
|
-
// Vite prints an error (https://github.com/vitejs/vite/issues/14328)
|
|
9864
|
-
// But Vitest works correctly either way
|
|
9865
|
-
const error = console.error;
|
|
9866
|
-
console.error = (...args) => {
|
|
9867
|
-
if (typeof args[0] === "string" && args[0].includes("WebSocket server error:")) return;
|
|
9868
|
-
error(...args);
|
|
9869
|
-
};
|
|
9870
|
-
const server = await createServer(inlineConfig);
|
|
9871
|
-
console.error = error;
|
|
9872
|
-
return server;
|
|
9873
|
-
}
|
|
9874
|
-
function isFileServingAllowed(configOrUrl, urlOrServer) {
|
|
9875
|
-
const config = typeof urlOrServer === "string" ? configOrUrl : urlOrServer.config;
|
|
9876
|
-
const url = typeof urlOrServer === "string" ? urlOrServer : configOrUrl;
|
|
9877
|
-
if (!config.server.fs.strict) return true;
|
|
9878
|
-
return isFileLoadingAllowed(config, fsPathFromUrl(url));
|
|
9879
|
-
}
|
|
9880
|
-
const FS_PREFIX = "/@fs/";
|
|
9881
|
-
const VOLUME_RE = /^[A-Z]:/i;
|
|
9882
|
-
function fsPathFromId(id) {
|
|
9883
|
-
const fsPath = normalizePath(id.startsWith(FS_PREFIX) ? id.slice(5) : id);
|
|
9884
|
-
return fsPath[0] === "/" || VOLUME_RE.test(fsPath) ? fsPath : `/${fsPath}`;
|
|
9885
|
-
}
|
|
9886
|
-
function fsPathFromUrl(url) {
|
|
9887
|
-
return fsPathFromId(cleanUrl(url));
|
|
9888
|
-
}
|
|
9889
|
-
|
|
9890
10036
|
class TestProject {
|
|
9891
10037
|
/**
|
|
9892
10038
|
* The global Vitest instance.
|
|
@@ -9962,8 +10108,8 @@ class TestProject {
|
|
|
9962
10108
|
* Creates a new test specification. Specifications describe how to run tests.
|
|
9963
10109
|
* @param moduleId The file path
|
|
9964
10110
|
*/
|
|
9965
|
-
createSpecification(moduleId,
|
|
9966
|
-
return new TestSpecification(this, moduleId, pool || getFilePoolName(this),
|
|
10111
|
+
createSpecification(moduleId, locationsOrOptions, pool) {
|
|
10112
|
+
return new TestSpecification(this, moduleId, pool || getFilePoolName(this), locationsOrOptions);
|
|
9967
10113
|
}
|
|
9968
10114
|
toJSON() {
|
|
9969
10115
|
return {
|
|
@@ -10188,7 +10334,7 @@ class TestProject {
|
|
|
10188
10334
|
if (!this.closingPromise) this.closingPromise = Promise.all([
|
|
10189
10335
|
this.vite?.close(),
|
|
10190
10336
|
this.typechecker?.stop(),
|
|
10191
|
-
this.browser?.close(),
|
|
10337
|
+
(this.browser || this._parent?._parentBrowser?.vite)?.close(),
|
|
10192
10338
|
this.clearTmpDir()
|
|
10193
10339
|
].filter(Boolean)).then(() => {
|
|
10194
10340
|
if (!this.runner.isClosed()) return this.runner.close();
|
|
@@ -10227,7 +10373,11 @@ class TestProject {
|
|
|
10227
10373
|
this._serializedDefines = createDefinesScript(server.config.define);
|
|
10228
10374
|
this._fetcher = createFetchModuleFunction(this._resolver, this._config, this.vitest._fsCache, this.vitest._traces, this.tmpDir);
|
|
10229
10375
|
const environment = server.environments.__vitest__;
|
|
10230
|
-
this.runner = new ServerModuleRunner(environment, this._fetcher, this._config);
|
|
10376
|
+
this.runner = this._config.experimental.viteModuleRunner === false ? new NativeModuleRunner(this._config.root) : new ServerModuleRunner(environment, this._fetcher, this._config);
|
|
10377
|
+
}
|
|
10378
|
+
/** @internal */
|
|
10379
|
+
_getViteEnvironments() {
|
|
10380
|
+
return [...Object.values(this.browser?.vite.environments || {}), ...Object.values(this.vite.environments || {})];
|
|
10231
10381
|
}
|
|
10232
10382
|
_serializeOverriddenConfig() {
|
|
10233
10383
|
// TODO: serialize the config _once_ or when needed
|
|
@@ -10342,7 +10492,8 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
|
|
|
10342
10492
|
"printConsoleTrace",
|
|
10343
10493
|
"inspect",
|
|
10344
10494
|
"inspectBrk",
|
|
10345
|
-
"fileParallelism"
|
|
10495
|
+
"fileParallelism",
|
|
10496
|
+
"tagsFilter"
|
|
10346
10497
|
].reduce((acc, name) => {
|
|
10347
10498
|
if (name in cliOptions) acc[name] = cliOptions[name];
|
|
10348
10499
|
return acc;
|
|
@@ -10361,6 +10512,14 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
|
|
|
10361
10512
|
...options,
|
|
10362
10513
|
root,
|
|
10363
10514
|
configFile,
|
|
10515
|
+
plugins: [{
|
|
10516
|
+
name: "vitest:tags",
|
|
10517
|
+
configResolved(config) {
|
|
10518
|
+
config.test ??= {};
|
|
10519
|
+
config.test.tags = options.test?.tags;
|
|
10520
|
+
},
|
|
10521
|
+
api: { vitest: { experimental: { ignoreFsModuleCache: true } } }
|
|
10522
|
+
}, ...options.plugins || []],
|
|
10364
10523
|
test: {
|
|
10365
10524
|
...options.test,
|
|
10366
10525
|
...cliOverrides
|
|
@@ -10827,6 +10986,10 @@ class TestCase extends ReportedTaskImplementation {
|
|
|
10827
10986
|
* Parent suite. If the test was called directly inside the module, the parent will be the module itself.
|
|
10828
10987
|
*/
|
|
10829
10988
|
parent;
|
|
10989
|
+
/**
|
|
10990
|
+
* Tags associated with the test.
|
|
10991
|
+
*/
|
|
10992
|
+
tags;
|
|
10830
10993
|
/** @internal */
|
|
10831
10994
|
constructor(task, project) {
|
|
10832
10995
|
super(task, project);
|
|
@@ -10836,6 +10999,7 @@ class TestCase extends ReportedTaskImplementation {
|
|
|
10836
10999
|
if (suite) this.parent = getReportedTask(project, suite);
|
|
10837
11000
|
else this.parent = this.module;
|
|
10838
11001
|
this.options = buildOptions(task);
|
|
11002
|
+
this.tags = this.options.tags || [];
|
|
10839
11003
|
}
|
|
10840
11004
|
/**
|
|
10841
11005
|
* Full name of the test including all parent suites separated with `>`.
|
|
@@ -10912,6 +11076,13 @@ class TestCase extends ReportedTaskImplementation {
|
|
|
10912
11076
|
flaky: !!result.retryCount && result.state === "pass" && result.retryCount > 0
|
|
10913
11077
|
};
|
|
10914
11078
|
}
|
|
11079
|
+
/**
|
|
11080
|
+
* Returns a new test specification that can be used to filter or run this specific test case.
|
|
11081
|
+
*/
|
|
11082
|
+
toTestSpecification() {
|
|
11083
|
+
const isTypecheck = this.task.meta.typecheck === true;
|
|
11084
|
+
return this.project.createSpecification(this.module.moduleId, { testIds: [this.id] }, isTypecheck ? "typecheck" : void 0);
|
|
11085
|
+
}
|
|
10915
11086
|
}
|
|
10916
11087
|
class TestCollection {
|
|
10917
11088
|
#task;
|
|
@@ -11031,6 +11202,14 @@ class TestSuite extends SuiteImplementation {
|
|
|
11031
11202
|
return getSuiteState(this.task);
|
|
11032
11203
|
}
|
|
11033
11204
|
/**
|
|
11205
|
+
* Returns a new test specification that can be used to filter or run this specific test suite.
|
|
11206
|
+
*/
|
|
11207
|
+
toTestSpecification() {
|
|
11208
|
+
const isTypecheck = this.task.meta.typecheck === true;
|
|
11209
|
+
const testIds = [...this.children.allTests()].map((test) => test.id);
|
|
11210
|
+
return this.project.createSpecification(this.module.moduleId, { testIds }, isTypecheck ? "typecheck" : void 0);
|
|
11211
|
+
}
|
|
11212
|
+
/**
|
|
11034
11213
|
* Full name of the suite including all parent suites separated with `>`.
|
|
11035
11214
|
*/
|
|
11036
11215
|
get fullName() {
|
|
@@ -11066,6 +11245,13 @@ class TestModule extends SuiteImplementation {
|
|
|
11066
11245
|
else if (typeof task.viteEnvironment === "string") this.viteEnvironment = project.vite.environments[task.viteEnvironment];
|
|
11067
11246
|
}
|
|
11068
11247
|
/**
|
|
11248
|
+
* Returns a new test specification that can be used to filter or run this specific test module.
|
|
11249
|
+
*/
|
|
11250
|
+
toTestSpecification() {
|
|
11251
|
+
const isTypecheck = this.task.meta.typecheck === true;
|
|
11252
|
+
return this.project.createSpecification(this.moduleId, void 0, isTypecheck ? "typecheck" : void 0);
|
|
11253
|
+
}
|
|
11254
|
+
/**
|
|
11069
11255
|
* Checks the running state of the test file.
|
|
11070
11256
|
*/
|
|
11071
11257
|
state() {
|
|
@@ -11099,6 +11285,8 @@ function buildOptions(task) {
|
|
|
11099
11285
|
shuffle: task.shuffle,
|
|
11100
11286
|
retry: task.retry,
|
|
11101
11287
|
repeats: task.repeats,
|
|
11288
|
+
tags: task.tags,
|
|
11289
|
+
timeout: task.type === "test" ? task.timeout : void 0,
|
|
11102
11290
|
mode: task.mode
|
|
11103
11291
|
};
|
|
11104
11292
|
}
|
|
@@ -11279,6 +11467,19 @@ class StateManager {
|
|
|
11279
11467
|
}
|
|
11280
11468
|
}
|
|
11281
11469
|
|
|
11470
|
+
function populateProjectsTags(rootProject, projects) {
|
|
11471
|
+
// Include root project if not already in the list
|
|
11472
|
+
const allProjects = projects.includes(rootProject) ? projects : [rootProject, ...projects];
|
|
11473
|
+
// Collect all tags from all projects (first definition wins)
|
|
11474
|
+
const globalTags = /* @__PURE__ */ new Map();
|
|
11475
|
+
for (const project of allProjects) for (const tag of project.config.tags || []) if (!globalTags.has(tag.name)) globalTags.set(tag.name, tag);
|
|
11476
|
+
// Add missing tags to each project (without overriding local definitions)
|
|
11477
|
+
for (const project of allProjects) {
|
|
11478
|
+
const projectTagNames = new Set(project.config.tags.map((t) => t.name));
|
|
11479
|
+
for (const [tagName, tagDef] of globalTags) if (!projectTagNames.has(tagName)) project.config.tags.push(tagDef);
|
|
11480
|
+
}
|
|
11481
|
+
}
|
|
11482
|
+
|
|
11282
11483
|
const types = {
|
|
11283
11484
|
'application/andrew-inset': ['ez'],
|
|
11284
11485
|
'application/appinstaller': ['appinstaller'],
|
|
@@ -11918,6 +12119,9 @@ class VitestWatcher {
|
|
|
11918
12119
|
this._onRerun.push(cb);
|
|
11919
12120
|
return this;
|
|
11920
12121
|
}
|
|
12122
|
+
close() {
|
|
12123
|
+
this.vitest.vite.watcher.close();
|
|
12124
|
+
}
|
|
11921
12125
|
unregisterWatcher = noop;
|
|
11922
12126
|
registerWatcher() {
|
|
11923
12127
|
const watcher = this.vitest.vite.watcher;
|
|
@@ -12017,7 +12221,9 @@ class VitestWatcher {
|
|
|
12017
12221
|
}
|
|
12018
12222
|
if (this.handleSetupFile(filepath)) return true;
|
|
12019
12223
|
const projects = this.vitest.projects.filter((project) => {
|
|
12020
|
-
return
|
|
12224
|
+
return project._getViteEnvironments().some(({ moduleGraph }) => {
|
|
12225
|
+
return moduleGraph.getModulesByFile(filepath)?.size;
|
|
12226
|
+
});
|
|
12021
12227
|
});
|
|
12022
12228
|
if (!projects.length) {
|
|
12023
12229
|
// if there are no modules it's possible that server was restarted
|
|
@@ -12030,8 +12236,8 @@ class VitestWatcher {
|
|
|
12030
12236
|
}
|
|
12031
12237
|
const files = [];
|
|
12032
12238
|
for (const project of projects) {
|
|
12033
|
-
const
|
|
12034
|
-
if (!
|
|
12239
|
+
const environmentMods = project._getViteEnvironments().map(({ moduleGraph }) => moduleGraph.getModulesByFile(filepath));
|
|
12240
|
+
if (!environmentMods.length) continue;
|
|
12035
12241
|
this.invalidates.add(filepath);
|
|
12036
12242
|
// one of test files that we already run, or one of test files that we can run
|
|
12037
12243
|
if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
|
|
@@ -12040,7 +12246,7 @@ class VitestWatcher {
|
|
|
12040
12246
|
continue;
|
|
12041
12247
|
}
|
|
12042
12248
|
let rerun = false;
|
|
12043
|
-
for (const mod of mods) mod.importers.forEach((i) => {
|
|
12249
|
+
for (const mods of environmentMods) for (const mod of mods || []) mod.importers.forEach((i) => {
|
|
12044
12250
|
if (!i.file) return;
|
|
12045
12251
|
if (this.handleFileChanged(i.file)) rerun = true;
|
|
12046
12252
|
});
|
|
@@ -12102,6 +12308,7 @@ class Vitest {
|
|
|
12102
12308
|
/** @internal */ reporters = [];
|
|
12103
12309
|
/** @internal */ runner;
|
|
12104
12310
|
/** @internal */ _testRun = void 0;
|
|
12311
|
+
/** @internal */ _config;
|
|
12105
12312
|
/** @internal */ _resolver;
|
|
12106
12313
|
/** @internal */ _fetcher;
|
|
12107
12314
|
/** @internal */ _fsCache;
|
|
@@ -12111,7 +12318,6 @@ class Vitest {
|
|
|
12111
12318
|
restartsCount = 0;
|
|
12112
12319
|
specifications;
|
|
12113
12320
|
pool;
|
|
12114
|
-
_config;
|
|
12115
12321
|
_vite;
|
|
12116
12322
|
_state;
|
|
12117
12323
|
_cache;
|
|
@@ -12199,7 +12405,7 @@ class Vitest {
|
|
|
12199
12405
|
this._fsCache = new FileSystemModuleCache(this);
|
|
12200
12406
|
this._fetcher = createFetchModuleFunction(this._resolver, this._config, this._fsCache, this._traces, this._tmpDir);
|
|
12201
12407
|
const environment = server.environments.__vitest__;
|
|
12202
|
-
this.runner = new ServerModuleRunner(environment, this._fetcher, resolved);
|
|
12408
|
+
this.runner = resolved.experimental.viteModuleRunner === false ? new NativeModuleRunner(resolved.root) : new ServerModuleRunner(environment, this._fetcher, resolved);
|
|
12203
12409
|
if (this.config.watch) {
|
|
12204
12410
|
// hijack server restart
|
|
12205
12411
|
const serverRestart = server.restart;
|
|
@@ -12248,6 +12454,9 @@ class Vitest {
|
|
|
12248
12454
|
}
|
|
12249
12455
|
if (!this.coreWorkspaceProject) this.coreWorkspaceProject = TestProject._createBasicProject(this);
|
|
12250
12456
|
if (this.config.testNamePattern) this.configOverride.testNamePattern = this.config.testNamePattern;
|
|
12457
|
+
// populate will merge all configs into every project,
|
|
12458
|
+
// we don't want that when just listing tags
|
|
12459
|
+
if (!this.config.listTags) populateProjectsTags(this.coreWorkspaceProject, this.projects);
|
|
12251
12460
|
this.reporters = resolved.mode === "benchmark" ? await createBenchmarkReporters(toArray(resolved.benchmark?.reporters), this.runner) : await createReporters(resolved.reporters, this);
|
|
12252
12461
|
await this._fsCache.ensureCacheIntegrity();
|
|
12253
12462
|
await Promise.all([...this._onSetServer.map((fn) => fn()), this._traces.waitInit()]);
|
|
@@ -12257,6 +12466,24 @@ class Vitest {
|
|
|
12257
12466
|
if (this.configOverride.coverage?.enabled === false) return null;
|
|
12258
12467
|
return this._coverageProvider;
|
|
12259
12468
|
}
|
|
12469
|
+
async listTags() {
|
|
12470
|
+
const listTags = this.config.listTags;
|
|
12471
|
+
if (typeof listTags === "boolean") this.logger.printTags();
|
|
12472
|
+
else if (listTags === "json") if (![this.getRootProject(), ...this.projects].some((p) => p.config.tags && p.config.tags.length > 0)) {
|
|
12473
|
+
process.exitCode = 1;
|
|
12474
|
+
this.logger.printNoTestTagsFound();
|
|
12475
|
+
} else {
|
|
12476
|
+
const manifest = {
|
|
12477
|
+
tags: this.config.tags,
|
|
12478
|
+
projects: this.projects.filter((p) => p !== this.coreWorkspaceProject).map((p) => ({
|
|
12479
|
+
name: p.name,
|
|
12480
|
+
tags: p.config.tags
|
|
12481
|
+
}))
|
|
12482
|
+
};
|
|
12483
|
+
this.logger.log(JSON.stringify(manifest, null, 2));
|
|
12484
|
+
}
|
|
12485
|
+
else throw new Error(`Unknown value for "test.listTags": ${listTags}`);
|
|
12486
|
+
}
|
|
12260
12487
|
async enableCoverage() {
|
|
12261
12488
|
this.configOverride.coverage = {};
|
|
12262
12489
|
this.configOverride.coverage.enabled = true;
|
|
@@ -12476,7 +12703,7 @@ class Vitest {
|
|
|
12476
12703
|
}
|
|
12477
12704
|
this.filenamePattern = filters && filters?.length > 0 ? filters : void 0;
|
|
12478
12705
|
startSpan.setAttribute("vitest.start.filters", this.filenamePattern || []);
|
|
12479
|
-
const
|
|
12706
|
+
const specifications = await this._traces.$("vitest.config.resolve_include_glob", async () => {
|
|
12480
12707
|
const specifications = await this.specifications.getRelevantTestSpecifications(filters);
|
|
12481
12708
|
startSpan.setAttribute("vitest.start.specifications", specifications.map((s) => {
|
|
12482
12709
|
const relativeModuleId = relative(s.project.config.root, s.moduleId);
|
|
@@ -12486,7 +12713,7 @@ class Vitest {
|
|
|
12486
12713
|
return specifications;
|
|
12487
12714
|
});
|
|
12488
12715
|
// if run with --changed, don't exit if no tests are found
|
|
12489
|
-
if (!
|
|
12716
|
+
if (!specifications.length) {
|
|
12490
12717
|
await this._traces.$("vitest.test_run", async () => {
|
|
12491
12718
|
await this._testRun.start([]);
|
|
12492
12719
|
const coverage = await this.coverageProvider?.generateCoverage?.({ allTestsRun: true });
|
|
@@ -12500,10 +12727,10 @@ class Vitest {
|
|
|
12500
12727
|
testModules: [],
|
|
12501
12728
|
unhandledErrors: []
|
|
12502
12729
|
};
|
|
12503
|
-
if (
|
|
12730
|
+
if (specifications.length) {
|
|
12504
12731
|
// populate once, update cache on watch
|
|
12505
|
-
await this.cache.stats.populateStats(this.config.root,
|
|
12506
|
-
testModules = await this.runFiles(
|
|
12732
|
+
await this.cache.stats.populateStats(this.config.root, specifications);
|
|
12733
|
+
testModules = await this.runFiles(specifications, true);
|
|
12507
12734
|
}
|
|
12508
12735
|
if (this.config.watch) await this.report("onWatcherStart");
|
|
12509
12736
|
return testModules;
|
|
@@ -12562,6 +12789,19 @@ class Vitest {
|
|
|
12562
12789
|
return this.runFiles(specifications, allTestsRun);
|
|
12563
12790
|
}
|
|
12564
12791
|
/**
|
|
12792
|
+
* Runs tests for the given file paths. This does not trigger `onWatcher*` events.
|
|
12793
|
+
* @param filepaths A list of file paths to run tests for.
|
|
12794
|
+
* @param allTestsRun Indicates whether all tests were run. This only matters for coverage.
|
|
12795
|
+
*/
|
|
12796
|
+
async runTestFiles(filepaths, allTestsRun = false) {
|
|
12797
|
+
const specifications = await this.specifications.getRelevantTestSpecifications(filepaths);
|
|
12798
|
+
if (!specifications.length) return {
|
|
12799
|
+
testModules: [],
|
|
12800
|
+
unhandledErrors: []
|
|
12801
|
+
};
|
|
12802
|
+
return this.runFiles(specifications, allTestsRun);
|
|
12803
|
+
}
|
|
12804
|
+
/**
|
|
12565
12805
|
* Rerun files and trigger `onWatcherRerun`, `onWatcherStart` and `onTestsRerun` events.
|
|
12566
12806
|
* @param specifications A list of specifications to run.
|
|
12567
12807
|
* @param allTestsRun Indicates whether all tests were run. This only matters for coverage.
|
|
@@ -12746,8 +12986,12 @@ class Vitest {
|
|
|
12746
12986
|
async rerunTask(id) {
|
|
12747
12987
|
const task = this.state.idMap.get(id);
|
|
12748
12988
|
if (!task) throw new Error(`Task ${id} was not found`);
|
|
12749
|
-
const
|
|
12750
|
-
|
|
12989
|
+
const reportedTask = this.state.getReportedEntityById(id);
|
|
12990
|
+
if (!reportedTask) throw new Error(`Test specification for task ${id} was not found`);
|
|
12991
|
+
const specifications = [reportedTask.toTestSpecification()];
|
|
12992
|
+
await Promise.all([this.report("onWatcherRerun", [task.file.filepath], "tasks" in task ? "rerun suite" : "rerun test"), ...this._onUserTestsRerun.map((fn) => fn(specifications))]);
|
|
12993
|
+
await this.runFiles(specifications, false);
|
|
12994
|
+
await this.report("onWatcherStart", ["module" in reportedTask ? reportedTask.module.task : reportedTask.task]);
|
|
12751
12995
|
}
|
|
12752
12996
|
/** @internal */
|
|
12753
12997
|
async changeProjectName(pattern) {
|
|
@@ -13055,7 +13299,8 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
|
|
|
13055
13299
|
// store defines for globalThis to make them
|
|
13056
13300
|
// reassignable when running in worker in src/runtime/setup.ts
|
|
13057
13301
|
const originalDefine = { ...viteConfig.define };
|
|
13058
|
-
|
|
13302
|
+
const defines = deleteDefineConfig(viteConfig);
|
|
13303
|
+
options.defines = defines;
|
|
13059
13304
|
options.viteDefine = originalDefine;
|
|
13060
13305
|
let open = false;
|
|
13061
13306
|
if (testConfig.ui && testConfig.open) open = testConfig.uiBase ?? "/__vitest__/";
|
|
@@ -13510,7 +13755,8 @@ async function startVitest(mode, cliFilters = [], options = {}, viteOverrides, v
|
|
|
13510
13755
|
else ctx.start(cliFilters);
|
|
13511
13756
|
});
|
|
13512
13757
|
try {
|
|
13513
|
-
if (ctx.config.
|
|
13758
|
+
if (ctx.config.listTags) await ctx.listTags();
|
|
13759
|
+
else if (ctx.config.clearCache) await ctx.experimental_clearCache();
|
|
13514
13760
|
else if (ctx.config.mergeReports) await ctx.mergeReports();
|
|
13515
13761
|
else if (ctx.config.standalone) await ctx.init();
|
|
13516
13762
|
else await ctx.start(cliFilters);
|