@vitest/browser 2.0.0-beta.2 → 2.0.0-beta.5
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/context.d.ts +91 -0
- package/dist/client/.vite/manifest.json +24 -0
- package/dist/client/__vitest__/assets/index-B74JATHR.css +1 -0
- package/dist/client/__vitest__/assets/index-BoV0brgU.js +51 -0
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/orchestrator-CEB54dI4.js +192 -0
- package/dist/client/__vitest_browser__/{rpc-By4jD8av.js → rpc-DBukiZYG.js} +217 -387
- package/dist/client/__vitest_browser__/{tester-CrKhlp5g.js → tester-C3Mchfpf.js} +246 -33
- package/dist/client/esm-client-injector.js +12 -28
- package/dist/client/{index.html → orchestrator.html} +8 -7
- package/dist/client/tester.html +4 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +363 -226
- package/dist/providers.js +25 -25
- package/package.json +13 -7
- package/dist/client/__vitest__/assets/index-D0Wp7rbG.css +0 -1
- package/dist/client/__vitest__/assets/index-D1SnFeL5.js +0 -51
- package/dist/client/__vitest_browser__/main-DmAU-Uff.js +0 -102
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { readFile as readFile$1 } from 'node:fs/promises';
|
|
3
3
|
import sirv from 'sirv';
|
|
4
4
|
import { coverageConfigDefaults } from 'vitest/config';
|
|
5
5
|
import { slash } from '@vitest/utils';
|
|
6
|
+
import fs, { promises } from 'node:fs';
|
|
7
|
+
import { resolve as resolve$1, dirname } from 'node:path';
|
|
8
|
+
import { isFileServingAllowed } from 'vitest/node';
|
|
6
9
|
import MagicString from 'magic-string';
|
|
7
10
|
import { esmWalker } from '@vitest/utils/ast';
|
|
8
11
|
|
|
@@ -156,241 +159,347 @@ const basename = function(p, extension) {
|
|
|
156
159
|
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
|
|
157
160
|
};
|
|
158
161
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
*/
|
|
163
|
-
function extract_names(param) {
|
|
164
|
-
return extract_identifiers(param).map((node) => node.name);
|
|
162
|
+
function assertFileAccess(path, project) {
|
|
163
|
+
if (!isFileServingAllowed(path, project.server) && !isFileServingAllowed(path, project.ctx.server))
|
|
164
|
+
throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
|
|
165
165
|
}
|
|
166
|
+
const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
|
|
167
|
+
const filepath = resolve$1(dirname(testPath), path);
|
|
168
|
+
assertFileAccess(filepath, project);
|
|
169
|
+
if (typeof options === "object" && !options.encoding)
|
|
170
|
+
options.encoding = "utf-8";
|
|
171
|
+
return promises.readFile(filepath, options);
|
|
172
|
+
};
|
|
173
|
+
const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
|
|
174
|
+
const filepath = resolve$1(dirname(testPath), path);
|
|
175
|
+
assertFileAccess(filepath, project);
|
|
176
|
+
const dir = dirname(filepath);
|
|
177
|
+
if (!fs.existsSync(dir))
|
|
178
|
+
await promises.mkdir(dir, { recursive: true });
|
|
179
|
+
await promises.writeFile(filepath, data, options);
|
|
180
|
+
};
|
|
181
|
+
const removeFile = async ({ project, testPath = process.cwd() }, path) => {
|
|
182
|
+
const filepath = resolve$1(dirname(testPath), path);
|
|
183
|
+
assertFileAccess(filepath, project);
|
|
184
|
+
await promises.rm(filepath);
|
|
185
|
+
};
|
|
166
186
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
* @param {import('estree').Identifier[]} nodes
|
|
170
|
-
* @returns {import('estree').Identifier[]}
|
|
171
|
-
*/
|
|
172
|
-
function extract_identifiers(param, nodes = []) {
|
|
173
|
-
switch (param.type) {
|
|
174
|
-
case 'Identifier':
|
|
175
|
-
nodes.push(param);
|
|
176
|
-
break;
|
|
177
|
-
|
|
178
|
-
case 'MemberExpression':
|
|
179
|
-
let object = param;
|
|
180
|
-
while (object.type === 'MemberExpression') {
|
|
181
|
-
object = /** @type {any} */ (object.object);
|
|
182
|
-
}
|
|
183
|
-
nodes.push(/** @type {any} */ (object));
|
|
184
|
-
break;
|
|
185
|
-
|
|
186
|
-
case 'ObjectPattern':
|
|
187
|
-
for (const prop of param.properties) {
|
|
188
|
-
if (prop.type === 'RestElement') {
|
|
189
|
-
extract_identifiers(prop.argument, nodes);
|
|
190
|
-
} else {
|
|
191
|
-
extract_identifiers(prop.value, nodes);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
break;
|
|
196
|
-
|
|
197
|
-
case 'ArrayPattern':
|
|
198
|
-
for (const element of param.elements) {
|
|
199
|
-
if (element) extract_identifiers(element, nodes);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
break;
|
|
203
|
-
|
|
204
|
-
case 'RestElement':
|
|
205
|
-
extract_identifiers(param.argument, nodes);
|
|
206
|
-
break;
|
|
207
|
-
|
|
208
|
-
case 'AssignmentPattern':
|
|
209
|
-
extract_identifiers(param.left, nodes);
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return nodes;
|
|
187
|
+
function isObject(payload) {
|
|
188
|
+
return payload != null && typeof payload === "object";
|
|
214
189
|
}
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
ast = parse(code);
|
|
230
|
-
} catch (err) {
|
|
231
|
-
console.error(`Cannot parse ${id}:
|
|
232
|
-
${err.message}`);
|
|
233
|
-
return;
|
|
190
|
+
function isSendKeysPayload(payload) {
|
|
191
|
+
const validOptions = ["type", "press", "down", "up"];
|
|
192
|
+
if (!isObject(payload))
|
|
193
|
+
throw new Error("You must provide a `SendKeysPayload` object");
|
|
194
|
+
const numberOfValidOptions = Object.keys(payload).filter(
|
|
195
|
+
(key) => validOptions.includes(key)
|
|
196
|
+
).length;
|
|
197
|
+
const unknownOptions = Object.keys(payload).filter((key) => !validOptions.includes(key));
|
|
198
|
+
if (numberOfValidOptions > 1) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`You must provide ONLY one of the following properties to pass to the browser runner: ${validOptions.join(
|
|
201
|
+
", "
|
|
202
|
+
)}.`
|
|
203
|
+
);
|
|
234
204
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if (skipHijack.some((skip) => source.match(skip)))
|
|
242
|
-
return null;
|
|
243
|
-
const importId = `__vi_esm_${uid++}__`;
|
|
244
|
-
const hasSpecifiers = node.specifiers.length > 0;
|
|
245
|
-
const code2 = hasSpecifiers ? `import { ${viInjectedKey} as ${importId} } from '${source}'
|
|
246
|
-
` : `import '${source}'
|
|
247
|
-
`;
|
|
248
|
-
return {
|
|
249
|
-
code: code2,
|
|
250
|
-
id: importId
|
|
251
|
-
};
|
|
252
|
-
};
|
|
253
|
-
function defineImport(node) {
|
|
254
|
-
const declaration = transformImportDeclaration(node);
|
|
255
|
-
if (!declaration)
|
|
256
|
-
return null;
|
|
257
|
-
s.appendLeft(hoistIndex, declaration.code);
|
|
258
|
-
return declaration.id;
|
|
205
|
+
if (numberOfValidOptions === 0) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`You must provide one of the following properties to pass to the browser runner: ${validOptions.join(
|
|
208
|
+
", "
|
|
209
|
+
)}.`
|
|
210
|
+
);
|
|
259
211
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
212
|
+
if (unknownOptions.length > 0)
|
|
213
|
+
throw new Error(`Unknown options \`${unknownOptions.join(", ")}\` present.`);
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
function isTypePayload(payload) {
|
|
217
|
+
return "type" in payload;
|
|
218
|
+
}
|
|
219
|
+
function isPressPayload(payload) {
|
|
220
|
+
return "press" in payload;
|
|
221
|
+
}
|
|
222
|
+
function isDownPayload(payload) {
|
|
223
|
+
return "down" in payload;
|
|
224
|
+
}
|
|
225
|
+
function isUpPayload(payload) {
|
|
226
|
+
return "up" in payload;
|
|
227
|
+
}
|
|
228
|
+
const sendKeys = async ({ provider }, payload) => {
|
|
229
|
+
if (!isSendKeysPayload(payload) || !payload)
|
|
230
|
+
throw new Error("You must provide a `SendKeysPayload` object");
|
|
231
|
+
if (provider.name === "playwright") {
|
|
232
|
+
const page = provider.page;
|
|
233
|
+
if (isTypePayload(payload))
|
|
234
|
+
await page.keyboard.type(payload.type);
|
|
235
|
+
else if (isPressPayload(payload))
|
|
236
|
+
await page.keyboard.press(payload.press);
|
|
237
|
+
else if (isDownPayload(payload))
|
|
238
|
+
await page.keyboard.down(payload.down);
|
|
239
|
+
else if (isUpPayload(payload))
|
|
240
|
+
await page.keyboard.up(payload.up);
|
|
241
|
+
} else if (provider.name === "webdriverio") {
|
|
242
|
+
const browser = provider.browser;
|
|
243
|
+
if (isTypePayload(payload))
|
|
244
|
+
await browser.keys(payload.type.split(""));
|
|
245
|
+
else if (isPressPayload(payload))
|
|
246
|
+
await browser.keys([payload.press]);
|
|
247
|
+
else
|
|
248
|
+
throw new Error('Only "press" and "type" are supported by webdriverio.');
|
|
249
|
+
} else {
|
|
250
|
+
throw new Error(`"sendKeys" is not supported for ${provider.name} browser provider.`);
|
|
265
251
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
var builtinCommands = {
|
|
255
|
+
readFile,
|
|
256
|
+
removeFile,
|
|
257
|
+
writeFile,
|
|
258
|
+
sendKeys
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
|
|
262
|
+
const ID_CONTEXT = "@vitest/browser/context";
|
|
263
|
+
function BrowserContext(project) {
|
|
264
|
+
project.config.browser.commands ??= {};
|
|
265
|
+
for (const [name, command] of Object.entries(builtinCommands))
|
|
266
|
+
project.config.browser.commands[name] ??= command;
|
|
267
|
+
for (const command in project.config.browser.commands) {
|
|
268
|
+
if (!/^[a-z_$][\w$]*$/i.test(command))
|
|
269
|
+
throw new Error(`Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`);
|
|
272
270
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
`${importId}.${spec.imported.name}`
|
|
284
|
-
);
|
|
285
|
-
} else if (spec.type === "ImportDefaultSpecifier") {
|
|
286
|
-
idToImportMap.set(spec.local.name, `${importId}.default`);
|
|
287
|
-
} else {
|
|
288
|
-
idToImportMap.set(spec.local.name, importId);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
271
|
+
return {
|
|
272
|
+
name: "vitest:browser:virtual-module:context",
|
|
273
|
+
enforce: "pre",
|
|
274
|
+
resolveId(id) {
|
|
275
|
+
if (id === ID_CONTEXT)
|
|
276
|
+
return VIRTUAL_ID_CONTEXT;
|
|
277
|
+
},
|
|
278
|
+
load(id) {
|
|
279
|
+
if (id === VIRTUAL_ID_CONTEXT)
|
|
280
|
+
return generateContextFile(project);
|
|
291
281
|
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function generateContextFile(project) {
|
|
285
|
+
const commands = Object.keys(project.config.browser.commands ?? {});
|
|
286
|
+
const filepathCode = "__vitest_worker__.filepath || __vitest_worker__.current?.file?.filepath || undefined";
|
|
287
|
+
const commandsCode = commands.map((command) => {
|
|
288
|
+
return ` ["${command}"]: (...args) => rpc().triggerCommand("${command}", ${filepathCode}, args),`;
|
|
289
|
+
}).join("\n");
|
|
290
|
+
return `
|
|
291
|
+
const rpc = () => __vitest_worker__.rpc
|
|
292
|
+
const channel = new BroadcastChannel('vitest')
|
|
293
|
+
|
|
294
|
+
export const server = {
|
|
295
|
+
platform: ${JSON.stringify(process.platform)},
|
|
296
|
+
version: ${JSON.stringify(process.version)},
|
|
297
|
+
provider: ${JSON.stringify(project.browserProvider.name)},
|
|
298
|
+
browser: ${JSON.stringify(project.config.browser.name)},
|
|
299
|
+
commands: {
|
|
300
|
+
${commandsCode}
|
|
292
301
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
}
|
|
303
|
+
export const commands = server.commands
|
|
304
|
+
export const page = {
|
|
305
|
+
get config() {
|
|
306
|
+
return __vitest_browser_runner__.config
|
|
307
|
+
},
|
|
308
|
+
viewport(width, height) {
|
|
309
|
+
const id = __vitest_browser_runner__.iframeId
|
|
310
|
+
channel.postMessage({ type: 'viewport', width, height, id })
|
|
311
|
+
return new Promise((resolve) => {
|
|
312
|
+
channel.addEventListener('message', function handler(e) {
|
|
313
|
+
if (e.data.type === 'viewport:done' && e.data.id === id) {
|
|
314
|
+
channel.removeEventListener('message', handler)
|
|
315
|
+
resolve()
|
|
304
316
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (node.source) {
|
|
309
|
-
const importId = defineImportAll(node.source.value);
|
|
310
|
-
for (const spec of node.specifiers) {
|
|
311
|
-
defineExport(
|
|
312
|
-
hoistIndex,
|
|
313
|
-
spec.exported.name,
|
|
314
|
-
`${importId}.${spec.local.name}`
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
} else {
|
|
318
|
-
for (const spec of node.specifiers) {
|
|
319
|
-
const local = spec.local.name;
|
|
320
|
-
const binding = idToImportMap.get(local);
|
|
321
|
-
defineExport(node.end, spec.exported.name, binding || local);
|
|
322
|
-
}
|
|
317
|
+
if (e.data.type === 'viewport:fail' && e.data.id === id) {
|
|
318
|
+
channel.removeEventListener('message', handler)
|
|
319
|
+
reject(new Error(e.data.error))
|
|
323
320
|
}
|
|
324
|
-
}
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function automockModule(code, parse) {
|
|
329
|
+
const ast = parse(code);
|
|
330
|
+
const m = new MagicString(code);
|
|
331
|
+
const allSpecifiers = [];
|
|
332
|
+
let importIndex = 0;
|
|
333
|
+
for (const _node of ast.body) {
|
|
334
|
+
if (_node.type === "ExportAllDeclaration") {
|
|
335
|
+
throw new Error(
|
|
336
|
+
`automocking files with \`export *\` is not supported in browser mode because it cannot be statically analysed`
|
|
337
|
+
);
|
|
325
338
|
}
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
339
|
+
if (_node.type === "ExportNamedDeclaration") {
|
|
340
|
+
let traversePattern2 = function(expression) {
|
|
341
|
+
if (expression.type === "Identifier") {
|
|
342
|
+
allSpecifiers.push({ name: expression.name });
|
|
343
|
+
} else if (expression.type === "ArrayPattern") {
|
|
344
|
+
expression.elements.forEach((element) => {
|
|
345
|
+
if (!element)
|
|
346
|
+
return;
|
|
347
|
+
traversePattern2(element);
|
|
348
|
+
});
|
|
349
|
+
} else if (expression.type === "ObjectPattern") {
|
|
350
|
+
expression.properties.forEach((property) => {
|
|
351
|
+
if (property.type === "RestElement")
|
|
352
|
+
traversePattern2(property);
|
|
353
|
+
else if (property.type === "Property")
|
|
354
|
+
traversePattern2(property.value);
|
|
355
|
+
else
|
|
356
|
+
;
|
|
357
|
+
});
|
|
358
|
+
} else if (expression.type === "RestElement") {
|
|
359
|
+
traversePattern2(expression.argument);
|
|
360
|
+
} else if (expression.type === "AssignmentPattern") {
|
|
361
|
+
throw new Error(`AssignmentPattern is not supported. Please open a new bug report.`);
|
|
362
|
+
} else if (expression.type === "MemberExpression") {
|
|
363
|
+
throw new Error(`MemberExpression is not supported. Please open a new bug report.`);
|
|
364
|
+
} else ;
|
|
365
|
+
};
|
|
366
|
+
const node = _node;
|
|
367
|
+
const declaration = node.declaration;
|
|
368
|
+
if (declaration) {
|
|
369
|
+
if (declaration.type === "FunctionDeclaration") {
|
|
370
|
+
allSpecifiers.push({ name: declaration.id.name });
|
|
371
|
+
} else if (declaration.type === "VariableDeclaration") {
|
|
372
|
+
declaration.declarations.forEach((declaration2) => {
|
|
373
|
+
traversePattern2(declaration2.id);
|
|
374
|
+
});
|
|
375
|
+
} else if (declaration.type === "ClassDeclaration") {
|
|
376
|
+
allSpecifiers.push({ name: declaration.id.name });
|
|
377
|
+
} else ;
|
|
378
|
+
m.remove(node.start, declaration.start);
|
|
379
|
+
}
|
|
380
|
+
const specifiers = node.specifiers || [];
|
|
381
|
+
const source = node.source;
|
|
382
|
+
if (!source && specifiers.length) {
|
|
383
|
+
specifiers.forEach((specifier) => {
|
|
384
|
+
const exported = specifier.exported;
|
|
385
|
+
allSpecifiers.push({
|
|
386
|
+
alias: exported.type === "Literal" ? exported.raw : exported.name,
|
|
387
|
+
name: specifier.local.name
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
m.remove(node.start, node.end);
|
|
391
|
+
} else if (source && specifiers.length) {
|
|
392
|
+
const importNames = [];
|
|
393
|
+
specifiers.forEach((specifier) => {
|
|
394
|
+
const importedName = `__vitest_imported_${importIndex++}__`;
|
|
395
|
+
const exported = specifier.exported;
|
|
396
|
+
importNames.push([specifier.local.name, importedName]);
|
|
397
|
+
allSpecifiers.push({
|
|
398
|
+
name: importedName,
|
|
399
|
+
alias: exported.type === "Literal" ? exported.raw : exported.name
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
const importString = `import { ${importNames.map(([name, alias]) => `${name} as ${alias}`).join(", ")} } from '${source.value}'`;
|
|
403
|
+
m.overwrite(node.start, node.end, importString);
|
|
348
404
|
}
|
|
349
405
|
}
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
else
|
|
356
|
-
s.appendLeft(hoistIndex, `${viExportAllHelper}(${viInjectedKey}, ${importId});
|
|
357
|
-
`);
|
|
406
|
+
if (_node.type === "ExportDefaultDeclaration") {
|
|
407
|
+
const node = _node;
|
|
408
|
+
const declaration = node.declaration;
|
|
409
|
+
allSpecifiers.push({ name: "__vitest_default", alias: "default" });
|
|
410
|
+
m.overwrite(node.start, declaration.start, `const __vitest_default = `);
|
|
358
411
|
}
|
|
359
412
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
413
|
+
const moduleObject = `
|
|
414
|
+
const __vitest_es_current_module__ = {
|
|
415
|
+
__esModule: true,
|
|
416
|
+
${allSpecifiers.map(({ name }) => `["${name}"]: ${name},`).join("\n ")}
|
|
417
|
+
}
|
|
418
|
+
const __vitest_mocked_module__ = __vitest_mocker__.mockObject(__vitest_es_current_module__)
|
|
419
|
+
`;
|
|
420
|
+
const assigning = allSpecifiers.map(({ name }, index) => {
|
|
421
|
+
return `const __vitest_mocked_${index}__ = __vitest_mocked_module__["${name}"]`;
|
|
422
|
+
}).join("\n");
|
|
423
|
+
const redeclarations = allSpecifiers.map(({ name, alias }, index) => {
|
|
424
|
+
return ` __vitest_mocked_${index}__ as ${alias || name},`;
|
|
425
|
+
}).join("\n");
|
|
426
|
+
const specifiersExports = `
|
|
427
|
+
export {
|
|
428
|
+
${redeclarations}
|
|
429
|
+
}
|
|
430
|
+
`;
|
|
431
|
+
m.append(moduleObject + assigning + specifiersExports);
|
|
432
|
+
return m;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
var BrowserMocker = (project) => {
|
|
436
|
+
return [
|
|
437
|
+
{
|
|
438
|
+
name: "vitest:browser:mocker",
|
|
439
|
+
enforce: "pre",
|
|
440
|
+
async load(id) {
|
|
441
|
+
const data = project.browserMocker.mocks.get(id);
|
|
442
|
+
if (!data)
|
|
443
|
+
return;
|
|
444
|
+
const { mock, sessionId } = data;
|
|
445
|
+
if (mock === void 0) {
|
|
446
|
+
const rpc = project.browserRpc.testers.get(sessionId);
|
|
447
|
+
if (!rpc)
|
|
448
|
+
throw new Error(`WebSocket rpc was destroyed for session ${sessionId}`);
|
|
449
|
+
const exports = await rpc.startMocking(id);
|
|
450
|
+
const module = `const module = __vitest_mocker__.get('${id}');`;
|
|
451
|
+
const keys = exports.map((name) => {
|
|
452
|
+
if (name === "default")
|
|
453
|
+
return `export default module['default'];`;
|
|
454
|
+
return `export const ${name} = module['${name}'];`;
|
|
455
|
+
}).join("\n");
|
|
456
|
+
return `${module}
|
|
457
|
+
${keys}`;
|
|
373
458
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
) {
|
|
378
|
-
s.update(id2.start, id2.end, binding);
|
|
459
|
+
if (mock === null)
|
|
460
|
+
return;
|
|
461
|
+
return readFile$1(mock, "utf-8");
|
|
379
462
|
}
|
|
380
463
|
},
|
|
464
|
+
{
|
|
465
|
+
name: "vitest:browser:automocker",
|
|
466
|
+
enforce: "post",
|
|
467
|
+
transform(code, id) {
|
|
468
|
+
const data = project.browserMocker.mocks.get(id);
|
|
469
|
+
if (!data)
|
|
470
|
+
return;
|
|
471
|
+
if (data.mock === null) {
|
|
472
|
+
const m = automockModule(code, this.parse);
|
|
473
|
+
return {
|
|
474
|
+
code: m.toString(),
|
|
475
|
+
map: m.generateMap({ hires: "boundary", source: id })
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
];
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
function injectDynamicImport(code, id, parse) {
|
|
484
|
+
const s = new MagicString(code);
|
|
485
|
+
let ast;
|
|
486
|
+
try {
|
|
487
|
+
ast = parse(code);
|
|
488
|
+
} catch (err) {
|
|
489
|
+
console.error(`Cannot parse ${id}:
|
|
490
|
+
${err.message}`);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
esmWalker(ast, {
|
|
381
494
|
// TODO: make env updatable
|
|
382
495
|
onImportMeta() {
|
|
383
496
|
},
|
|
384
497
|
onDynamicImport(node) {
|
|
385
|
-
const replace = "__vitest_browser_runner__.wrapModule(import(";
|
|
498
|
+
const replace = "__vitest_browser_runner__.wrapModule(() => import(";
|
|
386
499
|
s.overwrite(node.start, node.source.start, replace);
|
|
387
500
|
s.overwrite(node.end - 1, node.end, "))");
|
|
388
501
|
}
|
|
389
502
|
});
|
|
390
|
-
s.prepend(`const ${viInjectedKey} = { [Symbol.toStringTag]: "Module" };
|
|
391
|
-
`);
|
|
392
|
-
s.append(`
|
|
393
|
-
export { ${viInjectedKey} }`);
|
|
394
503
|
return {
|
|
395
504
|
ast,
|
|
396
505
|
code: s.toString(),
|
|
@@ -398,10 +507,24 @@ export { ${viInjectedKey} }`);
|
|
|
398
507
|
};
|
|
399
508
|
}
|
|
400
509
|
|
|
510
|
+
const regexDynamicImport = /import\s*\(/;
|
|
511
|
+
var DynamicImport = () => {
|
|
512
|
+
return {
|
|
513
|
+
name: "vitest:browser:esm-injector",
|
|
514
|
+
enforce: "post",
|
|
515
|
+
transform(source, id) {
|
|
516
|
+
if (!regexDynamicImport.test(source))
|
|
517
|
+
return;
|
|
518
|
+
return injectDynamicImport(source, id, this.parse);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
};
|
|
522
|
+
|
|
401
523
|
var index = (project, base = "/") => {
|
|
402
524
|
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
403
525
|
const distRoot = resolve(pkgRoot, "dist");
|
|
404
526
|
return [
|
|
527
|
+
...BrowserMocker(project),
|
|
405
528
|
{
|
|
406
529
|
enforce: "pre",
|
|
407
530
|
name: "vitest:browser",
|
|
@@ -413,9 +536,12 @@ var index = (project, base = "/") => {
|
|
|
413
536
|
}
|
|
414
537
|
},
|
|
415
538
|
async configureServer(server) {
|
|
416
|
-
const testerHtml = readFile(resolve(distRoot, "client/tester.html"), "utf8");
|
|
417
|
-
const
|
|
418
|
-
const injectorJs = readFile(resolve(distRoot, "client/esm-client-injector.js"), "utf8");
|
|
539
|
+
const testerHtml = readFile$1(resolve(distRoot, "client/tester.html"), "utf8");
|
|
540
|
+
const orchestratorHtml = project.config.browser.ui ? readFile$1(resolve(distRoot, "client/__vitest__/index.html"), "utf8") : readFile$1(resolve(distRoot, "client/orchestrator.html"), "utf8");
|
|
541
|
+
const injectorJs = readFile$1(resolve(distRoot, "client/esm-client-injector.js"), "utf8");
|
|
542
|
+
const manifest = (async () => {
|
|
543
|
+
return JSON.parse(await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8"));
|
|
544
|
+
})();
|
|
419
545
|
const favicon = `${base}favicon.svg`;
|
|
420
546
|
const testerPrefix = `${base}__vitest_test__/__test__/`;
|
|
421
547
|
server.middlewares.use((_req, res, next) => {
|
|
@@ -442,12 +568,26 @@ var index = (project, base = "/") => {
|
|
|
442
568
|
config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
|
|
443
569
|
const injector = replacer(await injectorJs, {
|
|
444
570
|
__VITEST_CONFIG__: JSON.stringify(config),
|
|
445
|
-
__VITEST_FILES__: JSON.stringify(files)
|
|
571
|
+
__VITEST_FILES__: JSON.stringify(files),
|
|
572
|
+
__VITEST_TYPE__: url.pathname === base ? '"orchestrator"' : '"tester"'
|
|
446
573
|
});
|
|
447
574
|
if (url.pathname === base) {
|
|
448
575
|
if (!indexScripts)
|
|
449
576
|
indexScripts = await formatScripts(project.config.browser.indexScripts, server);
|
|
450
|
-
|
|
577
|
+
let baseHtml = await orchestratorHtml;
|
|
578
|
+
if (project.config.browser.ui) {
|
|
579
|
+
const manifestContent = await manifest;
|
|
580
|
+
const jsEntry = manifestContent["orchestrator.html"].file;
|
|
581
|
+
baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
|
|
582
|
+
"<!-- !LOAD_METADATA! -->",
|
|
583
|
+
[
|
|
584
|
+
"<script>{__VITEST_INJECTOR__}<\/script>",
|
|
585
|
+
"{__VITEST_SCRIPTS__}",
|
|
586
|
+
`<script type="module" crossorigin src="${jsEntry}"><\/script>`
|
|
587
|
+
].join("\n")
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
const html2 = replacer(baseHtml, {
|
|
451
591
|
__VITEST_FAVICON__: favicon,
|
|
452
592
|
__VITEST_TITLE__: "Vitest Browser Runner",
|
|
453
593
|
__VITEST_SCRIPTS__: indexScripts,
|
|
@@ -459,6 +599,7 @@ var index = (project, base = "/") => {
|
|
|
459
599
|
}
|
|
460
600
|
const decodedTestFile = decodeURIComponent(url.pathname.slice(testerPrefix.length));
|
|
461
601
|
const tests = decodedTestFile === "__vitest_all__" || !files.includes(decodedTestFile) ? "__vitest_browser_runner__.files" : JSON.stringify([decodedTestFile]);
|
|
602
|
+
const iframeId = decodedTestFile === "__vitest_all__" ? '"__vitest_all__"' : JSON.stringify(decodedTestFile);
|
|
462
603
|
if (!testerScripts)
|
|
463
604
|
testerScripts = await formatScripts(project.config.browser.testerScripts, server);
|
|
464
605
|
const html = replacer(await testerHtml, {
|
|
@@ -470,6 +611,7 @@ var index = (project, base = "/") => {
|
|
|
470
611
|
// TODO: have only a single global variable to not pollute the global scope
|
|
471
612
|
`<script type="module">
|
|
472
613
|
__vitest_browser_runner__.runningFiles = ${tests}
|
|
614
|
+
__vitest_browser_runner__.iframeId = ${iframeId}
|
|
473
615
|
__vitest_browser_runner__.runTests(__vitest_browser_runner__.runningFiles)
|
|
474
616
|
<\/script>`
|
|
475
617
|
)
|
|
@@ -526,6 +668,9 @@ var index = (project, base = "/") => {
|
|
|
526
668
|
"vitest/browser",
|
|
527
669
|
"vitest/runners",
|
|
528
670
|
"@vitest/utils",
|
|
671
|
+
"std-env",
|
|
672
|
+
"tinybench",
|
|
673
|
+
"tinyspy",
|
|
529
674
|
// loupe is manually transformed
|
|
530
675
|
"loupe"
|
|
531
676
|
],
|
|
@@ -562,16 +707,8 @@ export default globalThis.loupe`;
|
|
|
562
707
|
return useId;
|
|
563
708
|
}
|
|
564
709
|
},
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
enforce: "post",
|
|
568
|
-
transform(source, id) {
|
|
569
|
-
const hijackESM = project.config.browser.slowHijackESM ?? false;
|
|
570
|
-
if (!hijackESM)
|
|
571
|
-
return;
|
|
572
|
-
return injectVitestModule(source, id, this.parse);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
710
|
+
BrowserContext(project),
|
|
711
|
+
DynamicImport()
|
|
575
712
|
];
|
|
576
713
|
};
|
|
577
714
|
function resolveCoverageFolder(project) {
|
|
@@ -584,7 +721,7 @@ function resolveCoverageFolder(project) {
|
|
|
584
721
|
if (!htmlReporter)
|
|
585
722
|
return void 0;
|
|
586
723
|
const root = resolve(
|
|
587
|
-
options.root ||
|
|
724
|
+
options.root || process.cwd(),
|
|
588
725
|
options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
|
|
589
726
|
);
|
|
590
727
|
const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
|
|
@@ -600,7 +737,7 @@ function wrapConfig(config) {
|
|
|
600
737
|
};
|
|
601
738
|
}
|
|
602
739
|
function replacer(code, values) {
|
|
603
|
-
return code.replace(
|
|
740
|
+
return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? "");
|
|
604
741
|
}
|
|
605
742
|
async function formatScripts(scripts, server) {
|
|
606
743
|
if (!scripts?.length)
|