vitest 2.1.0-beta.5 → 2.1.0-beta.7
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 +59 -320
- package/dist/browser.d.ts +6 -3
- package/dist/browser.js +2 -2
- package/dist/chunks/{RandomSequencer.B4M2ux5b.js → RandomSequencer.Bh5-tlNJ.js} +77 -58
- package/dist/chunks/{base.BH-FAiX7.js → base.BlXpj3e_.js} +1 -29
- package/dist/chunks/{base.BYvKfYzm.js → base.CchlWrnV.js} +2 -4
- package/dist/chunks/{cac.DosbLiJg.js → cac.B5XYKv2_.js} +26 -17
- package/dist/chunks/{cli-api.D8zgNrBU.js → cli-api.ByZPnilx.js} +7419 -7959
- package/dist/chunks/{constants.CaAN7icJ.js → constants.fzPh7AOq.js} +1 -1
- package/dist/chunks/{coverage.CqfT4xaf.js → coverage.zlNdAMHK.js} +5 -3
- package/dist/chunks/{creator.COdKw_ZN.js → creator.zfBZSJzo.js} +9 -9
- package/dist/chunks/{environment.0M5R1SX_.d.ts → environment.C5eAp3K6.d.ts} +1 -1
- package/dist/chunks/{execute.DT9BA6zp.js → execute._eQQfgI8.js} +322 -232
- package/dist/chunks/{git.ZtkbKc8u.js → git.B5SDxu-n.js} +5 -5
- package/dist/chunks/{globals.DRPLtPOv.js → globals.jM7MxN2t.js} +4 -4
- package/dist/chunks/{index.CM5UI-4O.js → index.Bn75ITYg.js} +3 -3
- package/dist/chunks/index.CPD77dLA.js +133 -0
- package/dist/chunks/{index.CxWPpGJz.js → index.CSjyR2-v.js} +2 -2
- package/dist/chunks/{index.Dx3f477d.js → index.DpJO1tkB.js} +1110 -1104
- package/dist/chunks/{index.CNZXZ9PJ.js → index.xm8OIiKD.js} +1 -1
- package/dist/chunks/mocker.cRtM890J.d.ts +17 -0
- package/dist/chunks/{reporters.DbwOGCsU.d.ts → reporters.WnPwkmgA.d.ts} +154 -129
- package/dist/chunks/{resolveConfig.RHsAM2_Q.js → resolveConfig.-K5hHm0S.js} +56 -34
- package/dist/chunks/{runBaseTests.BAhL8UH_.js → runBaseTests.Cztfoflv.js} +7 -7
- package/dist/chunks/{setup-common.KBrCO5LJ.js → setup-common.fGBFoQKJ.js} +1 -1
- package/dist/chunks/{utils.C3_cBsyn.js → utils.Cn0zI1t3.js} +16 -3
- package/dist/chunks/{utils.DO38lwfj.js → utils.Dbnmsfq1.js} +1 -1
- package/dist/chunks/{vi.B6QZ938s.js → vi.DGgiNzJE.js} +46 -37
- package/dist/chunks/vite.D2yAwzwa.d.ts +11 -0
- package/dist/chunks/{vm.kl9T_5ai.js → vm.CPXwWp4C.js} +1 -2
- package/dist/chunks/{worker.Cx2xE71X.d.ts → worker.Bws9Zuxu.d.ts} +1 -1
- package/dist/chunks/{worker.BANO5ak1.d.ts → worker.CmPmTxgH.d.ts} +2 -14
- package/dist/cli.js +3 -3
- package/dist/config.cjs +1 -0
- package/dist/config.d.ts +70 -2
- package/dist/config.js +1 -0
- package/dist/coverage.d.ts +4 -3
- package/dist/coverage.js +29 -5
- package/dist/environments.d.ts +2 -2
- package/dist/execute.d.ts +13 -14
- package/dist/execute.js +3 -2
- package/dist/index.d.ts +23 -16
- package/dist/index.js +4 -4
- package/dist/mocker.d.ts +1 -0
- package/dist/mocker.js +1 -0
- package/dist/node.d.ts +27 -9
- package/dist/node.js +44 -39
- package/dist/reporters.d.ts +2 -2
- package/dist/reporters.js +8 -8
- package/dist/runners.js +2 -2
- package/dist/utils.d.ts +1 -0
- package/dist/worker.js +3 -2
- package/dist/workers/forks.js +5 -4
- package/dist/workers/runVmTests.js +6 -6
- package/dist/workers/threads.js +5 -4
- package/dist/workers/vmForks.js +4 -4
- package/dist/workers/vmThreads.js +4 -4
- package/dist/workers.d.ts +4 -16
- package/dist/workers.js +6 -5
- package/mocker.d.ts +1 -0
- package/package.json +22 -17
- package/dist/chunks/index.DNUmWFkO.js +0 -319
|
@@ -2,34 +2,239 @@ import vm from 'node:vm';
|
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import { ViteNodeRunner, DEFAULT_REQUEST_STUBS } from 'vite-node/client';
|
|
5
|
-
import {
|
|
6
|
-
import { resolve, isAbsolute
|
|
5
|
+
import { isInternalRequest, isNodeBuiltin as isNodeBuiltin$1, toFilePath, isPrimitive } from 'vite-node/utils';
|
|
6
|
+
import { resolve as resolve$1, isAbsolute as isAbsolute$1, normalize as normalize$1, relative } from 'pathe';
|
|
7
7
|
import { processError } from '@vitest/utils/error';
|
|
8
8
|
import { distDir } from '../path.js';
|
|
9
|
-
import { highlight
|
|
10
|
-
import {
|
|
9
|
+
import { highlight } from '@vitest/utils';
|
|
10
|
+
import { MockerRegistry, mockObject, RedirectedModule, AutomockedModule } from '@vitest/mocker';
|
|
11
|
+
import { builtinModules } from 'node:module';
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
mockedValueMap = /* @__PURE__ */ new Map();
|
|
17
|
-
getId(value) {
|
|
18
|
-
return this.idMap.get(value);
|
|
19
|
-
}
|
|
20
|
-
getMockedValue(id) {
|
|
21
|
-
return this.mockedValueMap.get(id);
|
|
22
|
-
}
|
|
23
|
-
track(originalValue, mockedValue) {
|
|
24
|
-
const newId = this.idMap.size;
|
|
25
|
-
this.idMap.set(originalValue, newId);
|
|
26
|
-
this.mockedValueMap.set(newId, mockedValue);
|
|
27
|
-
return newId;
|
|
13
|
+
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
14
|
+
function normalizeWindowsPath(input = "") {
|
|
15
|
+
if (!input) {
|
|
16
|
+
return input;
|
|
28
17
|
}
|
|
18
|
+
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
29
19
|
}
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
const _UNC_REGEX = /^[/\\]{2}/;
|
|
21
|
+
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
22
|
+
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
|
|
23
|
+
const normalize = function(path) {
|
|
24
|
+
if (path.length === 0) {
|
|
25
|
+
return ".";
|
|
26
|
+
}
|
|
27
|
+
path = normalizeWindowsPath(path);
|
|
28
|
+
const isUNCPath = path.match(_UNC_REGEX);
|
|
29
|
+
const isPathAbsolute = isAbsolute(path);
|
|
30
|
+
const trailingSeparator = path[path.length - 1] === "/";
|
|
31
|
+
path = normalizeString(path, !isPathAbsolute);
|
|
32
|
+
if (path.length === 0) {
|
|
33
|
+
if (isPathAbsolute) {
|
|
34
|
+
return "/";
|
|
35
|
+
}
|
|
36
|
+
return trailingSeparator ? "./" : ".";
|
|
37
|
+
}
|
|
38
|
+
if (trailingSeparator) {
|
|
39
|
+
path += "/";
|
|
40
|
+
}
|
|
41
|
+
if (_DRIVE_LETTER_RE.test(path)) {
|
|
42
|
+
path += "/";
|
|
43
|
+
}
|
|
44
|
+
if (isUNCPath) {
|
|
45
|
+
if (!isPathAbsolute) {
|
|
46
|
+
return `//./${path}`;
|
|
47
|
+
}
|
|
48
|
+
return `//${path}`;
|
|
49
|
+
}
|
|
50
|
+
return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
|
|
51
|
+
};
|
|
52
|
+
const join = function(...arguments_) {
|
|
53
|
+
if (arguments_.length === 0) {
|
|
54
|
+
return ".";
|
|
55
|
+
}
|
|
56
|
+
let joined;
|
|
57
|
+
for (const argument of arguments_) {
|
|
58
|
+
if (argument && argument.length > 0) {
|
|
59
|
+
if (joined === void 0) {
|
|
60
|
+
joined = argument;
|
|
61
|
+
} else {
|
|
62
|
+
joined += `/${argument}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (joined === void 0) {
|
|
67
|
+
return ".";
|
|
68
|
+
}
|
|
69
|
+
return normalize(joined.replace(/\/\/+/g, "/"));
|
|
70
|
+
};
|
|
71
|
+
function cwd() {
|
|
72
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
73
|
+
return process.cwd().replace(/\\/g, "/");
|
|
74
|
+
}
|
|
75
|
+
return "/";
|
|
32
76
|
}
|
|
77
|
+
const resolve = function(...arguments_) {
|
|
78
|
+
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
79
|
+
let resolvedPath = "";
|
|
80
|
+
let resolvedAbsolute = false;
|
|
81
|
+
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
82
|
+
const path = index >= 0 ? arguments_[index] : cwd();
|
|
83
|
+
if (!path || path.length === 0) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
87
|
+
resolvedAbsolute = isAbsolute(path);
|
|
88
|
+
}
|
|
89
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
90
|
+
if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
|
|
91
|
+
return `/${resolvedPath}`;
|
|
92
|
+
}
|
|
93
|
+
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
94
|
+
};
|
|
95
|
+
function normalizeString(path, allowAboveRoot) {
|
|
96
|
+
let res = "";
|
|
97
|
+
let lastSegmentLength = 0;
|
|
98
|
+
let lastSlash = -1;
|
|
99
|
+
let dots = 0;
|
|
100
|
+
let char = null;
|
|
101
|
+
for (let index = 0; index <= path.length; ++index) {
|
|
102
|
+
if (index < path.length) {
|
|
103
|
+
char = path[index];
|
|
104
|
+
} else if (char === "/") {
|
|
105
|
+
break;
|
|
106
|
+
} else {
|
|
107
|
+
char = "/";
|
|
108
|
+
}
|
|
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) {
|
|
116
|
+
res = "";
|
|
117
|
+
lastSegmentLength = 0;
|
|
118
|
+
} else {
|
|
119
|
+
res = res.slice(0, lastSlashIndex);
|
|
120
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
121
|
+
}
|
|
122
|
+
lastSlash = index;
|
|
123
|
+
dots = 0;
|
|
124
|
+
continue;
|
|
125
|
+
} else if (res.length > 0) {
|
|
126
|
+
res = "";
|
|
127
|
+
lastSegmentLength = 0;
|
|
128
|
+
lastSlash = index;
|
|
129
|
+
dots = 0;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (allowAboveRoot) {
|
|
134
|
+
res += res.length > 0 ? "/.." : "..";
|
|
135
|
+
lastSegmentLength = 2;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
if (res.length > 0) {
|
|
139
|
+
res += `/${path.slice(lastSlash + 1, index)}`;
|
|
140
|
+
} else {
|
|
141
|
+
res = path.slice(lastSlash + 1, index);
|
|
142
|
+
}
|
|
143
|
+
lastSegmentLength = index - lastSlash - 1;
|
|
144
|
+
}
|
|
145
|
+
lastSlash = index;
|
|
146
|
+
dots = 0;
|
|
147
|
+
} else if (char === "." && dots !== -1) {
|
|
148
|
+
++dots;
|
|
149
|
+
} else {
|
|
150
|
+
dots = -1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return res;
|
|
154
|
+
}
|
|
155
|
+
const isAbsolute = function(p) {
|
|
156
|
+
return _IS_ABSOLUTE_RE.test(p);
|
|
157
|
+
};
|
|
158
|
+
const _EXTNAME_RE = /.(\.[^./]+)$/;
|
|
159
|
+
const extname = function(p) {
|
|
160
|
+
const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
|
|
161
|
+
return match && match[1] || "";
|
|
162
|
+
};
|
|
163
|
+
const dirname = function(p) {
|
|
164
|
+
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
165
|
+
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
|
|
166
|
+
segments[0] += "/";
|
|
167
|
+
}
|
|
168
|
+
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
169
|
+
};
|
|
170
|
+
const basename = function(p, extension) {
|
|
171
|
+
const lastSegment = normalizeWindowsPath(p).split("/").pop();
|
|
172
|
+
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const { existsSync, readdirSync, statSync } = fs;
|
|
176
|
+
function findMockRedirect(root, mockPath, external) {
|
|
177
|
+
const path = external || mockPath;
|
|
178
|
+
if (external || isNodeBuiltin(mockPath) || !existsSync(mockPath)) {
|
|
179
|
+
let findFile2 = function(mockFolder2, baseOriginal2) {
|
|
180
|
+
const files = readdirSync(mockFolder2);
|
|
181
|
+
for (const file of files) {
|
|
182
|
+
const baseFile = basename(file, extname(file));
|
|
183
|
+
if (baseFile === baseOriginal2) {
|
|
184
|
+
const path2 = resolve(mockFolder2, file);
|
|
185
|
+
if (statSync(path2).isFile()) {
|
|
186
|
+
return path2;
|
|
187
|
+
} else {
|
|
188
|
+
const indexFile = findFile2(path2, "index");
|
|
189
|
+
if (indexFile) {
|
|
190
|
+
return indexFile;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
};
|
|
197
|
+
const mockDirname = dirname(path);
|
|
198
|
+
const mockFolder = join(root, "__mocks__", mockDirname);
|
|
199
|
+
if (!existsSync(mockFolder)) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const baseOriginal = basename(path);
|
|
203
|
+
return findFile2(mockFolder, baseOriginal);
|
|
204
|
+
}
|
|
205
|
+
const dir = dirname(path);
|
|
206
|
+
const baseId = basename(path);
|
|
207
|
+
const fullPath = resolve(dir, "__mocks__", baseId);
|
|
208
|
+
return existsSync(fullPath) ? fullPath : null;
|
|
209
|
+
}
|
|
210
|
+
const builtins = /* @__PURE__ */ new Set([
|
|
211
|
+
...builtinModules,
|
|
212
|
+
"assert/strict",
|
|
213
|
+
"diagnostics_channel",
|
|
214
|
+
"dns/promises",
|
|
215
|
+
"fs/promises",
|
|
216
|
+
"path/posix",
|
|
217
|
+
"path/win32",
|
|
218
|
+
"readline/promises",
|
|
219
|
+
"stream/consumers",
|
|
220
|
+
"stream/promises",
|
|
221
|
+
"stream/web",
|
|
222
|
+
"timers/promises",
|
|
223
|
+
"util/types",
|
|
224
|
+
"wasi"
|
|
225
|
+
]);
|
|
226
|
+
const prefixedBuiltins = /* @__PURE__ */ new Set(["node:test", "node:sqlite"]);
|
|
227
|
+
const NODE_BUILTIN_NAMESPACE = "node:";
|
|
228
|
+
function isNodeBuiltin(id) {
|
|
229
|
+
if (prefixedBuiltins.has(id)) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
return builtins.has(
|
|
233
|
+
id.startsWith(NODE_BUILTIN_NAMESPACE) ? id.slice(NODE_BUILTIN_NAMESPACE.length) : id
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const spyModulePath = resolve$1(distDir, "spy.js");
|
|
33
238
|
class VitestMocker {
|
|
34
239
|
constructor(executor) {
|
|
35
240
|
this.executor = executor;
|
|
@@ -70,18 +275,15 @@ class VitestMocker {
|
|
|
70
275
|
}
|
|
71
276
|
static pendingIds = [];
|
|
72
277
|
spyModule;
|
|
73
|
-
resolveCache = /* @__PURE__ */ new Map();
|
|
74
278
|
primitives;
|
|
75
279
|
filterPublicKeys;
|
|
280
|
+
registries = /* @__PURE__ */ new Map();
|
|
76
281
|
mockContext = {
|
|
77
282
|
callstack: null
|
|
78
283
|
};
|
|
79
284
|
get root() {
|
|
80
285
|
return this.executor.options.root;
|
|
81
286
|
}
|
|
82
|
-
get mockMap() {
|
|
83
|
-
return this.executor.options.mockMap;
|
|
84
|
-
}
|
|
85
287
|
get moduleCache() {
|
|
86
288
|
return this.executor.moduleCache;
|
|
87
289
|
}
|
|
@@ -91,6 +293,16 @@ class VitestMocker {
|
|
|
91
293
|
async initializeSpyModule() {
|
|
92
294
|
this.spyModule = await this.executor.executeId(spyModulePath);
|
|
93
295
|
}
|
|
296
|
+
getMockerRegistry() {
|
|
297
|
+
const suite = this.getSuiteFilepath();
|
|
298
|
+
if (!this.registries.has(suite)) {
|
|
299
|
+
this.registries.set(suite, new MockerRegistry());
|
|
300
|
+
}
|
|
301
|
+
return this.registries.get(suite);
|
|
302
|
+
}
|
|
303
|
+
reset() {
|
|
304
|
+
this.registries.clear();
|
|
305
|
+
}
|
|
94
306
|
deleteCachedItem(id) {
|
|
95
307
|
const mockId = this.getMockPath(id);
|
|
96
308
|
if (this.moduleCache.has(mockId)) {
|
|
@@ -109,15 +321,6 @@ class VitestMocker {
|
|
|
109
321
|
Object.assign(error, { codeFrame });
|
|
110
322
|
return error;
|
|
111
323
|
}
|
|
112
|
-
getMocks() {
|
|
113
|
-
const suite = this.getSuiteFilepath();
|
|
114
|
-
const suiteMocks = this.mockMap.get(suite);
|
|
115
|
-
const globalMocks = this.mockMap.get("global");
|
|
116
|
-
return {
|
|
117
|
-
...globalMocks,
|
|
118
|
-
...suiteMocks
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
324
|
async resolvePath(rawId, importer) {
|
|
122
325
|
let id;
|
|
123
326
|
let fsPath;
|
|
@@ -132,7 +335,7 @@ class VitestMocker {
|
|
|
132
335
|
throw error;
|
|
133
336
|
}
|
|
134
337
|
}
|
|
135
|
-
const external = !isAbsolute(fsPath) || this.isModuleDirectory(fsPath) ? rawId : null;
|
|
338
|
+
const external = !isAbsolute$1(fsPath) || this.isModuleDirectory(fsPath) ? rawId : null;
|
|
136
339
|
return {
|
|
137
340
|
id,
|
|
138
341
|
fsPath,
|
|
@@ -149,11 +352,17 @@ class VitestMocker {
|
|
|
149
352
|
mock.id,
|
|
150
353
|
mock.importer
|
|
151
354
|
);
|
|
152
|
-
if (mock.
|
|
355
|
+
if (mock.action === "unmock") {
|
|
153
356
|
this.unmockPath(fsPath);
|
|
154
357
|
}
|
|
155
|
-
if (mock.
|
|
156
|
-
this.mockPath(
|
|
358
|
+
if (mock.action === "mock") {
|
|
359
|
+
this.mockPath(
|
|
360
|
+
mock.id,
|
|
361
|
+
fsPath,
|
|
362
|
+
external,
|
|
363
|
+
mock.type,
|
|
364
|
+
mock.factory
|
|
365
|
+
);
|
|
157
366
|
}
|
|
158
367
|
})
|
|
159
368
|
);
|
|
@@ -164,23 +373,7 @@ class VitestMocker {
|
|
|
164
373
|
if (cached) {
|
|
165
374
|
return cached;
|
|
166
375
|
}
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
exports = await mock();
|
|
170
|
-
} catch (err) {
|
|
171
|
-
const vitestError = this.createError(
|
|
172
|
-
'[vitest] There was an error when mocking a module. If you are using "vi.mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file. Read more: https://vitest.dev/api/vi.html#vi-mock'
|
|
173
|
-
);
|
|
174
|
-
vitestError.cause = err;
|
|
175
|
-
throw vitestError;
|
|
176
|
-
}
|
|
177
|
-
const filepath = dep.slice(5);
|
|
178
|
-
const mockpath = this.resolveCache.get(this.getSuiteFilepath())?.[filepath] || filepath;
|
|
179
|
-
if (exports === null || typeof exports !== "object") {
|
|
180
|
-
throw this.createError(
|
|
181
|
-
`[vitest] vi.mock("${mockpath}", factory?: () => unknown) is not returning an object. Did you mean to return an object with a "default" key?`
|
|
182
|
-
);
|
|
183
|
-
}
|
|
376
|
+
const exports = await mock.resolve();
|
|
184
377
|
const moduleExports = new Proxy(exports, {
|
|
185
378
|
get: (target, prop) => {
|
|
186
379
|
const val = target[prop];
|
|
@@ -193,12 +386,10 @@ class VitestMocker {
|
|
|
193
386
|
return void 0;
|
|
194
387
|
}
|
|
195
388
|
throw this.createError(
|
|
196
|
-
`[vitest] No "${String(
|
|
197
|
-
prop
|
|
198
|
-
)}" export is defined on the "${mockpath}" mock. Did you forget to return it from "vi.mock"?
|
|
389
|
+
`[vitest] No "${String(prop)}" export is defined on the "${mock.raw}" mock. Did you forget to return it from "vi.mock"?
|
|
199
390
|
If you need to partially mock a module, you can use "importOriginal" helper inside:
|
|
200
391
|
`,
|
|
201
|
-
highlight(`vi.mock("${
|
|
392
|
+
highlight(`vi.mock(import("${mock.raw}"), async (importOriginal) => {
|
|
202
393
|
const actual = await importOriginal()
|
|
203
394
|
return {
|
|
204
395
|
...actual,
|
|
@@ -213,172 +404,58 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
213
404
|
this.moduleCache.set(dep, { exports: moduleExports });
|
|
214
405
|
return moduleExports;
|
|
215
406
|
}
|
|
407
|
+
// public method to avoid circular dependency
|
|
216
408
|
getMockContext() {
|
|
217
409
|
return this.mockContext;
|
|
218
410
|
}
|
|
411
|
+
// path used to store mocked dependencies
|
|
219
412
|
getMockPath(dep) {
|
|
220
413
|
return `mock:${dep}`;
|
|
221
414
|
}
|
|
222
415
|
getDependencyMock(id) {
|
|
223
|
-
|
|
416
|
+
const registry = this.getMockerRegistry();
|
|
417
|
+
return registry.get(id);
|
|
224
418
|
}
|
|
225
419
|
normalizePath(path) {
|
|
226
420
|
return this.moduleCache.normalizePath(path);
|
|
227
421
|
}
|
|
228
422
|
resolveMockPath(mockPath, external) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (fs.statSync(path2).isFile()) {
|
|
238
|
-
return path2;
|
|
239
|
-
} else {
|
|
240
|
-
const indexFile = findFile2(path2, "index");
|
|
241
|
-
if (indexFile) {
|
|
242
|
-
return indexFile;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return null;
|
|
248
|
-
};
|
|
249
|
-
const mockDirname = dirname(path);
|
|
250
|
-
const mockFolder = join(this.root, "__mocks__", mockDirname);
|
|
251
|
-
if (!existsSync(mockFolder)) {
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
const baseOriginal = basename(path);
|
|
255
|
-
return findFile2(mockFolder, baseOriginal);
|
|
256
|
-
}
|
|
257
|
-
const dir = dirname(path);
|
|
258
|
-
const baseId = basename(path);
|
|
259
|
-
const fullPath = resolve(dir, "__mocks__", baseId);
|
|
260
|
-
return existsSync(fullPath) ? fullPath : null;
|
|
261
|
-
}
|
|
262
|
-
mockObject(object, mockExports = {}) {
|
|
263
|
-
const finalizers = new Array();
|
|
264
|
-
const refs = new RefTracker();
|
|
265
|
-
const define = (container, key, value) => {
|
|
266
|
-
try {
|
|
267
|
-
container[key] = value;
|
|
268
|
-
return true;
|
|
269
|
-
} catch {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
const mockPropertiesOf = (container, newContainer) => {
|
|
274
|
-
const containerType = getType(container);
|
|
275
|
-
const isModule = containerType === "Module" || !!container.__esModule;
|
|
276
|
-
for (const { key: property, descriptor } of getAllMockableProperties(
|
|
277
|
-
container,
|
|
278
|
-
isModule,
|
|
279
|
-
this.primitives
|
|
280
|
-
)) {
|
|
281
|
-
if (!isModule && descriptor.get) {
|
|
282
|
-
try {
|
|
283
|
-
Object.defineProperty(newContainer, property, descriptor);
|
|
284
|
-
} catch {
|
|
285
|
-
}
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
if (isSpecialProp(property, containerType)) {
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
const value = container[property];
|
|
292
|
-
const refId = refs.getId(value);
|
|
293
|
-
if (refId !== void 0) {
|
|
294
|
-
finalizers.push(
|
|
295
|
-
() => define(newContainer, property, refs.getMockedValue(refId))
|
|
296
|
-
);
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
const type = getType(value);
|
|
300
|
-
if (Array.isArray(value)) {
|
|
301
|
-
define(newContainer, property, []);
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
const isFunction = type.includes("Function") && typeof value === "function";
|
|
305
|
-
if ((!isFunction || value.__isMockFunction) && type !== "Object" && type !== "Module") {
|
|
306
|
-
define(newContainer, property, value);
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
if (!define(newContainer, property, isFunction ? value : {})) {
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
if (isFunction) {
|
|
313
|
-
let mockFunction2 = function() {
|
|
314
|
-
if (this instanceof newContainer[property]) {
|
|
315
|
-
for (const { key, descriptor: descriptor2 } of getAllMockableProperties(
|
|
316
|
-
this,
|
|
317
|
-
false,
|
|
318
|
-
primitives
|
|
319
|
-
)) {
|
|
320
|
-
if (descriptor2.get) {
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
const value2 = this[key];
|
|
324
|
-
const type2 = getType(value2);
|
|
325
|
-
const isFunction2 = type2.includes("Function") && typeof value2 === "function";
|
|
326
|
-
if (isFunction2) {
|
|
327
|
-
const original = this[key];
|
|
328
|
-
const mock2 = spyModule.spyOn(this, key).mockImplementation(original);
|
|
329
|
-
mock2.mockRestore = () => {
|
|
330
|
-
mock2.mockReset();
|
|
331
|
-
mock2.mockImplementation(original);
|
|
332
|
-
return mock2;
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
if (!this.spyModule) {
|
|
339
|
-
throw this.createError(
|
|
340
|
-
"[vitest] `spyModule` is not defined. This is Vitest error. Please open a new issue with reproduction."
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
const spyModule = this.spyModule;
|
|
344
|
-
const primitives = this.primitives;
|
|
345
|
-
const mock = spyModule.spyOn(newContainer, property).mockImplementation(mockFunction2);
|
|
346
|
-
mock.mockRestore = () => {
|
|
347
|
-
mock.mockReset();
|
|
348
|
-
mock.mockImplementation(mockFunction2);
|
|
349
|
-
return mock;
|
|
350
|
-
};
|
|
351
|
-
Object.defineProperty(newContainer[property], "length", { value: 0 });
|
|
352
|
-
}
|
|
353
|
-
refs.track(value, newContainer[property]);
|
|
354
|
-
mockPropertiesOf(value, newContainer[property]);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
const mockedObject = mockExports;
|
|
358
|
-
mockPropertiesOf(object, mockedObject);
|
|
359
|
-
for (const finalizer of finalizers) {
|
|
360
|
-
finalizer();
|
|
423
|
+
return findMockRedirect(this.root, mockPath, external);
|
|
424
|
+
}
|
|
425
|
+
mockObject(object, mockExports = {}, behavior = "automock") {
|
|
426
|
+
const spyOn = this.spyModule?.spyOn;
|
|
427
|
+
if (!spyOn) {
|
|
428
|
+
throw this.createError(
|
|
429
|
+
"[vitest] `spyModule` is not defined. This is a Vitest error. Please open a new issue with reproduction."
|
|
430
|
+
);
|
|
361
431
|
}
|
|
362
|
-
return
|
|
432
|
+
return mockObject({
|
|
433
|
+
globalConstructors: this.primitives,
|
|
434
|
+
spyOn,
|
|
435
|
+
type: behavior
|
|
436
|
+
}, object, mockExports);
|
|
363
437
|
}
|
|
364
438
|
unmockPath(path) {
|
|
365
|
-
const
|
|
439
|
+
const registry = this.getMockerRegistry();
|
|
366
440
|
const id = this.normalizePath(path);
|
|
367
|
-
|
|
368
|
-
if (mock && id in mock) {
|
|
369
|
-
delete mock[id];
|
|
370
|
-
}
|
|
441
|
+
registry.delete(id);
|
|
371
442
|
this.deleteCachedItem(id);
|
|
372
443
|
}
|
|
373
|
-
mockPath(originalId, path, external, factory) {
|
|
444
|
+
mockPath(originalId, path, external, mockType, factory) {
|
|
445
|
+
const registry = this.getMockerRegistry();
|
|
374
446
|
const id = this.normalizePath(path);
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
447
|
+
if (mockType === "manual") {
|
|
448
|
+
registry.register("manual", originalId, id, factory);
|
|
449
|
+
} else if (mockType === "autospy") {
|
|
450
|
+
registry.register("autospy", originalId, id);
|
|
451
|
+
} else {
|
|
452
|
+
const redirect = this.resolveMockPath(id, external);
|
|
453
|
+
if (redirect) {
|
|
454
|
+
registry.register("redirect", originalId, id, redirect);
|
|
455
|
+
} else {
|
|
456
|
+
registry.register("automock", originalId, id);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
382
459
|
this.deleteCachedItem(id);
|
|
383
460
|
}
|
|
384
461
|
async importActual(rawId, importer, callstack) {
|
|
@@ -394,23 +471,31 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
394
471
|
const { id, fsPath, external } = await this.resolvePath(rawId, importee);
|
|
395
472
|
const normalizedId = this.normalizePath(fsPath);
|
|
396
473
|
let mock = this.getDependencyMock(normalizedId);
|
|
397
|
-
if (mock
|
|
398
|
-
|
|
474
|
+
if (!mock) {
|
|
475
|
+
const redirect = this.resolveMockPath(normalizedId, external);
|
|
476
|
+
if (redirect) {
|
|
477
|
+
mock = new RedirectedModule(rawId, normalizedId, redirect);
|
|
478
|
+
} else {
|
|
479
|
+
mock = new AutomockedModule(rawId, normalizedId);
|
|
480
|
+
}
|
|
399
481
|
}
|
|
400
|
-
if (mock ===
|
|
482
|
+
if (mock.type === "automock" || mock.type === "autospy") {
|
|
401
483
|
const mod = await this.executor.cachedRequest(id, fsPath, [importee]);
|
|
402
|
-
return this.mockObject(mod);
|
|
484
|
+
return this.mockObject(mod, {}, mock.type);
|
|
403
485
|
}
|
|
404
|
-
if (
|
|
486
|
+
if (mock.type === "manual") {
|
|
405
487
|
return this.callFunctionMock(fsPath, mock);
|
|
406
488
|
}
|
|
407
|
-
return this.executor.dependencyRequest(mock, mock, [importee]);
|
|
489
|
+
return this.executor.dependencyRequest(mock.redirect, mock.redirect, [importee]);
|
|
408
490
|
}
|
|
409
491
|
async requestWithMock(url, callstack) {
|
|
410
492
|
const id = this.normalizePath(url);
|
|
411
493
|
const mock = this.getDependencyMock(id);
|
|
494
|
+
if (!mock) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
412
497
|
const mockPath = this.getMockPath(id);
|
|
413
|
-
if (mock ===
|
|
498
|
+
if (mock.type === "automock" || mock.type === "autospy") {
|
|
414
499
|
const cache = this.moduleCache.get(mockPath);
|
|
415
500
|
if (cache.exports) {
|
|
416
501
|
return cache.exports;
|
|
@@ -418,10 +503,10 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
418
503
|
const exports = {};
|
|
419
504
|
this.moduleCache.set(mockPath, { exports });
|
|
420
505
|
const mod = await this.executor.directRequest(url, url, callstack);
|
|
421
|
-
this.mockObject(mod, exports);
|
|
506
|
+
this.mockObject(mod, exports, mock.type);
|
|
422
507
|
return exports;
|
|
423
508
|
}
|
|
424
|
-
if (
|
|
509
|
+
if (mock.type === "manual" && !callstack.includes(mockPath) && !callstack.includes(url)) {
|
|
425
510
|
try {
|
|
426
511
|
callstack.push(mockPath);
|
|
427
512
|
this.mockContext.callstack = callstack;
|
|
@@ -431,29 +516,37 @@ If you need to partially mock a module, you can use "importOriginal" helper insi
|
|
|
431
516
|
const indexMock = callstack.indexOf(mockPath);
|
|
432
517
|
callstack.splice(indexMock, 1);
|
|
433
518
|
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
return mock;
|
|
519
|
+
} else if (mock.type === "redirect" && !callstack.includes(mock.redirect)) {
|
|
520
|
+
return mock.redirect;
|
|
437
521
|
}
|
|
438
522
|
}
|
|
439
|
-
queueMock(id, importer,
|
|
523
|
+
queueMock(id, importer, factoryOrOptions) {
|
|
524
|
+
const mockType = getMockType(factoryOrOptions);
|
|
440
525
|
VitestMocker.pendingIds.push({
|
|
441
|
-
|
|
526
|
+
action: "mock",
|
|
442
527
|
id,
|
|
443
528
|
importer,
|
|
444
|
-
factory,
|
|
445
|
-
|
|
529
|
+
factory: typeof factoryOrOptions === "function" ? factoryOrOptions : void 0,
|
|
530
|
+
type: mockType
|
|
446
531
|
});
|
|
447
532
|
}
|
|
448
|
-
queueUnmock(id, importer
|
|
533
|
+
queueUnmock(id, importer) {
|
|
449
534
|
VitestMocker.pendingIds.push({
|
|
450
|
-
|
|
535
|
+
action: "unmock",
|
|
451
536
|
id,
|
|
452
|
-
importer
|
|
453
|
-
throwIfCached
|
|
537
|
+
importer
|
|
454
538
|
});
|
|
455
539
|
}
|
|
456
540
|
}
|
|
541
|
+
function getMockType(factoryOrOptions) {
|
|
542
|
+
if (!factoryOrOptions) {
|
|
543
|
+
return "automock";
|
|
544
|
+
}
|
|
545
|
+
if (typeof factoryOrOptions === "function") {
|
|
546
|
+
return "manual";
|
|
547
|
+
}
|
|
548
|
+
return factoryOrOptions.spy ? "autospy" : "automock";
|
|
549
|
+
}
|
|
457
550
|
|
|
458
551
|
const { readFileSync } = fs;
|
|
459
552
|
async function createVitestExecutor(options) {
|
|
@@ -536,9 +629,6 @@ async function startVitestExecutor(options) {
|
|
|
536
629
|
get moduleCache() {
|
|
537
630
|
return state().moduleCache;
|
|
538
631
|
},
|
|
539
|
-
get mockMap() {
|
|
540
|
-
return state().mockMap;
|
|
541
|
-
},
|
|
542
632
|
get interopDefault() {
|
|
543
633
|
return state().config.deps.interopDefault;
|
|
544
634
|
},
|
|
@@ -642,7 +732,7 @@ class VitestExecutor extends ViteNodeRunner {
|
|
|
642
732
|
return false;
|
|
643
733
|
}
|
|
644
734
|
const transformMode = this.state.environment?.transformMode ?? "ssr";
|
|
645
|
-
return transformMode === "ssr" ? !isNodeBuiltin(id) : !id.startsWith("node:");
|
|
735
|
+
return transformMode === "ssr" ? !isNodeBuiltin$1(id) : !id.startsWith("node:");
|
|
646
736
|
}
|
|
647
737
|
async originalResolveUrl(id, importer) {
|
|
648
738
|
return super.resolveUrl(id, importer);
|
|
@@ -707,7 +797,7 @@ class VitestExecutor extends ViteNodeRunner {
|
|
|
707
797
|
return super.dependencyRequest(id, fsPath, callstack);
|
|
708
798
|
}
|
|
709
799
|
prepareContext(context) {
|
|
710
|
-
if (this.state.filepath && normalize(this.state.filepath) === normalize(context.__filename)) {
|
|
800
|
+
if (this.state.filepath && normalize$1(this.state.filepath) === normalize$1(context.__filename)) {
|
|
711
801
|
const globalNamespace = this.options.context || globalThis;
|
|
712
802
|
Object.defineProperty(context.__vite_ssr_import_meta__, "vitest", {
|
|
713
803
|
// @ts-expect-error injected untyped global
|