vitest 4.0.0-beta.4 → 4.0.0-beta.6
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 +1 -1
- package/dist/browser.d.ts +8 -9
- package/dist/browser.js +3 -2
- package/dist/chunks/base.BXI97p6t.js +39 -0
- package/dist/chunks/{benchmark.CYdenmiT.js → benchmark.UW6Ezvxy.js} +6 -8
- package/dist/chunks/{browser.d.BRP8scJf.d.ts → browser.d.Cawq_X_N.d.ts} +1 -1
- package/dist/chunks/{cac.CY0IAxC4.js → cac.WE-urWw5.js} +38 -115
- package/dist/chunks/{cli-api.B8xRY9Zt.js → cli-api.CZz3evYC.js} +931 -1439
- package/dist/chunks/{config.d.DZo8c7fw.d.ts → config.d.CKNVOKm0.d.ts} +3 -8
- package/dist/chunks/{console.DoJHFxmj.js → console.B0quX7yH.js} +32 -68
- package/dist/chunks/{constants.CXzqaLmq.js → constants.D_Q9UYh-.js} +1 -6
- package/dist/chunks/{coverage.C84l9G-M.js → coverage.BPRS6xgn.js} +395 -665
- package/dist/chunks/{coverage.DVF1vEu8.js → coverage.D_JHT54q.js} +2 -2
- package/dist/chunks/{coverage.d.CNYjU4GF.d.ts → coverage.d.BZtK59WP.d.ts} +7 -5
- package/dist/chunks/{creator.yfA2ExGt.js → creator.KEg6n5IC.js} +29 -75
- package/dist/chunks/{date.Bq6ZW5rf.js → date.-jtEtIeV.js} +6 -17
- package/dist/chunks/{environment.d.Bhm9oc0v.d.ts → environment.d.2fYMoz3o.d.ts} +26 -4
- package/dist/chunks/{git.BVQ8w_Sw.js → git.BFNcloKD.js} +1 -2
- package/dist/chunks/{global.d.DAhT2emn.d.ts → global.d.K6uBQHzY.d.ts} +1 -1
- package/dist/chunks/{globals.Dgo-vS5G.js → globals.lgsmH00r.js} +7 -6
- package/dist/chunks/{index.D3SKT3tv.js → index.7w0eqmYM.js} +14 -24
- package/dist/chunks/{index.D1_MsKEt.js → index.AR8aAkCC.js} +4 -2
- package/dist/chunks/{index.CmSc2RE5.js → index.BG0gqZH-.js} +43 -106
- package/dist/chunks/{index.CtUvr1c8.js → index.CsFXYRkW.js} +27 -46
- package/dist/chunks/{index.Bz6b0Ib7.js → index.VNI-1z5c.js} +276 -604
- package/dist/chunks/{inspector.C914Efll.js → inspector.CvQD-Nie.js} +10 -25
- package/dist/chunks/moduleRunner.d.8kKUsuDg.d.ts +202 -0
- package/dist/chunks/moduleTransport.I-bgQy0S.js +19 -0
- package/dist/chunks/{node.fjCdwEIl.js → node.BOqcT2jW.js} +1 -1
- package/dist/chunks/{plugin.d.CLhMcYdD.d.ts → plugin.d.DuiQJfUL.d.ts} +1 -1
- package/dist/chunks/{reporters.d.DWg40D2B.d.ts → reporters.d.CqR9-CDJ.d.ts} +52 -101
- package/dist/chunks/resolver.Bx6lE0iq.js +119 -0
- package/dist/chunks/{rpc.jnQO9F8a.js → rpc.RpPylpp0.js} +7 -21
- package/dist/chunks/runBaseTests.D6sfuWBM.js +99 -0
- package/dist/chunks/{setup-common.Ebx5x0eP.js → setup-common.hLGRxhC8.js} +15 -27
- package/dist/chunks/startModuleRunner.C8TW8zTN.js +655 -0
- package/dist/chunks/{typechecker.CMNPqJOo.js → typechecker.Cd1wvxUM.js} +97 -209
- package/dist/chunks/{utils.CcGm2cd1.js → utils.C2YI6McM.js} +4 -13
- package/dist/chunks/{utils.XdZDrNZV.js → utils.C7__0Iv5.js} +7 -17
- package/dist/chunks/{vi.CA0EPI9Y.js → vi.BfdOiD4j.js} +116 -269
- package/dist/chunks/{vm.BUnLJt_P.js → vm.BHBje7cC.js} +101 -225
- package/dist/chunks/{worker.d.zjyR34Pb.d.ts → worker.d.D9QWnzAe.d.ts} +16 -13
- package/dist/chunks/{worker.d.C-1AbnVe.d.ts → worker.d.Db-UVmXc.d.ts} +1 -1
- package/dist/cli.js +4 -4
- package/dist/config.cjs +3 -9
- package/dist/config.d.ts +10 -12
- package/dist/config.js +1 -1
- package/dist/coverage.d.ts +10 -11
- package/dist/coverage.js +5 -6
- package/dist/environments.d.ts +2 -2
- package/dist/environments.js +1 -1
- package/dist/index.d.ts +10 -9
- package/dist/index.js +6 -5
- package/dist/module-evaluator.d.ts +12 -0
- package/dist/module-evaluator.js +276 -0
- package/dist/module-runner.js +15 -0
- package/dist/node.d.ts +12 -13
- package/dist/node.js +19 -24
- package/dist/reporters.d.ts +7 -8
- package/dist/reporters.js +3 -3
- package/dist/runners.d.ts +3 -3
- package/dist/runners.js +35 -57
- package/dist/snapshot.js +2 -2
- package/dist/suite.js +2 -2
- package/dist/worker.js +82 -45
- package/dist/workers/forks.js +11 -10
- package/dist/workers/runVmTests.js +27 -46
- package/dist/workers/threads.js +11 -10
- package/dist/workers/vmForks.js +11 -10
- package/dist/workers/vmThreads.js +11 -10
- package/dist/workers.d.ts +5 -4
- package/dist/workers.js +17 -16
- package/package.json +22 -17
- package/dist/chunks/base.BaCDDRPG.js +0 -38
- package/dist/chunks/execute.Dt-pCVcL.js +0 -708
- package/dist/chunks/runBaseTests.DBVVLMSb.js +0 -129
- package/dist/execute.d.ts +0 -148
- package/dist/execute.js +0 -13
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { builtinModules, isBuiltin } from 'node:module';
|
|
3
|
+
import { highlight, isBareImport } from '@vitest/utils';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { normalize as normalize$1, join as join$1 } from 'pathe';
|
|
6
|
+
import { distDir } from '../path.js';
|
|
7
|
+
import { processError } from '@vitest/utils/error';
|
|
8
|
+
import { VitestModuleEvaluator, unwrapId } from '../module-evaluator.js';
|
|
9
|
+
import { resolve as resolve$1, isAbsolute as isAbsolute$1 } from 'node:path';
|
|
10
|
+
import vm from 'node:vm';
|
|
11
|
+
import { MockerRegistry, mockObject, RedirectedModule, AutomockedModule } from '@vitest/mocker';
|
|
12
|
+
import { ModuleRunner } from 'vite/module-runner';
|
|
13
|
+
import { V as VitestTransport } from './moduleTransport.I-bgQy0S.js';
|
|
14
|
+
|
|
15
|
+
const bareVitestRegexp = /^@?vitest(?:\/|$)/, normalizedDistDir = normalize$1(distDir), relativeIds = {}, externalizeMap = /* @__PURE__ */ new Map();
|
|
16
|
+
// all Vitest imports always need to be externalized
|
|
17
|
+
function getCachedVitestImport(id, state) {
|
|
18
|
+
if (id.startsWith("/@fs/") || id.startsWith("\\@fs\\")) id = id.slice(process.platform === "win32" ? 5 : 4);
|
|
19
|
+
if (externalizeMap.has(id)) return {
|
|
20
|
+
externalize: externalizeMap.get(id),
|
|
21
|
+
type: "module"
|
|
22
|
+
};
|
|
23
|
+
// always externalize Vitest because we import from there before running tests
|
|
24
|
+
// so we already have it cached by Node.js
|
|
25
|
+
const root = state().config.root, relativeRoot = relativeIds[root] ?? (relativeIds[root] = normalizedDistDir.slice(root.length));
|
|
26
|
+
if (id.includes(distDir) || id.includes(normalizedDistDir)) {
|
|
27
|
+
const externalize = id.startsWith("file://") ? id : pathToFileURL(id).toString();
|
|
28
|
+
return externalizeMap.set(id, externalize), {
|
|
29
|
+
externalize,
|
|
30
|
+
type: "module"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (relativeRoot && relativeRoot !== "/" && id.startsWith(relativeRoot)) {
|
|
34
|
+
const path = join$1(root, id), externalize = pathToFileURL(path).toString();
|
|
35
|
+
return externalizeMap.set(id, externalize), {
|
|
36
|
+
externalize,
|
|
37
|
+
type: "module"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return bareVitestRegexp.test(id) ? (externalizeMap.set(id, id), {
|
|
41
|
+
externalize: id,
|
|
42
|
+
type: "module"
|
|
43
|
+
}) : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const dispose = [];
|
|
47
|
+
function listenForErrors(state) {
|
|
48
|
+
dispose.forEach((fn) => fn()), dispose.length = 0;
|
|
49
|
+
function catchError(err, type, event) {
|
|
50
|
+
const worker = state(), listeners = process.listeners(event);
|
|
51
|
+
// if there is another listener, assume that it's handled by user code
|
|
52
|
+
// one is Vitest's own listener
|
|
53
|
+
if (listeners.length > 1) return;
|
|
54
|
+
const error = processError(err);
|
|
55
|
+
if (typeof error === "object" && error != null) {
|
|
56
|
+
if (error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : void 0, worker.filepath) error.VITEST_TEST_PATH = worker.filepath;
|
|
57
|
+
error.VITEST_AFTER_ENV_TEARDOWN = worker.environmentTeardownRun;
|
|
58
|
+
}
|
|
59
|
+
state().rpc.onUnhandledError(error, type);
|
|
60
|
+
}
|
|
61
|
+
const uncaughtException = (e) => catchError(e, "Uncaught Exception", "uncaughtException"), unhandledRejection = (e) => catchError(e, "Unhandled Rejection", "unhandledRejection");
|
|
62
|
+
process.on("uncaughtException", uncaughtException), process.on("unhandledRejection", unhandledRejection), dispose.push(() => {
|
|
63
|
+
process.off("uncaughtException", uncaughtException), process.off("unhandledRejection", unhandledRejection);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
68
|
+
function normalizeWindowsPath(input = "") {
|
|
69
|
+
return input && input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
70
|
+
}
|
|
71
|
+
const _UNC_REGEX = /^[/\\]{2}/, _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/, _DRIVE_LETTER_RE = /^[A-Za-z]:$/, _EXTNAME_RE = /.(\.[^./]+|\.)$/, normalize = function(path) {
|
|
72
|
+
if (path.length === 0) return ".";
|
|
73
|
+
path = normalizeWindowsPath(path);
|
|
74
|
+
const isUNCPath = path.match(_UNC_REGEX), isPathAbsolute = isAbsolute(path), trailingSeparator = path[path.length - 1] === "/";
|
|
75
|
+
if (path = normalizeString(path, !isPathAbsolute), path.length === 0) return isPathAbsolute ? "/" : trailingSeparator ? "./" : ".";
|
|
76
|
+
if (trailingSeparator) path += "/";
|
|
77
|
+
if (_DRIVE_LETTER_RE.test(path)) path += "/";
|
|
78
|
+
return isUNCPath ? isPathAbsolute ? `//${path}` : `//./${path}` : isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
|
|
79
|
+
}, join = function(...segments) {
|
|
80
|
+
let path = "";
|
|
81
|
+
for (const seg of segments) {
|
|
82
|
+
if (!seg) continue;
|
|
83
|
+
if (path.length > 0) {
|
|
84
|
+
const pathTrailing = path[path.length - 1] === "/", segLeading = seg[0] === "/", both = pathTrailing && segLeading;
|
|
85
|
+
if (both) path += seg.slice(1);
|
|
86
|
+
else path += pathTrailing || segLeading ? seg : `/${seg}`;
|
|
87
|
+
} else path += seg;
|
|
88
|
+
}
|
|
89
|
+
return normalize(path);
|
|
90
|
+
};
|
|
91
|
+
function cwd$1() {
|
|
92
|
+
return typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd().replace(/\\/g, "/") : "/";
|
|
93
|
+
}
|
|
94
|
+
const resolve = function(...arguments_) {
|
|
95
|
+
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
96
|
+
let resolvedPath = "", resolvedAbsolute = false;
|
|
97
|
+
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
98
|
+
const path = index >= 0 ? arguments_[index] : cwd$1();
|
|
99
|
+
!path || path.length === 0 || (resolvedPath = `${path}/${resolvedPath}`, resolvedAbsolute = isAbsolute(path));
|
|
100
|
+
}
|
|
101
|
+
return resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute), resolvedAbsolute && !isAbsolute(resolvedPath) ? `/${resolvedPath}` : resolvedPath.length > 0 ? resolvedPath : ".";
|
|
102
|
+
};
|
|
103
|
+
function normalizeString(path, allowAboveRoot) {
|
|
104
|
+
let res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, char = null;
|
|
105
|
+
for (let index = 0; index <= path.length; ++index) {
|
|
106
|
+
if (index < path.length) char = path[index];
|
|
107
|
+
else if (char === "/") break;
|
|
108
|
+
else char = "/";
|
|
109
|
+
if (char === "/") {
|
|
110
|
+
if (lastSlash === index - 1 || dots === 1);
|
|
111
|
+
else if (dots === 2) {
|
|
112
|
+
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
|
113
|
+
if (res.length > 2) {
|
|
114
|
+
const lastSlashIndex = res.lastIndexOf("/");
|
|
115
|
+
if (lastSlashIndex === -1) res = "", lastSegmentLength = 0;
|
|
116
|
+
else res = res.slice(0, lastSlashIndex), lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
117
|
+
lastSlash = index, dots = 0;
|
|
118
|
+
continue;
|
|
119
|
+
} else if (res.length > 0) {
|
|
120
|
+
res = "", lastSegmentLength = 0, lastSlash = index, dots = 0;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (allowAboveRoot) res += res.length > 0 ? "/.." : "..", lastSegmentLength = 2;
|
|
125
|
+
} else {
|
|
126
|
+
if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
|
|
127
|
+
else res = path.slice(lastSlash + 1, index);
|
|
128
|
+
lastSegmentLength = index - lastSlash - 1;
|
|
129
|
+
}
|
|
130
|
+
lastSlash = index, dots = 0;
|
|
131
|
+
} else if (char === "." && dots !== -1) ++dots;
|
|
132
|
+
else dots = -1;
|
|
133
|
+
}
|
|
134
|
+
return res;
|
|
135
|
+
}
|
|
136
|
+
const isAbsolute = function(p) {
|
|
137
|
+
return _IS_ABSOLUTE_RE.test(p);
|
|
138
|
+
}, extname = function(p) {
|
|
139
|
+
if (p === "..") return "";
|
|
140
|
+
const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
|
|
141
|
+
return match && match[1] || "";
|
|
142
|
+
}, dirname = function(p) {
|
|
143
|
+
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
144
|
+
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) segments[0] += "/";
|
|
145
|
+
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
146
|
+
}, basename = function(p, extension) {
|
|
147
|
+
const segments = normalizeWindowsPath(p).split("/");
|
|
148
|
+
let lastSegment = "";
|
|
149
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
150
|
+
const val = segments[i];
|
|
151
|
+
if (val) {
|
|
152
|
+
lastSegment = val;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const { existsSync, readdirSync, statSync } = fs;
|
|
160
|
+
function findMockRedirect(root, mockPath, external) {
|
|
161
|
+
const path = external || mockPath;
|
|
162
|
+
// it's a node_module alias
|
|
163
|
+
// all mocks should be inside <root>/__mocks__
|
|
164
|
+
if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) {
|
|
165
|
+
const mockDirname = dirname(path), mockFolder = join(root, "__mocks__", mockDirname);
|
|
166
|
+
if (!existsSync(mockFolder)) return null;
|
|
167
|
+
const baseOriginal = basename(path);
|
|
168
|
+
function findFile(mockFolder, baseOriginal) {
|
|
169
|
+
const files = readdirSync(mockFolder);
|
|
170
|
+
for (const file of files) {
|
|
171
|
+
const baseFile = basename(file, extname(file));
|
|
172
|
+
if (baseFile === baseOriginal) {
|
|
173
|
+
const path = resolve(mockFolder, file);
|
|
174
|
+
// if the same name, return the file
|
|
175
|
+
if (statSync(path).isFile()) return path;
|
|
176
|
+
{
|
|
177
|
+
// find folder/index.{js,ts}
|
|
178
|
+
const indexFile = findFile(path, "index");
|
|
179
|
+
if (indexFile) return indexFile;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return findFile(mockFolder, baseOriginal);
|
|
186
|
+
}
|
|
187
|
+
const dir = dirname(path), baseId = basename(path), fullPath = resolve(dir, "__mocks__", baseId);
|
|
188
|
+
return existsSync(fullPath) ? fullPath : null;
|
|
189
|
+
}
|
|
190
|
+
const builtins = new Set([
|
|
191
|
+
...builtinModules,
|
|
192
|
+
"assert/strict",
|
|
193
|
+
"diagnostics_channel",
|
|
194
|
+
"dns/promises",
|
|
195
|
+
"fs/promises",
|
|
196
|
+
"path/posix",
|
|
197
|
+
"path/win32",
|
|
198
|
+
"readline/promises",
|
|
199
|
+
"stream/consumers",
|
|
200
|
+
"stream/promises",
|
|
201
|
+
"stream/web",
|
|
202
|
+
"timers/promises",
|
|
203
|
+
"util/types",
|
|
204
|
+
"wasi"
|
|
205
|
+
]), prefixedBuiltins$1 = new Set([
|
|
206
|
+
"node:sea",
|
|
207
|
+
"node:sqlite",
|
|
208
|
+
"node:test",
|
|
209
|
+
"node:test/reporters"
|
|
210
|
+
]), NODE_BUILTIN_NAMESPACE = "node:";
|
|
211
|
+
function isNodeBuiltin(id) {
|
|
212
|
+
return prefixedBuiltins$1.has(id) ? true : builtins.has(id.startsWith(NODE_BUILTIN_NAMESPACE) ? id.slice(5) : id);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const spyModulePath = resolve$1(distDir, "spy.js");
|
|
216
|
+
class VitestMocker {
|
|
217
|
+
static pendingIds = [];
|
|
218
|
+
spyModule;
|
|
219
|
+
primitives;
|
|
220
|
+
filterPublicKeys;
|
|
221
|
+
registries = /* @__PURE__ */ new Map();
|
|
222
|
+
mockContext = { callstack: null };
|
|
223
|
+
constructor(moduleRunner, options) {
|
|
224
|
+
this.moduleRunner = moduleRunner, this.options = options;
|
|
225
|
+
const context = this.options.context;
|
|
226
|
+
if (context) this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
|
|
227
|
+
else this.primitives = {
|
|
228
|
+
Object,
|
|
229
|
+
Error,
|
|
230
|
+
Function,
|
|
231
|
+
RegExp,
|
|
232
|
+
Symbol: globalThis.Symbol,
|
|
233
|
+
Array,
|
|
234
|
+
Map
|
|
235
|
+
};
|
|
236
|
+
const Symbol = this.primitives.Symbol;
|
|
237
|
+
this.filterPublicKeys = [
|
|
238
|
+
"__esModule",
|
|
239
|
+
Symbol.asyncIterator,
|
|
240
|
+
Symbol.hasInstance,
|
|
241
|
+
Symbol.isConcatSpreadable,
|
|
242
|
+
Symbol.iterator,
|
|
243
|
+
Symbol.match,
|
|
244
|
+
Symbol.matchAll,
|
|
245
|
+
Symbol.replace,
|
|
246
|
+
Symbol.search,
|
|
247
|
+
Symbol.split,
|
|
248
|
+
Symbol.species,
|
|
249
|
+
Symbol.toPrimitive,
|
|
250
|
+
Symbol.toStringTag,
|
|
251
|
+
Symbol.unscopables
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
get root() {
|
|
255
|
+
return this.options.root;
|
|
256
|
+
}
|
|
257
|
+
get evaluatedModules() {
|
|
258
|
+
return this.moduleRunner.evaluatedModules;
|
|
259
|
+
}
|
|
260
|
+
get moduleDirectories() {
|
|
261
|
+
return this.options.moduleDirectories || [];
|
|
262
|
+
}
|
|
263
|
+
async initializeSpyModule() {
|
|
264
|
+
this.spyModule ||= await this.moduleRunner.import(spyModulePath);
|
|
265
|
+
}
|
|
266
|
+
getMockerRegistry() {
|
|
267
|
+
const suite = this.getSuiteFilepath();
|
|
268
|
+
if (!this.registries.has(suite)) this.registries.set(suite, new MockerRegistry());
|
|
269
|
+
return this.registries.get(suite);
|
|
270
|
+
}
|
|
271
|
+
reset() {
|
|
272
|
+
this.registries.clear();
|
|
273
|
+
}
|
|
274
|
+
invalidateModuleById(id) {
|
|
275
|
+
const mockId = this.getMockPath(id), node = this.evaluatedModules.getModuleById(mockId);
|
|
276
|
+
if (node) this.evaluatedModules.invalidateModule(node), node.mockedExports = void 0;
|
|
277
|
+
}
|
|
278
|
+
isModuleDirectory(path) {
|
|
279
|
+
return this.moduleDirectories.some((dir) => path.includes(dir));
|
|
280
|
+
}
|
|
281
|
+
getSuiteFilepath() {
|
|
282
|
+
return this.options.getCurrentTestFilepath() || "global";
|
|
283
|
+
}
|
|
284
|
+
createError(message, codeFrame) {
|
|
285
|
+
const Error = this.primitives.Error, error = new Error(message);
|
|
286
|
+
return Object.assign(error, { codeFrame }), error;
|
|
287
|
+
}
|
|
288
|
+
async resolveId(rawId, importer) {
|
|
289
|
+
const result = await this.options.resolveId(rawId, importer);
|
|
290
|
+
if (!result) {
|
|
291
|
+
const id = normalizeModuleId(rawId);
|
|
292
|
+
return {
|
|
293
|
+
id,
|
|
294
|
+
url: rawId,
|
|
295
|
+
external: id
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
// external is node_module or unresolved module
|
|
299
|
+
// for example, some people mock "vscode" and don't have it installed
|
|
300
|
+
const external = !isAbsolute$1(result.file) || this.isModuleDirectory(result.file) ? normalizeModuleId(rawId) : null;
|
|
301
|
+
return {
|
|
302
|
+
...result,
|
|
303
|
+
id: normalizeModuleId(result.id),
|
|
304
|
+
external
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
async resolveMocks() {
|
|
308
|
+
VitestMocker.pendingIds.length && (await Promise.all(VitestMocker.pendingIds.map(async (mock) => {
|
|
309
|
+
const { id, url, external } = await this.resolveId(mock.id, mock.importer);
|
|
310
|
+
if (mock.action === "unmock") this.unmockPath(id);
|
|
311
|
+
if (mock.action === "mock") this.mockPath(mock.id, id, url, external, mock.type, mock.factory);
|
|
312
|
+
})), VitestMocker.pendingIds = []);
|
|
313
|
+
}
|
|
314
|
+
ensureModule(id, url) {
|
|
315
|
+
const node = this.evaluatedModules.ensureModule(id, url);
|
|
316
|
+
return node.meta = {
|
|
317
|
+
id,
|
|
318
|
+
url,
|
|
319
|
+
code: "",
|
|
320
|
+
file: null,
|
|
321
|
+
invalidate: false
|
|
322
|
+
}, node;
|
|
323
|
+
}
|
|
324
|
+
async callFunctionMock(id, url, mock) {
|
|
325
|
+
const node = this.ensureModule(id, url);
|
|
326
|
+
if (node.exports) return node.exports;
|
|
327
|
+
const exports = await mock.resolve(), moduleExports = new Proxy(exports, { get: (target, prop) => {
|
|
328
|
+
const val = target[prop];
|
|
329
|
+
// 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
|
|
330
|
+
if (prop === "then") {
|
|
331
|
+
if (target instanceof Promise) return target.then.bind(target);
|
|
332
|
+
} else if (!(prop in target)) {
|
|
333
|
+
if (this.filterPublicKeys.includes(prop)) return void 0;
|
|
334
|
+
throw this.createError(`[vitest] No "${String(prop)}" export is defined on the "${mock.raw}" mock. Did you forget to return it from "vi.mock"?
|
|
335
|
+
If you need to partially mock a module, you can use "importOriginal" helper inside:
|
|
336
|
+
`, highlight(`vi.mock(import("${mock.raw}"), async (importOriginal) => {
|
|
337
|
+
const actual = await importOriginal()
|
|
338
|
+
return {
|
|
339
|
+
...actual,
|
|
340
|
+
// your mocked methods
|
|
341
|
+
}
|
|
342
|
+
})`));
|
|
343
|
+
}
|
|
344
|
+
return val;
|
|
345
|
+
} });
|
|
346
|
+
return node.exports = moduleExports, moduleExports;
|
|
347
|
+
}
|
|
348
|
+
// public method to avoid circular dependency
|
|
349
|
+
getMockContext() {
|
|
350
|
+
return this.mockContext;
|
|
351
|
+
}
|
|
352
|
+
// path used to store mocked dependencies
|
|
353
|
+
getMockPath(dep) {
|
|
354
|
+
return `mock:${dep}`;
|
|
355
|
+
}
|
|
356
|
+
getDependencyMock(id) {
|
|
357
|
+
const registry = this.getMockerRegistry();
|
|
358
|
+
return registry.getById(fixLeadingSlashes(id));
|
|
359
|
+
}
|
|
360
|
+
findMockRedirect(mockPath, external) {
|
|
361
|
+
return findMockRedirect(this.root, mockPath, external);
|
|
362
|
+
}
|
|
363
|
+
mockObject(object, mockExports = {}, behavior = "automock") {
|
|
364
|
+
const createMockInstance = this.spyModule?.createMockInstance;
|
|
365
|
+
if (!createMockInstance) throw this.createError("[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
|
|
366
|
+
return mockObject({
|
|
367
|
+
globalConstructors: this.primitives,
|
|
368
|
+
createMockInstance,
|
|
369
|
+
type: behavior
|
|
370
|
+
}, object, mockExports);
|
|
371
|
+
}
|
|
372
|
+
unmockPath(id) {
|
|
373
|
+
const registry = this.getMockerRegistry();
|
|
374
|
+
registry.deleteById(id), this.invalidateModuleById(id);
|
|
375
|
+
}
|
|
376
|
+
mockPath(originalId, id, url, external, mockType, factory) {
|
|
377
|
+
const registry = this.getMockerRegistry();
|
|
378
|
+
if (mockType === "manual") registry.register("manual", originalId, id, url, factory);
|
|
379
|
+
else if (mockType === "autospy") registry.register("autospy", originalId, id, url);
|
|
380
|
+
else {
|
|
381
|
+
const redirect = this.findMockRedirect(id, external);
|
|
382
|
+
if (redirect) registry.register("redirect", originalId, id, url, redirect);
|
|
383
|
+
else registry.register("automock", originalId, id, url);
|
|
384
|
+
}
|
|
385
|
+
// every time the mock is registered, we remove the previous one from the cache
|
|
386
|
+
this.invalidateModuleById(id);
|
|
387
|
+
}
|
|
388
|
+
async importActual(rawId, importer, callstack) {
|
|
389
|
+
const { url } = await this.resolveId(rawId, importer), node = await this.moduleRunner.fetchModule(url, importer), result = await this.moduleRunner.cachedRequest(node.url, node, callstack || [importer], void 0, true);
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
async importMock(rawId, importer) {
|
|
393
|
+
const { id, url, external } = await this.resolveId(rawId, importer);
|
|
394
|
+
let mock = this.getDependencyMock(id);
|
|
395
|
+
if (!mock) {
|
|
396
|
+
const redirect = this.findMockRedirect(id, external);
|
|
397
|
+
if (redirect) mock = new RedirectedModule(rawId, id, rawId, redirect);
|
|
398
|
+
else mock = new AutomockedModule(rawId, id, rawId);
|
|
399
|
+
}
|
|
400
|
+
if (mock.type === "automock" || mock.type === "autospy") {
|
|
401
|
+
const node = await this.moduleRunner.fetchModule(url, importer), mod = await this.moduleRunner.cachedRequest(url, node, [importer], void 0, true), Object = this.primitives.Object;
|
|
402
|
+
return this.mockObject(mod, Object.create(Object.prototype), mock.type);
|
|
403
|
+
}
|
|
404
|
+
if (mock.type === "manual") return this.callFunctionMock(id, url, mock);
|
|
405
|
+
const node = await this.moduleRunner.fetchModule(mock.redirect);
|
|
406
|
+
return this.moduleRunner.cachedRequest(mock.redirect, node, [importer], void 0, true);
|
|
407
|
+
}
|
|
408
|
+
async requestWithMockedModule(url, evaluatedNode, callstack, mock) {
|
|
409
|
+
const mockId = this.getMockPath(evaluatedNode.id);
|
|
410
|
+
if (mock.type === "automock" || mock.type === "autospy") {
|
|
411
|
+
const cache = this.evaluatedModules.getModuleById(mockId);
|
|
412
|
+
if (cache && cache.mockedExports) return cache.mockedExports;
|
|
413
|
+
const Object = this.primitives.Object, exports = Object.create(null);
|
|
414
|
+
Object.defineProperty(exports, Symbol.toStringTag, {
|
|
415
|
+
value: "Module",
|
|
416
|
+
configurable: true,
|
|
417
|
+
writable: true
|
|
418
|
+
});
|
|
419
|
+
const node = this.ensureModule(mockId, this.getMockPath(evaluatedNode.url));
|
|
420
|
+
node.meta = evaluatedNode.meta, node.file = evaluatedNode.file, node.mockedExports = exports;
|
|
421
|
+
const mod = await this.moduleRunner.cachedRequest(url, node, callstack, void 0, true);
|
|
422
|
+
return this.mockObject(mod, exports, mock.type), exports;
|
|
423
|
+
}
|
|
424
|
+
if (mock.type === "manual" && !callstack.includes(mockId) && !callstack.includes(url)) try {
|
|
425
|
+
return callstack.push(mockId), this.mockContext.callstack = callstack, await this.callFunctionMock(mockId, this.getMockPath(url), mock);
|
|
426
|
+
} finally {
|
|
427
|
+
this.mockContext.callstack = null;
|
|
428
|
+
const indexMock = callstack.indexOf(mockId);
|
|
429
|
+
callstack.splice(indexMock, 1);
|
|
430
|
+
}
|
|
431
|
+
else if (mock.type === "redirect" && !callstack.includes(mock.redirect)) return mock.redirect;
|
|
432
|
+
}
|
|
433
|
+
async mockedRequest(url, evaluatedNode, callstack) {
|
|
434
|
+
const mock = this.getDependencyMock(evaluatedNode.id);
|
|
435
|
+
if (mock) return this.requestWithMockedModule(url, evaluatedNode, callstack, mock);
|
|
436
|
+
}
|
|
437
|
+
queueMock(id, importer, factoryOrOptions) {
|
|
438
|
+
const mockType = getMockType(factoryOrOptions);
|
|
439
|
+
VitestMocker.pendingIds.push({
|
|
440
|
+
action: "mock",
|
|
441
|
+
id,
|
|
442
|
+
importer,
|
|
443
|
+
factory: typeof factoryOrOptions === "function" ? factoryOrOptions : void 0,
|
|
444
|
+
type: mockType
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
queueUnmock(id, importer) {
|
|
448
|
+
VitestMocker.pendingIds.push({
|
|
449
|
+
action: "unmock",
|
|
450
|
+
id,
|
|
451
|
+
importer
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function getMockType(factoryOrOptions) {
|
|
456
|
+
return factoryOrOptions ? typeof factoryOrOptions === "function" ? "manual" : factoryOrOptions.spy ? "autospy" : "automock" : "automock";
|
|
457
|
+
}
|
|
458
|
+
// unique id that is not available as "$bare_import" like "test"
|
|
459
|
+
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
|
|
460
|
+
const prefixedBuiltins = new Set([
|
|
461
|
+
"node:sea",
|
|
462
|
+
"node:sqlite",
|
|
463
|
+
"node:test",
|
|
464
|
+
"node:test/reporters"
|
|
465
|
+
]), isWindows$1 = process.platform === "win32";
|
|
466
|
+
// transform file url to id
|
|
467
|
+
// virtual:custom -> virtual:custom
|
|
468
|
+
// \0custom -> \0custom
|
|
469
|
+
// /root/id -> /id
|
|
470
|
+
// /root/id.js -> /id.js
|
|
471
|
+
// C:/root/id.js -> /id.js
|
|
472
|
+
// C:\root\id.js -> /id.js
|
|
473
|
+
// TODO: expose this in vite/module-runner
|
|
474
|
+
function normalizeModuleId(file) {
|
|
475
|
+
if (prefixedBuiltins.has(file)) return file;
|
|
476
|
+
// unix style, but Windows path still starts with the drive letter to check the root
|
|
477
|
+
const unixFile = slash(file).replace(/^\/@fs\//, isWindows$1 ? "" : "/").replace(/^node:/, "").replace(/^\/+/, "/");
|
|
478
|
+
// if it's not in the root, keep it as a path, not a URL
|
|
479
|
+
return unixFile.replace(/^file:\//, "/");
|
|
480
|
+
}
|
|
481
|
+
const windowsSlashRE = /\\/g;
|
|
482
|
+
function slash(p) {
|
|
483
|
+
return p.replace(windowsSlashRE, "/");
|
|
484
|
+
}
|
|
485
|
+
const multipleSlashRe = /^\/+/;
|
|
486
|
+
// module-runner incorrectly replaces file:///path with `///path`
|
|
487
|
+
function fixLeadingSlashes(id) {
|
|
488
|
+
return id.startsWith("//") ? id.replace(multipleSlashRe, "/") : id;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// @ts-expect-error overriding private method
|
|
492
|
+
class VitestModuleRunner extends ModuleRunner {
|
|
493
|
+
mocker;
|
|
494
|
+
moduleExecutionInfo;
|
|
495
|
+
constructor(options) {
|
|
496
|
+
const transport = new VitestTransport(options.transport), evaluatedModules = options.evaluatedModules;
|
|
497
|
+
if (super({
|
|
498
|
+
transport,
|
|
499
|
+
hmr: false,
|
|
500
|
+
evaluatedModules,
|
|
501
|
+
sourcemapInterceptor: "prepareStackTrace"
|
|
502
|
+
}, options.evaluator), this.options = options, this.moduleExecutionInfo = options.getWorkerState().moduleExecutionInfo, this.mocker = options.mocker || new VitestMocker(this, {
|
|
503
|
+
context: options.vm?.context,
|
|
504
|
+
resolveId: options.transport.resolveId,
|
|
505
|
+
get root() {
|
|
506
|
+
return options.getWorkerState().config.root;
|
|
507
|
+
},
|
|
508
|
+
get moduleDirectories() {
|
|
509
|
+
return options.getWorkerState().config.deps.moduleDirectories || [];
|
|
510
|
+
},
|
|
511
|
+
getCurrentTestFilepath() {
|
|
512
|
+
return options.getWorkerState().filepath;
|
|
513
|
+
}
|
|
514
|
+
}), options.vm) options.vm.context.__vitest_mocker__ = this.mocker;
|
|
515
|
+
else Object.defineProperty(globalThis, "__vitest_mocker__", {
|
|
516
|
+
configurable: true,
|
|
517
|
+
writable: true,
|
|
518
|
+
value: this.mocker
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
async import(rawId) {
|
|
522
|
+
const resolved = await this.options.transport.resolveId(rawId);
|
|
523
|
+
return resolved ? super.import(resolved.url) : super.import(rawId);
|
|
524
|
+
}
|
|
525
|
+
async fetchModule(url, importer) {
|
|
526
|
+
const module = await this.cachedModule(url, importer);
|
|
527
|
+
return module;
|
|
528
|
+
}
|
|
529
|
+
_cachedRequest(url, module, callstack = [], metadata) {
|
|
530
|
+
// @ts-expect-error "cachedRequest" is private
|
|
531
|
+
return super.cachedRequest(url, module, callstack, metadata);
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* @internal
|
|
535
|
+
*/
|
|
536
|
+
async cachedRequest(url, mod, callstack = [], metadata, ignoreMock = false) {
|
|
537
|
+
if (ignoreMock) return this._cachedRequest(url, mod, callstack, metadata);
|
|
538
|
+
let mocked;
|
|
539
|
+
if (mod.meta && "mockedModule" in mod.meta) mocked = await this.mocker.requestWithMockedModule(url, mod, callstack, mod.meta.mockedModule);
|
|
540
|
+
else mocked = await this.mocker.mockedRequest(url, mod, callstack);
|
|
541
|
+
if (typeof mocked === "string") {
|
|
542
|
+
const node = await this.fetchModule(mocked);
|
|
543
|
+
return this._cachedRequest(mocked, node, callstack, metadata);
|
|
544
|
+
}
|
|
545
|
+
return mocked != null && typeof mocked === "object" ? mocked : this._cachedRequest(url, mod, callstack, metadata);
|
|
546
|
+
}
|
|
547
|
+
/** @internal */
|
|
548
|
+
_invalidateSubTreeById(ids, invalidated = /* @__PURE__ */ new Set()) {
|
|
549
|
+
for (const id of ids) {
|
|
550
|
+
if (invalidated.has(id)) continue;
|
|
551
|
+
const node = this.evaluatedModules.getModuleById(id);
|
|
552
|
+
if (!node) continue;
|
|
553
|
+
invalidated.add(id);
|
|
554
|
+
const subIds = Array.from(this.evaluatedModules.idToModuleMap).filter(([, mod]) => mod.importers.has(id)).map(([key]) => key);
|
|
555
|
+
if (subIds.length) this._invalidateSubTreeById(subIds, invalidated);
|
|
556
|
+
this.evaluatedModules.invalidateModule(node);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const { readFileSync } = fs, browserExternalId = "__vite-browser-external", browserExternalLength = 24;
|
|
562
|
+
const VITEST_VM_CONTEXT_SYMBOL = "__vitest_vm_context__";
|
|
563
|
+
const cwd = process.cwd(), isWindows = process.platform === "win32";
|
|
564
|
+
async function startVitestModuleRunner(options) {
|
|
565
|
+
const state = () => globalThis.__vitest_worker__ || options.state, rpc = () => state().rpc;
|
|
566
|
+
process.exit = (code = process.exitCode || 0) => {
|
|
567
|
+
throw new Error(`process.exit unexpectedly called with "${code}"`);
|
|
568
|
+
}, listenForErrors(state);
|
|
569
|
+
const environment = () => {
|
|
570
|
+
const environment = state().environment;
|
|
571
|
+
return environment.viteEnvironment || environment.name;
|
|
572
|
+
}, vm = options.context && options.externalModulesExecutor ? {
|
|
573
|
+
context: options.context,
|
|
574
|
+
externalModulesExecutor: options.externalModulesExecutor
|
|
575
|
+
} : void 0, evaluator = options.evaluator || new VitestModuleEvaluator(vm, {
|
|
576
|
+
get moduleExecutionInfo() {
|
|
577
|
+
return state().moduleExecutionInfo;
|
|
578
|
+
},
|
|
579
|
+
get interopDefault() {
|
|
580
|
+
return state().config.deps.interopDefault;
|
|
581
|
+
},
|
|
582
|
+
getCurrentTestFilepath: () => state().filepath
|
|
583
|
+
}), moduleRunner = new VitestModuleRunner({
|
|
584
|
+
evaluatedModules: options.evaluatedModules,
|
|
585
|
+
evaluator,
|
|
586
|
+
mocker: options.mocker,
|
|
587
|
+
transport: {
|
|
588
|
+
async fetchModule(id, importer, options) {
|
|
589
|
+
const resolvingModules = state().resolvingModules;
|
|
590
|
+
if (isWindows) {
|
|
591
|
+
if (id[1] === ":") {
|
|
592
|
+
// The drive letter is different for whatever reason, we need to normalize it to CWD
|
|
593
|
+
if (id[0] !== cwd[0] && id[0].toUpperCase() === cwd[0].toUpperCase()) {
|
|
594
|
+
const isUpperCase = cwd[0].toUpperCase() === cwd[0];
|
|
595
|
+
id = (isUpperCase ? id[0].toUpperCase() : id[0].toLowerCase()) + id.slice(1);
|
|
596
|
+
}
|
|
597
|
+
// always mark absolute windows paths, otherwise Vite will externalize it
|
|
598
|
+
id = `/@id/${id}`;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
const vitest = getCachedVitestImport(id, state);
|
|
602
|
+
if (vitest) return vitest;
|
|
603
|
+
const rawId = unwrapId(id);
|
|
604
|
+
resolvingModules.add(rawId);
|
|
605
|
+
try {
|
|
606
|
+
if (VitestMocker.pendingIds.length) await moduleRunner.mocker.resolveMocks();
|
|
607
|
+
const resolvedMock = moduleRunner.mocker.getDependencyMock(rawId);
|
|
608
|
+
if (resolvedMock?.type === "manual" || resolvedMock?.type === "redirect") return {
|
|
609
|
+
code: "",
|
|
610
|
+
file: null,
|
|
611
|
+
id,
|
|
612
|
+
url: id,
|
|
613
|
+
invalidate: false,
|
|
614
|
+
mockedModule: resolvedMock
|
|
615
|
+
};
|
|
616
|
+
if (isBuiltin(rawId) || rawId.startsWith(browserExternalId)) return {
|
|
617
|
+
externalize: toBuiltin(rawId),
|
|
618
|
+
type: "builtin"
|
|
619
|
+
};
|
|
620
|
+
const result = await rpc().fetch(id, importer, environment(), options);
|
|
621
|
+
if ("cached" in result) {
|
|
622
|
+
const code = readFileSync(result.tmp, "utf-8");
|
|
623
|
+
return {
|
|
624
|
+
code,
|
|
625
|
+
...result
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
return result;
|
|
629
|
+
} catch (cause) {
|
|
630
|
+
// rethrow vite error if it cannot load the module because it's not resolved
|
|
631
|
+
if (typeof cause === "object" && cause != null && cause.code === "ERR_LOAD_URL" || typeof cause?.message === "string" && cause.message.includes("Failed to load url") || typeof cause?.message === "string" && cause.message.startsWith("Cannot find module '")) {
|
|
632
|
+
const error = new Error(`Cannot find ${isBareImport(id) ? "package" : "module"} '${id}'${importer ? ` imported from '${importer}'` : ""}`, { cause });
|
|
633
|
+
throw error.code = "ERR_MODULE_NOT_FOUND", error;
|
|
634
|
+
}
|
|
635
|
+
throw cause;
|
|
636
|
+
} finally {
|
|
637
|
+
resolvingModules.delete(rawId);
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
resolveId(id, importer) {
|
|
641
|
+
return rpc().resolve(id, importer, environment());
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
getWorkerState: state,
|
|
645
|
+
vm
|
|
646
|
+
});
|
|
647
|
+
return await moduleRunner.import("/@vite/env"), await moduleRunner.mocker.initializeSpyModule(), moduleRunner;
|
|
648
|
+
}
|
|
649
|
+
function toBuiltin(id) {
|
|
650
|
+
if (id.startsWith(browserExternalId)) id = id.slice(browserExternalLength);
|
|
651
|
+
if (!id.startsWith("node:")) id = `node:${id}`;
|
|
652
|
+
return id;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export { VitestModuleRunner as V, VITEST_VM_CONTEXT_SYMBOL as a, startVitestModuleRunner as s };
|