vitest 3.2.0-beta.2 → 3.2.0
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 +29 -0
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +2 -2
- package/dist/chunks/{base.DwtwORaC.js → base.Cg0miDlQ.js} +11 -14
- package/dist/chunks/{benchmark.BoF7jW0Q.js → benchmark.CYdenmiT.js} +4 -6
- package/dist/chunks/{cac.I9MLYfT-.js → cac.6rXCxFY1.js} +76 -143
- package/dist/chunks/{cli-api.d6IK1pnk.js → cli-api.Cej3MBjA.js} +1460 -1344
- package/dist/chunks/{config.d.UqE-KR0o.d.ts → config.d.D2ROskhv.d.ts} +2 -0
- package/dist/chunks/{console.K1NMVOSc.js → console.CtFJOzRO.js} +25 -45
- package/dist/chunks/{constants.BZZyIeIE.js → constants.DnKduX2e.js} +1 -0
- package/dist/chunks/{coverage.0iPg4Wrz.js → coverage.DVF1vEu8.js} +4 -12
- package/dist/chunks/{coverage.OGU09Jbh.js → coverage.EIiagJJP.js} +578 -993
- package/dist/chunks/{creator.DGAdZ4Hj.js → creator.GK6I-cL4.js} +39 -83
- package/dist/chunks/date.Bq6ZW5rf.js +73 -0
- package/dist/chunks/{defaults.DSxsTG0h.js → defaults.B7q_naMc.js} +2 -1
- package/dist/chunks/{env.Dq0hM4Xv.js → env.D4Lgay0q.js} +1 -1
- package/dist/chunks/{environment.d.D8YDy2v5.d.ts → environment.d.cL3nLXbE.d.ts} +1 -0
- package/dist/chunks/{execute.JlGHLJZT.js → execute.B7h3T_Hc.js} +126 -217
- package/dist/chunks/{git.DXfdBEfR.js → git.BVQ8w_Sw.js} +1 -3
- package/dist/chunks/{global.d.BPa1eL3O.d.ts → global.d.MAmajcmJ.d.ts} +5 -1
- package/dist/chunks/{globals.CpxW8ccg.js → globals.DEHgCU4V.js} +7 -6
- package/dist/chunks/{index.CV36oG_L.js → index.BZ0g1JD2.js} +430 -625
- package/dist/chunks/{index.DswW_LEs.js → index.BbB8_kAK.js} +25 -24
- package/dist/chunks/{index.CmC5OK9L.js → index.CIyJn3t1.js} +38 -82
- package/dist/chunks/{index.CfXMNXHg.js → index.CdQS2e2Q.js} +4 -2
- package/dist/chunks/{index.DFXFpH3w.js → index.CmSc2RE5.js} +85 -105
- package/dist/chunks/index.D3XRDfWc.js +213 -0
- package/dist/chunks/{inspector.DbDkSkFn.js → inspector.C914Efll.js} +4 -1
- package/dist/chunks/{node.3xsWotC9.js → node.fjCdwEIl.js} +1 -1
- package/dist/chunks/{reporters.d.CLC9rhKy.d.ts → reporters.d.C1ogPriE.d.ts} +47 -9
- package/dist/chunks/{rpc.D9_013TY.js → rpc.Iovn4oWe.js} +10 -19
- package/dist/chunks/{runBaseTests.Dn2vyej_.js → runBaseTests.Dd85QTll.js} +27 -31
- package/dist/chunks/{setup-common.CYo3Y0dD.js → setup-common.Dd054P77.js} +16 -42
- package/dist/chunks/{typechecker.DnTrplSJ.js → typechecker.DRKU1-1g.js} +163 -186
- package/dist/chunks/{utils.BfxieIyZ.js → utils.CAioKnHs.js} +9 -14
- package/dist/chunks/{utils.CgTj3MsC.js → utils.XdZDrNZV.js} +6 -13
- package/dist/chunks/{vi.BFR5YIgu.js → vi.bdSIJ99Y.js} +137 -263
- package/dist/chunks/{vite.d.CBZ3M_ru.d.ts → vite.d.DqE4-hhK.d.ts} +3 -1
- package/dist/chunks/{vm.C1HHjtNS.js → vm.BThCzidc.js} +164 -212
- package/dist/chunks/{worker.d.D5Xdi-Zr.d.ts → worker.d.DvqK5Vmu.d.ts} +1 -1
- package/dist/chunks/{worker.d.CoCI7hzP.d.ts → worker.d.tQu2eJQy.d.ts} +5 -3
- package/dist/cli.js +5 -5
- package/dist/config.cjs +3 -1
- package/dist/config.d.ts +7 -6
- package/dist/config.js +3 -3
- package/dist/coverage.d.ts +4 -4
- package/dist/coverage.js +7 -7
- package/dist/environments.d.ts +6 -2
- package/dist/environments.js +1 -1
- package/dist/execute.d.ts +9 -3
- package/dist/execute.js +1 -1
- package/dist/index.d.ts +28 -15
- package/dist/index.js +5 -5
- package/dist/node.d.ts +18 -10
- package/dist/node.js +17 -17
- package/dist/reporters.d.ts +4 -4
- package/dist/reporters.js +4 -4
- package/dist/runners.d.ts +6 -3
- package/dist/runners.js +59 -80
- package/dist/snapshot.js +2 -2
- package/dist/suite.js +2 -2
- package/dist/worker.js +39 -41
- package/dist/workers/forks.js +6 -4
- package/dist/workers/runVmTests.js +20 -21
- package/dist/workers/threads.js +4 -4
- package/dist/workers/vmForks.js +6 -6
- package/dist/workers/vmThreads.js +6 -6
- package/dist/workers.d.ts +4 -4
- package/dist/workers.js +10 -10
- package/package.json +21 -19
- package/dist/chunks/date.CDOsz-HY.js +0 -53
- package/dist/chunks/index.CK1YOQaa.js +0 -143
|
@@ -13,9 +13,7 @@ import { highlight } from '@vitest/utils';
|
|
|
13
13
|
|
|
14
14
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
15
15
|
function normalizeWindowsPath(input = "") {
|
|
16
|
-
if (!input)
|
|
17
|
-
return input;
|
|
18
|
-
}
|
|
16
|
+
if (!input) return input;
|
|
19
17
|
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
20
18
|
}
|
|
21
19
|
const _UNC_REGEX = /^[/\\]{2}/;
|
|
@@ -23,30 +21,20 @@ const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
|
23
21
|
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
|
|
24
22
|
const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
|
|
25
23
|
const normalize = function(path) {
|
|
26
|
-
if (path.length === 0)
|
|
27
|
-
return ".";
|
|
28
|
-
}
|
|
24
|
+
if (path.length === 0) return ".";
|
|
29
25
|
path = normalizeWindowsPath(path);
|
|
30
26
|
const isUNCPath = path.match(_UNC_REGEX);
|
|
31
27
|
const isPathAbsolute = isAbsolute(path);
|
|
32
28
|
const trailingSeparator = path[path.length - 1] === "/";
|
|
33
29
|
path = normalizeString(path, !isPathAbsolute);
|
|
34
30
|
if (path.length === 0) {
|
|
35
|
-
if (isPathAbsolute)
|
|
36
|
-
return "/";
|
|
37
|
-
}
|
|
31
|
+
if (isPathAbsolute) return "/";
|
|
38
32
|
return trailingSeparator ? "./" : ".";
|
|
39
33
|
}
|
|
40
|
-
if (trailingSeparator)
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
if (_DRIVE_LETTER_RE.test(path)) {
|
|
44
|
-
path += "/";
|
|
45
|
-
}
|
|
34
|
+
if (trailingSeparator) path += "/";
|
|
35
|
+
if (_DRIVE_LETTER_RE.test(path)) path += "/";
|
|
46
36
|
if (isUNCPath) {
|
|
47
|
-
if (!isPathAbsolute) {
|
|
48
|
-
return `//./${path}`;
|
|
49
|
-
}
|
|
37
|
+
if (!isPathAbsolute) return `//./${path}`;
|
|
50
38
|
return `//${path}`;
|
|
51
39
|
}
|
|
52
40
|
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
|
|
@@ -54,28 +42,19 @@ const normalize = function(path) {
|
|
|
54
42
|
const join = function(...segments) {
|
|
55
43
|
let path = "";
|
|
56
44
|
for (const seg of segments) {
|
|
57
|
-
if (!seg)
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
45
|
+
if (!seg) continue;
|
|
60
46
|
if (path.length > 0) {
|
|
61
47
|
const pathTrailing = path[path.length - 1] === "/";
|
|
62
48
|
const segLeading = seg[0] === "/";
|
|
63
49
|
const both = pathTrailing && segLeading;
|
|
64
|
-
if (both)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
path += pathTrailing || segLeading ? seg : `/${seg}`;
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
path += seg;
|
|
71
|
-
}
|
|
50
|
+
if (both) path += seg.slice(1);
|
|
51
|
+
else path += pathTrailing || segLeading ? seg : `/${seg}`;
|
|
52
|
+
} else path += seg;
|
|
72
53
|
}
|
|
73
54
|
return normalize(path);
|
|
74
55
|
};
|
|
75
56
|
function cwd() {
|
|
76
|
-
if (typeof process !== "undefined" && typeof process.cwd === "function")
|
|
77
|
-
return process.cwd().replace(/\\/g, "/");
|
|
78
|
-
}
|
|
57
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd().replace(/\\/g, "/");
|
|
79
58
|
return "/";
|
|
80
59
|
}
|
|
81
60
|
const resolve = function(...arguments_) {
|
|
@@ -84,16 +63,12 @@ const resolve = function(...arguments_) {
|
|
|
84
63
|
let resolvedAbsolute = false;
|
|
85
64
|
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
86
65
|
const path = index >= 0 ? arguments_[index] : cwd();
|
|
87
|
-
if (!path || path.length === 0)
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
66
|
+
if (!path || path.length === 0) continue;
|
|
90
67
|
resolvedPath = `${path}/${resolvedPath}`;
|
|
91
68
|
resolvedAbsolute = isAbsolute(path);
|
|
92
69
|
}
|
|
93
70
|
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
94
|
-
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
|
|
95
|
-
return `/${resolvedPath}`;
|
|
96
|
-
}
|
|
71
|
+
if (resolvedAbsolute && !isAbsolute(resolvedPath)) return `/${resolvedPath}`;
|
|
97
72
|
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
98
73
|
};
|
|
99
74
|
function normalizeString(path, allowAboveRoot) {
|
|
@@ -103,13 +78,9 @@ function normalizeString(path, allowAboveRoot) {
|
|
|
103
78
|
let dots = 0;
|
|
104
79
|
let char = null;
|
|
105
80
|
for (let index = 0; index <= path.length; ++index) {
|
|
106
|
-
if (index < path.length)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
break;
|
|
110
|
-
} else {
|
|
111
|
-
char = "/";
|
|
112
|
-
}
|
|
81
|
+
if (index < path.length) char = path[index];
|
|
82
|
+
else if (char === "/") break;
|
|
83
|
+
else char = "/";
|
|
113
84
|
if (char === "/") {
|
|
114
85
|
if (lastSlash === index - 1 || dots === 1);
|
|
115
86
|
else if (dots === 2) {
|
|
@@ -139,20 +110,14 @@ function normalizeString(path, allowAboveRoot) {
|
|
|
139
110
|
lastSegmentLength = 2;
|
|
140
111
|
}
|
|
141
112
|
} else {
|
|
142
|
-
if (res.length > 0) {
|
|
143
|
-
|
|
144
|
-
} else {
|
|
145
|
-
res = path.slice(lastSlash + 1, index);
|
|
146
|
-
}
|
|
113
|
+
if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
|
|
114
|
+
else res = path.slice(lastSlash + 1, index);
|
|
147
115
|
lastSegmentLength = index - lastSlash - 1;
|
|
148
116
|
}
|
|
149
117
|
lastSlash = index;
|
|
150
118
|
dots = 0;
|
|
151
|
-
} else if (char === "." && dots !== -1)
|
|
152
|
-
|
|
153
|
-
} else {
|
|
154
|
-
dots = -1;
|
|
155
|
-
}
|
|
119
|
+
} else if (char === "." && dots !== -1) ++dots;
|
|
120
|
+
else dots = -1;
|
|
156
121
|
}
|
|
157
122
|
return res;
|
|
158
123
|
}
|
|
@@ -166,9 +131,7 @@ const extname = function(p) {
|
|
|
166
131
|
};
|
|
167
132
|
const dirname = function(p) {
|
|
168
133
|
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
169
|
-
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0]))
|
|
170
|
-
segments[0] += "/";
|
|
171
|
-
}
|
|
134
|
+
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) segments[0] += "/";
|
|
172
135
|
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
173
136
|
};
|
|
174
137
|
const basename = function(p, extension) {
|
|
@@ -187,12 +150,12 @@ const basename = function(p, extension) {
|
|
|
187
150
|
const { existsSync, readdirSync, statSync } = fs;
|
|
188
151
|
function findMockRedirect(root, mockPath, external) {
|
|
189
152
|
const path = external || mockPath;
|
|
153
|
+
// it's a node_module alias
|
|
154
|
+
// all mocks should be inside <root>/__mocks__
|
|
190
155
|
if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) {
|
|
191
156
|
const mockDirname = dirname(path);
|
|
192
157
|
const mockFolder = join(root, "__mocks__", mockDirname);
|
|
193
|
-
if (!existsSync(mockFolder))
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
158
|
+
if (!existsSync(mockFolder)) return null;
|
|
196
159
|
const baseOriginal = basename(path);
|
|
197
160
|
function findFile(mockFolder, baseOriginal) {
|
|
198
161
|
const files = readdirSync(mockFolder);
|
|
@@ -200,13 +163,12 @@ function findMockRedirect(root, mockPath, external) {
|
|
|
200
163
|
const baseFile = basename(file, extname(file));
|
|
201
164
|
if (baseFile === baseOriginal) {
|
|
202
165
|
const path = resolve(mockFolder, file);
|
|
203
|
-
if
|
|
204
|
-
|
|
205
|
-
|
|
166
|
+
// if the same name, return the file
|
|
167
|
+
if (statSync(path).isFile()) return path;
|
|
168
|
+
else {
|
|
169
|
+
// find folder/index.{js,ts}
|
|
206
170
|
const indexFile = findFile(path, "index");
|
|
207
|
-
if (indexFile)
|
|
208
|
-
return indexFile;
|
|
209
|
-
}
|
|
171
|
+
if (indexFile) return indexFile;
|
|
210
172
|
}
|
|
211
173
|
}
|
|
212
174
|
}
|
|
@@ -235,6 +197,7 @@ const builtins = new Set([
|
|
|
235
197
|
"util/types",
|
|
236
198
|
"wasi"
|
|
237
199
|
]);
|
|
200
|
+
// https://nodejs.org/api/modules.html#built-in-modules-with-mandatory-node-prefix
|
|
238
201
|
const prefixedBuiltins = new Set([
|
|
239
202
|
"node:sea",
|
|
240
203
|
"node:sqlite",
|
|
@@ -243,9 +206,7 @@ const prefixedBuiltins = new Set([
|
|
|
243
206
|
]);
|
|
244
207
|
const NODE_BUILTIN_NAMESPACE = "node:";
|
|
245
208
|
function isNodeBuiltin(id) {
|
|
246
|
-
if (prefixedBuiltins.has(id))
|
|
247
|
-
return true;
|
|
248
|
-
}
|
|
209
|
+
if (prefixedBuiltins.has(id)) return true;
|
|
249
210
|
return builtins.has(id.startsWith(NODE_BUILTIN_NAMESPACE) ? id.slice(NODE_BUILTIN_NAMESPACE.length) : id);
|
|
250
211
|
}
|
|
251
212
|
|
|
@@ -255,24 +216,21 @@ class VitestMocker {
|
|
|
255
216
|
spyModule;
|
|
256
217
|
primitives;
|
|
257
218
|
filterPublicKeys;
|
|
258
|
-
registries = new Map();
|
|
219
|
+
registries = /* @__PURE__ */ new Map();
|
|
259
220
|
mockContext = { callstack: null };
|
|
260
221
|
constructor(executor) {
|
|
261
222
|
this.executor = executor;
|
|
262
223
|
const context = this.executor.options.context;
|
|
263
|
-
if (context) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
Map
|
|
274
|
-
};
|
|
275
|
-
}
|
|
224
|
+
if (context) this.primitives = vm.runInContext("({ Object, Error, Function, RegExp, Symbol, Array, Map })", context);
|
|
225
|
+
else this.primitives = {
|
|
226
|
+
Object,
|
|
227
|
+
Error,
|
|
228
|
+
Function,
|
|
229
|
+
RegExp,
|
|
230
|
+
Symbol: globalThis.Symbol,
|
|
231
|
+
Array,
|
|
232
|
+
Map
|
|
233
|
+
};
|
|
276
234
|
const Symbol = this.primitives.Symbol;
|
|
277
235
|
this.filterPublicKeys = [
|
|
278
236
|
"__esModule",
|
|
@@ -305,9 +263,7 @@ class VitestMocker {
|
|
|
305
263
|
}
|
|
306
264
|
getMockerRegistry() {
|
|
307
265
|
const suite = this.getSuiteFilepath();
|
|
308
|
-
if (!this.registries.has(suite))
|
|
309
|
-
this.registries.set(suite, new MockerRegistry());
|
|
310
|
-
}
|
|
266
|
+
if (!this.registries.has(suite)) this.registries.set(suite, new MockerRegistry());
|
|
311
267
|
return this.registries.get(suite);
|
|
312
268
|
}
|
|
313
269
|
reset() {
|
|
@@ -315,9 +271,7 @@ class VitestMocker {
|
|
|
315
271
|
}
|
|
316
272
|
deleteCachedItem(id) {
|
|
317
273
|
const mockId = this.getMockPath(id);
|
|
318
|
-
if (this.moduleCache.has(mockId))
|
|
319
|
-
this.moduleCache.delete(mockId);
|
|
320
|
-
}
|
|
274
|
+
if (this.moduleCache.has(mockId)) this.moduleCache.delete(mockId);
|
|
321
275
|
}
|
|
322
276
|
isModuleDirectory(path) {
|
|
323
277
|
return this.moduleDirectories.some((dir) => path.includes(dir));
|
|
@@ -337,14 +291,15 @@ class VitestMocker {
|
|
|
337
291
|
try {
|
|
338
292
|
[id, fsPath] = await this.executor.originalResolveUrl(rawId, importer);
|
|
339
293
|
} catch (error) {
|
|
294
|
+
// it's allowed to mock unresolved modules
|
|
340
295
|
if (error.code === "ERR_MODULE_NOT_FOUND") {
|
|
341
296
|
const { id: unresolvedId } = error[Symbol.for("vitest.error.not_found.data")];
|
|
342
297
|
id = unresolvedId;
|
|
343
298
|
fsPath = unresolvedId;
|
|
344
|
-
} else
|
|
345
|
-
throw error;
|
|
346
|
-
}
|
|
299
|
+
} else throw error;
|
|
347
300
|
}
|
|
301
|
+
// external is node_module or unresolved module
|
|
302
|
+
// for example, some people mock "vscode" and don't have it installed
|
|
348
303
|
const external = !isAbsolute$1(fsPath) || this.isModuleDirectory(fsPath) ? rawId : null;
|
|
349
304
|
return {
|
|
350
305
|
id,
|
|
@@ -353,37 +308,28 @@ class VitestMocker {
|
|
|
353
308
|
};
|
|
354
309
|
}
|
|
355
310
|
async resolveMocks() {
|
|
356
|
-
if (!VitestMocker.pendingIds.length)
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
311
|
+
if (!VitestMocker.pendingIds.length) return;
|
|
359
312
|
await Promise.all(VitestMocker.pendingIds.map(async (mock) => {
|
|
360
313
|
const { fsPath, external } = await this.resolvePath(mock.id, mock.importer);
|
|
361
|
-
if (mock.action === "unmock")
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
if (mock.action === "mock") {
|
|
365
|
-
this.mockPath(mock.id, fsPath, external, mock.type, mock.factory);
|
|
366
|
-
}
|
|
314
|
+
if (mock.action === "unmock") this.unmockPath(fsPath);
|
|
315
|
+
if (mock.action === "mock") this.mockPath(mock.id, fsPath, external, mock.type, mock.factory);
|
|
367
316
|
}));
|
|
368
317
|
VitestMocker.pendingIds = [];
|
|
369
318
|
}
|
|
370
319
|
async callFunctionMock(dep, mock) {
|
|
371
320
|
const cached = this.moduleCache.get(dep)?.exports;
|
|
372
|
-
if (cached)
|
|
373
|
-
return cached;
|
|
374
|
-
}
|
|
321
|
+
if (cached) return cached;
|
|
375
322
|
const exports = await mock.resolve();
|
|
376
323
|
const moduleExports = new Proxy(exports, { get: (target, prop) => {
|
|
377
324
|
const val = target[prop];
|
|
325
|
+
// 'then' can exist on non-Promise objects, need nested instanceof check for logic to work
|
|
378
326
|
if (prop === "then") {
|
|
379
|
-
if (target instanceof Promise)
|
|
380
|
-
return target.then.bind(target);
|
|
381
|
-
}
|
|
327
|
+
if (target instanceof Promise) return target.then.bind(target);
|
|
382
328
|
} else if (!(prop in target)) {
|
|
383
|
-
if (this.filterPublicKeys.includes(prop))
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
329
|
+
if (this.filterPublicKeys.includes(prop)) return void 0;
|
|
330
|
+
throw this.createError(`[vitest] No "${String(prop)}" export is defined on the "${mock.raw}" mock. Did you forget to return it from "vi.mock"?
|
|
331
|
+
If you need to partially mock a module, you can use "importOriginal" helper inside:
|
|
332
|
+
`, highlight(`vi.mock(import("${mock.raw}"), async (importOriginal) => {
|
|
387
333
|
const actual = await importOriginal()
|
|
388
334
|
return {
|
|
389
335
|
...actual,
|
|
@@ -396,9 +342,11 @@ class VitestMocker {
|
|
|
396
342
|
this.moduleCache.set(dep, { exports: moduleExports });
|
|
397
343
|
return moduleExports;
|
|
398
344
|
}
|
|
345
|
+
// public method to avoid circular dependency
|
|
399
346
|
getMockContext() {
|
|
400
347
|
return this.mockContext;
|
|
401
348
|
}
|
|
349
|
+
// path used to store mocked dependencies
|
|
402
350
|
getMockPath(dep) {
|
|
403
351
|
return `mock:${dep}`;
|
|
404
352
|
}
|
|
@@ -414,9 +362,7 @@ class VitestMocker {
|
|
|
414
362
|
}
|
|
415
363
|
mockObject(object, mockExports = {}, behavior = "automock") {
|
|
416
364
|
const spyOn = this.spyModule?.spyOn;
|
|
417
|
-
if (!spyOn)
|
|
418
|
-
throw this.createError("[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
|
|
419
|
-
}
|
|
365
|
+
if (!spyOn) throw this.createError("[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction.");
|
|
420
366
|
return mockObject({
|
|
421
367
|
globalConstructors: this.primitives,
|
|
422
368
|
spyOn,
|
|
@@ -432,18 +378,14 @@ class VitestMocker {
|
|
|
432
378
|
mockPath(originalId, path, external, mockType, factory) {
|
|
433
379
|
const registry = this.getMockerRegistry();
|
|
434
380
|
const id = this.normalizePath(path);
|
|
435
|
-
if (mockType === "manual")
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
registry.register("autospy", originalId, id, id);
|
|
439
|
-
} else {
|
|
381
|
+
if (mockType === "manual") registry.register("manual", originalId, id, id, factory);
|
|
382
|
+
else if (mockType === "autospy") registry.register("autospy", originalId, id, id);
|
|
383
|
+
else {
|
|
440
384
|
const redirect = this.resolveMockPath(id, external);
|
|
441
|
-
if (redirect)
|
|
442
|
-
|
|
443
|
-
} else {
|
|
444
|
-
registry.register("automock", originalId, id, id);
|
|
445
|
-
}
|
|
385
|
+
if (redirect) registry.register("redirect", originalId, id, id, redirect);
|
|
386
|
+
else registry.register("automock", originalId, id, id);
|
|
446
387
|
}
|
|
388
|
+
// every time the mock is registered, we remove the previous one from the cache
|
|
447
389
|
this.deleteCachedItem(id);
|
|
448
390
|
}
|
|
449
391
|
async importActual(rawId, importer, callstack) {
|
|
@@ -457,52 +399,44 @@ class VitestMocker {
|
|
|
457
399
|
let mock = this.getDependencyMock(normalizedId);
|
|
458
400
|
if (!mock) {
|
|
459
401
|
const redirect = this.resolveMockPath(normalizedId, external);
|
|
460
|
-
if (redirect)
|
|
461
|
-
|
|
462
|
-
} else {
|
|
463
|
-
mock = new AutomockedModule(rawId, normalizedId, normalizedId);
|
|
464
|
-
}
|
|
402
|
+
if (redirect) mock = new RedirectedModule(rawId, normalizedId, normalizedId, redirect);
|
|
403
|
+
else mock = new AutomockedModule(rawId, normalizedId, normalizedId);
|
|
465
404
|
}
|
|
466
405
|
if (mock.type === "automock" || mock.type === "autospy") {
|
|
467
406
|
const mod = await this.executor.cachedRequest(id, fsPath, [importee]);
|
|
468
407
|
return this.mockObject(mod, {}, mock.type);
|
|
469
408
|
}
|
|
470
|
-
if (mock.type === "manual")
|
|
471
|
-
return this.callFunctionMock(fsPath, mock);
|
|
472
|
-
}
|
|
409
|
+
if (mock.type === "manual") return this.callFunctionMock(fsPath, mock);
|
|
473
410
|
return this.executor.dependencyRequest(mock.redirect, mock.redirect, [importee]);
|
|
474
411
|
}
|
|
475
412
|
async requestWithMock(url, callstack) {
|
|
476
413
|
const id = this.normalizePath(url);
|
|
477
414
|
const mock = this.getDependencyMock(id);
|
|
478
|
-
if (!mock)
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
415
|
+
if (!mock) return;
|
|
481
416
|
const mockPath = this.getMockPath(id);
|
|
482
417
|
if (mock.type === "automock" || mock.type === "autospy") {
|
|
483
418
|
const cache = this.moduleCache.get(mockPath);
|
|
484
|
-
if (cache.exports)
|
|
485
|
-
return cache.exports;
|
|
486
|
-
}
|
|
419
|
+
if (cache.exports) return cache.exports;
|
|
487
420
|
const exports = {};
|
|
421
|
+
// Assign the empty exports object early to allow for cycles to work. The object will be filled by mockObject()
|
|
488
422
|
this.moduleCache.set(mockPath, { exports });
|
|
489
423
|
const mod = await this.executor.directRequest(url, url, callstack);
|
|
490
424
|
this.mockObject(mod, exports, mock.type);
|
|
491
425
|
return exports;
|
|
492
426
|
}
|
|
493
|
-
if (mock.type === "manual" && !callstack.includes(mockPath) && !callstack.includes(url)) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
return mock.redirect;
|
|
427
|
+
if (mock.type === "manual" && !callstack.includes(mockPath) && !callstack.includes(url)) try {
|
|
428
|
+
callstack.push(mockPath);
|
|
429
|
+
// this will not work if user does Promise.all(import(), import())
|
|
430
|
+
// we can also use AsyncLocalStorage to store callstack, but this won't work in the browser
|
|
431
|
+
// maybe we should improve mock API in the future?
|
|
432
|
+
this.mockContext.callstack = callstack;
|
|
433
|
+
return await this.callFunctionMock(mockPath, mock);
|
|
434
|
+
} finally {
|
|
435
|
+
this.mockContext.callstack = null;
|
|
436
|
+
const indexMock = callstack.indexOf(mockPath);
|
|
437
|
+
callstack.splice(indexMock, 1);
|
|
505
438
|
}
|
|
439
|
+
else if (mock.type === "redirect" && !callstack.includes(mock.redirect)) return mock.redirect;
|
|
506
440
|
}
|
|
507
441
|
queueMock(id, importer, factoryOrOptions) {
|
|
508
442
|
const mockType = getMockType(factoryOrOptions);
|
|
@@ -510,7 +444,7 @@ class VitestMocker {
|
|
|
510
444
|
action: "mock",
|
|
511
445
|
id,
|
|
512
446
|
importer,
|
|
513
|
-
factory: typeof factoryOrOptions === "function" ? factoryOrOptions :
|
|
447
|
+
factory: typeof factoryOrOptions === "function" ? factoryOrOptions : void 0,
|
|
514
448
|
type: mockType
|
|
515
449
|
});
|
|
516
450
|
}
|
|
@@ -523,12 +457,8 @@ class VitestMocker {
|
|
|
523
457
|
}
|
|
524
458
|
}
|
|
525
459
|
function getMockType(factoryOrOptions) {
|
|
526
|
-
if (!factoryOrOptions)
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
if (typeof factoryOrOptions === "function") {
|
|
530
|
-
return "manual";
|
|
531
|
-
}
|
|
460
|
+
if (!factoryOrOptions) return "automock";
|
|
461
|
+
if (typeof factoryOrOptions === "function") return "manual";
|
|
532
462
|
return factoryOrOptions.spy ? "autospy" : "automock";
|
|
533
463
|
}
|
|
534
464
|
|
|
@@ -540,7 +470,7 @@ async function createVitestExecutor(options) {
|
|
|
540
470
|
await runner.mocker.initializeSpyModule();
|
|
541
471
|
return runner;
|
|
542
472
|
}
|
|
543
|
-
const externalizeMap = new Map();
|
|
473
|
+
const externalizeMap = /* @__PURE__ */ new Map();
|
|
544
474
|
const bareVitestRegexp = /^@?vitest(?:\/|$)/;
|
|
545
475
|
const dispose = [];
|
|
546
476
|
function listenForErrors(state) {
|
|
@@ -549,15 +479,13 @@ function listenForErrors(state) {
|
|
|
549
479
|
function catchError(err, type, event) {
|
|
550
480
|
const worker = state();
|
|
551
481
|
const listeners = process.listeners(event);
|
|
552
|
-
if
|
|
553
|
-
|
|
554
|
-
|
|
482
|
+
// if there is another listener, assume that it's handled by user code
|
|
483
|
+
// one is Vitest's own listener
|
|
484
|
+
if (listeners.length > 1) return;
|
|
555
485
|
const error = processError(err);
|
|
556
486
|
if (!isPrimitive(error)) {
|
|
557
|
-
error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name :
|
|
558
|
-
if (worker.filepath)
|
|
559
|
-
error.VITEST_TEST_PATH = worker.filepath;
|
|
560
|
-
}
|
|
487
|
+
error.VITEST_TEST_NAME = worker.current?.type === "test" ? worker.current.name : void 0;
|
|
488
|
+
if (worker.filepath) error.VITEST_TEST_PATH = worker.filepath;
|
|
561
489
|
error.VITEST_AFTER_ENV_TEARDOWN = worker.environmentTeardownRun;
|
|
562
490
|
}
|
|
563
491
|
state().rpc.onUnhandledError(error, type);
|
|
@@ -573,9 +501,9 @@ function listenForErrors(state) {
|
|
|
573
501
|
}
|
|
574
502
|
const relativeIds = {};
|
|
575
503
|
function getVitestImport(id, state) {
|
|
576
|
-
if (externalizeMap.has(id)) {
|
|
577
|
-
|
|
578
|
-
|
|
504
|
+
if (externalizeMap.has(id)) return { externalize: externalizeMap.get(id) };
|
|
505
|
+
// always externalize Vitest because we import from there before running tests
|
|
506
|
+
// so we already have it cached by Node.js
|
|
579
507
|
const root = state().config.root;
|
|
580
508
|
const relativeRoot = relativeIds[root] ?? (relativeIds[root] = normalizedDistDir.slice(root.length));
|
|
581
509
|
if (id.includes(distDir) || id.includes(normalizedDistDir) || relativeRoot && relativeRoot !== "/" && id.startsWith(relativeRoot)) {
|
|
@@ -603,9 +531,7 @@ async function startVitestExecutor(options) {
|
|
|
603
531
|
return await createVitestExecutor({
|
|
604
532
|
async fetchModule(id) {
|
|
605
533
|
const vitest = getVitestImport(id, state);
|
|
606
|
-
if (vitest)
|
|
607
|
-
return vitest;
|
|
608
|
-
}
|
|
534
|
+
if (vitest) return vitest;
|
|
609
535
|
const result = await rpc().fetch(id, getTransformMode());
|
|
610
536
|
if (result.id && !result.externalize) {
|
|
611
537
|
const code = readFileSync(result.id, "utf-8");
|
|
@@ -638,9 +564,7 @@ async function startVitestExecutor(options) {
|
|
|
638
564
|
});
|
|
639
565
|
}
|
|
640
566
|
function updateStyle(id, css) {
|
|
641
|
-
if (typeof document === "undefined")
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
567
|
+
if (typeof document === "undefined") return;
|
|
644
568
|
const element = document.querySelector(`[data-vite-dev-id="${id}"]`);
|
|
645
569
|
if (element) {
|
|
646
570
|
element.textContent = css;
|
|
@@ -654,13 +578,9 @@ function updateStyle(id, css) {
|
|
|
654
578
|
head?.appendChild(style);
|
|
655
579
|
}
|
|
656
580
|
function removeStyle(id) {
|
|
657
|
-
if (typeof document === "undefined")
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
581
|
+
if (typeof document === "undefined") return;
|
|
660
582
|
const sheet = document.querySelector(`[data-vite-dev-id="${id}"]`);
|
|
661
|
-
if (sheet)
|
|
662
|
-
document.head.removeChild(sheet);
|
|
663
|
-
}
|
|
583
|
+
if (sheet) document.head.removeChild(sheet);
|
|
664
584
|
}
|
|
665
585
|
function getDefaultRequestStubs(context) {
|
|
666
586
|
if (!context) {
|
|
@@ -705,36 +625,31 @@ class VitestExecutor extends ViteNodeRunner {
|
|
|
705
625
|
} else if (options.externalModulesExecutor) {
|
|
706
626
|
this.primitives = vm.runInContext("({ Object, Reflect, Symbol })", options.context);
|
|
707
627
|
this.externalModules = options.externalModulesExecutor;
|
|
708
|
-
} else
|
|
709
|
-
throw new Error("When context is provided, externalModulesExecutor must be provided as well.");
|
|
710
|
-
}
|
|
628
|
+
} else throw new Error("When context is provided, externalModulesExecutor must be provided as well.");
|
|
711
629
|
}
|
|
712
630
|
getContextPrimitives() {
|
|
713
631
|
return this.primitives;
|
|
714
632
|
}
|
|
715
633
|
get state() {
|
|
634
|
+
// @ts-expect-error injected untyped global
|
|
716
635
|
return globalThis.__vitest_worker__ || this.options.state;
|
|
717
636
|
}
|
|
718
637
|
get moduleExecutionInfo() {
|
|
719
638
|
return this.options.moduleExecutionInfo;
|
|
720
639
|
}
|
|
721
640
|
shouldResolveId(id, _importee) {
|
|
722
|
-
if (isInternalRequest(id) || id.startsWith("data:"))
|
|
723
|
-
return false;
|
|
724
|
-
}
|
|
641
|
+
if (isInternalRequest(id) || id.startsWith("data:")) return false;
|
|
725
642
|
const transformMode = this.state.environment?.transformMode ?? "ssr";
|
|
643
|
+
// do not try and resolve node builtins in Node
|
|
644
|
+
// import('url') returns Node internal even if 'url' package is installed
|
|
726
645
|
return transformMode === "ssr" ? !isNodeBuiltin$1(id) : !id.startsWith("node:");
|
|
727
646
|
}
|
|
728
647
|
async originalResolveUrl(id, importer) {
|
|
729
648
|
return super.resolveUrl(id, importer);
|
|
730
649
|
}
|
|
731
650
|
async resolveUrl(id, importer) {
|
|
732
|
-
if (VitestMocker.pendingIds.length)
|
|
733
|
-
|
|
734
|
-
}
|
|
735
|
-
if (importer && importer.startsWith("mock:")) {
|
|
736
|
-
importer = importer.slice(5);
|
|
737
|
-
}
|
|
651
|
+
if (VitestMocker.pendingIds.length) await this.mocker.resolveMocks();
|
|
652
|
+
if (importer && importer.startsWith("mock:")) importer = importer.slice(5);
|
|
738
653
|
try {
|
|
739
654
|
return await super.resolveUrl(id, importer);
|
|
740
655
|
} catch (error) {
|
|
@@ -742,18 +657,15 @@ class VitestExecutor extends ViteNodeRunner {
|
|
|
742
657
|
const { id } = error[Symbol.for("vitest.error.not_found.data")];
|
|
743
658
|
const path = this.mocker.normalizePath(id);
|
|
744
659
|
const mock = this.mocker.getDependencyMock(path);
|
|
745
|
-
if (mock !==
|
|
746
|
-
return [id, id];
|
|
747
|
-
}
|
|
660
|
+
if (mock !== void 0) return [id, id];
|
|
748
661
|
}
|
|
749
662
|
throw error;
|
|
750
663
|
}
|
|
751
664
|
}
|
|
752
665
|
async runModule(context, transformed) {
|
|
753
666
|
const vmContext = this.options.context;
|
|
754
|
-
if (!vmContext || !this.externalModules)
|
|
755
|
-
|
|
756
|
-
}
|
|
667
|
+
if (!vmContext || !this.externalModules) return super.runModule(context, transformed);
|
|
668
|
+
// add 'use strict' since ESM enables it by default
|
|
757
669
|
const codeDefinition = `'use strict';async (${Object.keys(context).join(",")})=>{{`;
|
|
758
670
|
const code = `${codeDefinition}${transformed}\n}}`;
|
|
759
671
|
const options = {
|
|
@@ -761,37 +673,34 @@ class VitestExecutor extends ViteNodeRunner {
|
|
|
761
673
|
lineOffset: 0,
|
|
762
674
|
columnOffset: -codeDefinition.length
|
|
763
675
|
};
|
|
764
|
-
this.
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
676
|
+
const finishModuleExecutionInfo = this.startCalculateModuleExecutionInfo(options.filename, codeDefinition.length);
|
|
677
|
+
try {
|
|
678
|
+
const fn = vm.runInContext(code, vmContext, {
|
|
679
|
+
...options,
|
|
680
|
+
importModuleDynamically: this.externalModules.importModuleDynamically
|
|
681
|
+
});
|
|
682
|
+
await fn(...Object.values(context));
|
|
683
|
+
} finally {
|
|
684
|
+
this.options.moduleExecutionInfo?.set(options.filename, finishModuleExecutionInfo());
|
|
685
|
+
}
|
|
770
686
|
}
|
|
771
687
|
async importExternalModule(path) {
|
|
772
|
-
if (this.externalModules)
|
|
773
|
-
return this.externalModules.import(path);
|
|
774
|
-
}
|
|
688
|
+
if (this.externalModules) return this.externalModules.import(path);
|
|
775
689
|
return super.importExternalModule(path);
|
|
776
690
|
}
|
|
777
691
|
async dependencyRequest(id, fsPath, callstack) {
|
|
778
692
|
const mocked = await this.mocker.requestWithMock(fsPath, callstack);
|
|
779
|
-
if (typeof mocked === "string")
|
|
780
|
-
|
|
781
|
-
}
|
|
782
|
-
if (mocked && typeof mocked === "object") {
|
|
783
|
-
return mocked;
|
|
784
|
-
}
|
|
693
|
+
if (typeof mocked === "string") return super.dependencyRequest(mocked, mocked, callstack);
|
|
694
|
+
if (mocked && typeof mocked === "object") return mocked;
|
|
785
695
|
return super.dependencyRequest(id, fsPath, callstack);
|
|
786
696
|
}
|
|
787
697
|
prepareContext(context) {
|
|
698
|
+
// support `import.meta.vitest` for test entry
|
|
788
699
|
if (this.state.filepath && normalize$1(this.state.filepath) === normalize$1(context.__filename)) {
|
|
789
700
|
const globalNamespace = this.options.context || globalThis;
|
|
790
701
|
Object.defineProperty(context.__vite_ssr_import_meta__, "vitest", { get: () => globalNamespace.__vitest_index__ });
|
|
791
702
|
}
|
|
792
|
-
if (this.options.context && this.externalModules)
|
|
793
|
-
context.require = this.externalModules.createRequire(context.__filename);
|
|
794
|
-
}
|
|
703
|
+
if (this.options.context && this.externalModules) context.require = this.externalModules.createRequire(context.__filename);
|
|
795
704
|
return context;
|
|
796
705
|
}
|
|
797
706
|
}
|
|
@@ -18,9 +18,7 @@ class VitestGit {
|
|
|
18
18
|
}
|
|
19
19
|
async findChangedFiles(options) {
|
|
20
20
|
const root = await this.getRoot(this.cwd);
|
|
21
|
-
if (!root)
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
21
|
+
if (!root) return null;
|
|
24
22
|
this.root = root;
|
|
25
23
|
const changedSince = options.changedSince;
|
|
26
24
|
if (typeof changedSince === "string") {
|