@vitest/browser 3.1.0 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/.vite/manifest.json +6 -6
- package/dist/client/__vitest__/assets/index-Cv3XDLXs.js +52 -0
- package/dist/client/__vitest__/assets/{index-B0KEk_KY.css → index-D6BhetW8.css} +1 -1
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/orchestrator-CuTjqoE1.js +287 -0
- package/dist/client/__vitest_browser__/{tester-DiLSqOx4.js → tester-D8qCxA_3.js} +3172 -3084
- package/dist/client/__vitest_browser__/{utils-CNTxSNQV.js → utils-Owv5OOOf.js} +2 -2
- package/dist/client/error-catcher.js +3 -3
- package/dist/client/esm-client-injector.js +0 -1
- package/dist/client/orchestrator.html +2 -2
- package/dist/client/tester/tester.html +2 -2
- package/dist/client.js +51 -22
- package/dist/context.js +4 -4
- package/dist/expect-element.js +1 -1
- package/dist/index-C34csx3Z.js +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.js +342 -242
- package/dist/locators/index.js +1 -1
- package/dist/locators/playwright.js +1 -1
- package/dist/locators/preview.js +1 -1
- package/dist/locators/webdriverio.js +1 -1
- package/dist/providers.js +1 -1
- package/dist/state.js +2 -67
- package/dist/{webdriver-2iYWIzBv.js → webdriver-BzjiKqT8.js} +0 -4
- package/package.json +14 -14
- package/dist/client/__vitest__/assets/index-BLZJq7cG.js +0 -52
- package/dist/client/__vitest_browser__/orchestrator-CqPXjvQE.js +0 -241
- package/dist/index-DjDyxzt8.js +0 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import c from 'tinyrainbow';
|
|
|
4
4
|
import { getFilePoolName, distDir, resolveApiServerConfig, resolveFsAllow, isFileServingAllowed, createDebugger, isValidApiRequest, createViteLogger, createViteServer } from 'vitest/node';
|
|
5
5
|
import fs, { readFileSync, lstatSync, promises, existsSync } from 'node:fs';
|
|
6
6
|
import { createRequire } from 'node:module';
|
|
7
|
-
import { slash as slash$1, toArray } from '@vitest/utils';
|
|
7
|
+
import { slash as slash$1, toArray, createDefer } from '@vitest/utils';
|
|
8
8
|
import MagicString from 'magic-string';
|
|
9
9
|
import sirv from 'sirv';
|
|
10
10
|
import * as vite from 'vite';
|
|
@@ -13,12 +13,12 @@ import { fileURLToPath } from 'node:url';
|
|
|
13
13
|
import crypto from 'node:crypto';
|
|
14
14
|
import { mkdir, readFile as readFile$1 } from 'node:fs/promises';
|
|
15
15
|
import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
|
|
16
|
-
import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-
|
|
16
|
+
import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-BzjiKqT8.js';
|
|
17
17
|
import { resolve as resolve$1, dirname as dirname$1, basename as basename$1, normalize as normalize$1 } from 'node:path';
|
|
18
18
|
import { WebSocketServer } from 'ws';
|
|
19
19
|
import * as nodeos from 'node:os';
|
|
20
20
|
|
|
21
|
-
var version = "3.1.
|
|
21
|
+
var version = "3.1.2";
|
|
22
22
|
|
|
23
23
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
24
24
|
function normalizeWindowsPath(input = "") {
|
|
@@ -213,6 +213,113 @@ const basename = function(p, extension) {
|
|
|
213
213
|
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
214
214
|
const distRoot = resolve(pkgRoot, "dist");
|
|
215
215
|
|
|
216
|
+
/// <reference types="../types/index.d.ts" />
|
|
217
|
+
|
|
218
|
+
// (c) 2020-present Andrea Giammarchi
|
|
219
|
+
|
|
220
|
+
const {parse: $parse, stringify: $stringify} = JSON;
|
|
221
|
+
const {keys} = Object;
|
|
222
|
+
|
|
223
|
+
const Primitive = String; // it could be Number
|
|
224
|
+
const primitive = 'string'; // it could be 'number'
|
|
225
|
+
|
|
226
|
+
const ignore = {};
|
|
227
|
+
const object = 'object';
|
|
228
|
+
|
|
229
|
+
const noop = (_, value) => value;
|
|
230
|
+
|
|
231
|
+
const primitives = value => (
|
|
232
|
+
value instanceof Primitive ? Primitive(value) : value
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const Primitives = (_, value) => (
|
|
236
|
+
typeof value === primitive ? new Primitive(value) : value
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const revive = (input, parsed, output, $) => {
|
|
240
|
+
const lazy = [];
|
|
241
|
+
for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
|
|
242
|
+
const k = ke[y];
|
|
243
|
+
const value = output[k];
|
|
244
|
+
if (value instanceof Primitive) {
|
|
245
|
+
const tmp = input[value];
|
|
246
|
+
if (typeof tmp === object && !parsed.has(tmp)) {
|
|
247
|
+
parsed.add(tmp);
|
|
248
|
+
output[k] = ignore;
|
|
249
|
+
lazy.push({k, a: [input, parsed, tmp, $]});
|
|
250
|
+
}
|
|
251
|
+
else
|
|
252
|
+
output[k] = $.call(output, k, tmp);
|
|
253
|
+
}
|
|
254
|
+
else if (output[k] !== ignore)
|
|
255
|
+
output[k] = $.call(output, k, value);
|
|
256
|
+
}
|
|
257
|
+
for (let {length} = lazy, i = 0; i < length; i++) {
|
|
258
|
+
const {k, a} = lazy[i];
|
|
259
|
+
output[k] = $.call(output, k, revive.apply(null, a));
|
|
260
|
+
}
|
|
261
|
+
return output;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const set = (known, input, value) => {
|
|
265
|
+
const index = Primitive(input.push(value) - 1);
|
|
266
|
+
known.set(value, index);
|
|
267
|
+
return index;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Converts a specialized flatted string into a JS value.
|
|
272
|
+
* @param {string} text
|
|
273
|
+
* @param {(this: any, key: string, value: any) => any} [reviver]
|
|
274
|
+
* @returns {any}
|
|
275
|
+
*/
|
|
276
|
+
const parse = (text, reviver) => {
|
|
277
|
+
const input = $parse(text, Primitives).map(primitives);
|
|
278
|
+
const value = input[0];
|
|
279
|
+
const $ = reviver || noop;
|
|
280
|
+
const tmp = typeof value === object && value ?
|
|
281
|
+
revive(input, new Set, value, $) :
|
|
282
|
+
value;
|
|
283
|
+
return $.call({'': tmp}, '', tmp);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Converts a JS value into a specialized flatted string.
|
|
288
|
+
* @param {any} value
|
|
289
|
+
* @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
|
|
290
|
+
* @param {string | number | undefined} [space]
|
|
291
|
+
* @returns {string}
|
|
292
|
+
*/
|
|
293
|
+
const stringify = (value, replacer, space) => {
|
|
294
|
+
const $ = replacer && typeof replacer === object ?
|
|
295
|
+
(k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
|
|
296
|
+
(replacer || noop);
|
|
297
|
+
const known = new Map;
|
|
298
|
+
const input = [];
|
|
299
|
+
const output = [];
|
|
300
|
+
let i = +set(known, input, $.call({'': value}, '', value));
|
|
301
|
+
let firstRun = !i;
|
|
302
|
+
while (i < input.length) {
|
|
303
|
+
firstRun = true;
|
|
304
|
+
output[i] = $stringify(input[i++], replace, space);
|
|
305
|
+
}
|
|
306
|
+
return '[' + output.join(',') + ']';
|
|
307
|
+
function replace(key, value) {
|
|
308
|
+
if (firstRun) {
|
|
309
|
+
firstRun = !firstRun;
|
|
310
|
+
return value;
|
|
311
|
+
}
|
|
312
|
+
const after = $.call(this, key, value);
|
|
313
|
+
switch (typeof after) {
|
|
314
|
+
case object:
|
|
315
|
+
if (after === null) return after;
|
|
316
|
+
case primitive:
|
|
317
|
+
return known.get(after) || set(known, input, after);
|
|
318
|
+
}
|
|
319
|
+
return after;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
216
323
|
function replacer(code, values) {
|
|
217
324
|
return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
|
|
218
325
|
}
|
|
@@ -249,7 +356,6 @@ async function resolveOrchestrator(globalServer, url, res) {
|
|
|
249
356
|
sessionId = contexts[contexts.length - 1] ?? "none";
|
|
250
357
|
}
|
|
251
358
|
const session = globalServer.vitest._browserSessions.getSession(sessionId);
|
|
252
|
-
const files = session?.files ?? [];
|
|
253
359
|
const browserProject = session?.project.browser || [...globalServer.children][0];
|
|
254
360
|
if (!browserProject) {
|
|
255
361
|
return;
|
|
@@ -259,12 +365,11 @@ async function resolveOrchestrator(globalServer, url, res) {
|
|
|
259
365
|
__VITEST_PROVIDER__: JSON.stringify(browserProject.config.browser.provider || "preview"),
|
|
260
366
|
__VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
|
|
261
367
|
__VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root }),
|
|
262
|
-
__VITEST_METHOD__: JSON.stringify(
|
|
263
|
-
__VITEST_FILES__: JSON.stringify(files),
|
|
368
|
+
__VITEST_METHOD__: JSON.stringify("orchestrate"),
|
|
264
369
|
__VITEST_TYPE__: "\"orchestrator\"",
|
|
265
370
|
__VITEST_SESSION_ID__: JSON.stringify(sessionId),
|
|
266
371
|
__VITEST_TESTER_ID__: "\"none\"",
|
|
267
|
-
__VITEST_PROVIDED_CONTEXT__:
|
|
372
|
+
__VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(browserProject.project.getProvidedContext())),
|
|
268
373
|
__VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
|
|
269
374
|
});
|
|
270
375
|
res.removeHeader("Content-Security-Policy");
|
|
@@ -327,119 +432,12 @@ function createOrchestratorMiddleware(parentServer) {
|
|
|
327
432
|
};
|
|
328
433
|
}
|
|
329
434
|
|
|
330
|
-
/// <reference types="../types/index.d.ts" />
|
|
331
|
-
|
|
332
|
-
// (c) 2020-present Andrea Giammarchi
|
|
333
|
-
|
|
334
|
-
const {parse: $parse, stringify: $stringify} = JSON;
|
|
335
|
-
const {keys} = Object;
|
|
336
|
-
|
|
337
|
-
const Primitive = String; // it could be Number
|
|
338
|
-
const primitive = 'string'; // it could be 'number'
|
|
339
|
-
|
|
340
|
-
const ignore = {};
|
|
341
|
-
const object = 'object';
|
|
342
|
-
|
|
343
|
-
const noop = (_, value) => value;
|
|
344
|
-
|
|
345
|
-
const primitives = value => (
|
|
346
|
-
value instanceof Primitive ? Primitive(value) : value
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
const Primitives = (_, value) => (
|
|
350
|
-
typeof value === primitive ? new Primitive(value) : value
|
|
351
|
-
);
|
|
352
|
-
|
|
353
|
-
const revive = (input, parsed, output, $) => {
|
|
354
|
-
const lazy = [];
|
|
355
|
-
for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
|
|
356
|
-
const k = ke[y];
|
|
357
|
-
const value = output[k];
|
|
358
|
-
if (value instanceof Primitive) {
|
|
359
|
-
const tmp = input[value];
|
|
360
|
-
if (typeof tmp === object && !parsed.has(tmp)) {
|
|
361
|
-
parsed.add(tmp);
|
|
362
|
-
output[k] = ignore;
|
|
363
|
-
lazy.push({k, a: [input, parsed, tmp, $]});
|
|
364
|
-
}
|
|
365
|
-
else
|
|
366
|
-
output[k] = $.call(output, k, tmp);
|
|
367
|
-
}
|
|
368
|
-
else if (output[k] !== ignore)
|
|
369
|
-
output[k] = $.call(output, k, value);
|
|
370
|
-
}
|
|
371
|
-
for (let {length} = lazy, i = 0; i < length; i++) {
|
|
372
|
-
const {k, a} = lazy[i];
|
|
373
|
-
output[k] = $.call(output, k, revive.apply(null, a));
|
|
374
|
-
}
|
|
375
|
-
return output;
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
const set = (known, input, value) => {
|
|
379
|
-
const index = Primitive(input.push(value) - 1);
|
|
380
|
-
known.set(value, index);
|
|
381
|
-
return index;
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Converts a specialized flatted string into a JS value.
|
|
386
|
-
* @param {string} text
|
|
387
|
-
* @param {(this: any, key: string, value: any) => any} [reviver]
|
|
388
|
-
* @returns {any}
|
|
389
|
-
*/
|
|
390
|
-
const parse = (text, reviver) => {
|
|
391
|
-
const input = $parse(text, Primitives).map(primitives);
|
|
392
|
-
const value = input[0];
|
|
393
|
-
const $ = reviver || noop;
|
|
394
|
-
const tmp = typeof value === object && value ?
|
|
395
|
-
revive(input, new Set, value, $) :
|
|
396
|
-
value;
|
|
397
|
-
return $.call({'': tmp}, '', tmp);
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Converts a JS value into a specialized flatted string.
|
|
402
|
-
* @param {any} value
|
|
403
|
-
* @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
|
|
404
|
-
* @param {string | number | undefined} [space]
|
|
405
|
-
* @returns {string}
|
|
406
|
-
*/
|
|
407
|
-
const stringify = (value, replacer, space) => {
|
|
408
|
-
const $ = replacer && typeof replacer === object ?
|
|
409
|
-
(k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
|
|
410
|
-
(replacer || noop);
|
|
411
|
-
const known = new Map;
|
|
412
|
-
const input = [];
|
|
413
|
-
const output = [];
|
|
414
|
-
let i = +set(known, input, $.call({'': value}, '', value));
|
|
415
|
-
let firstRun = !i;
|
|
416
|
-
while (i < input.length) {
|
|
417
|
-
firstRun = true;
|
|
418
|
-
output[i] = $stringify(input[i++], replace, space);
|
|
419
|
-
}
|
|
420
|
-
return '[' + output.join(',') + ']';
|
|
421
|
-
function replace(key, value) {
|
|
422
|
-
if (firstRun) {
|
|
423
|
-
firstRun = !firstRun;
|
|
424
|
-
return value;
|
|
425
|
-
}
|
|
426
|
-
const after = $.call(this, key, value);
|
|
427
|
-
switch (typeof after) {
|
|
428
|
-
case object:
|
|
429
|
-
if (after === null) return after;
|
|
430
|
-
case primitive:
|
|
431
|
-
return known.get(after) || set(known, input, after);
|
|
432
|
-
}
|
|
433
|
-
return after;
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
|
-
|
|
437
435
|
async function resolveTester(globalServer, url, res, next) {
|
|
438
436
|
const csp = res.getHeader("Content-Security-Policy");
|
|
439
437
|
if (typeof csp === "string") {
|
|
440
438
|
res.setHeader("Content-Security-Policy", csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *"));
|
|
441
439
|
}
|
|
442
|
-
const
|
|
440
|
+
const sessionId = url.searchParams.get("sessionId") || "none";
|
|
443
441
|
const session = globalServer.vitest._browserSessions.getSession(sessionId);
|
|
444
442
|
if (!session) {
|
|
445
443
|
res.statusCode = 400;
|
|
@@ -447,11 +445,6 @@ async function resolveTester(globalServer, url, res, next) {
|
|
|
447
445
|
return;
|
|
448
446
|
}
|
|
449
447
|
const project = globalServer.vitest.getProjectByName(session.project.name || "");
|
|
450
|
-
const { testFiles } = await project.globTestFiles();
|
|
451
|
-
const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
|
|
452
|
-
const iframeId = JSON.stringify(testFile);
|
|
453
|
-
const files = session.files ?? [];
|
|
454
|
-
const method = session.method ?? "run";
|
|
455
448
|
const browserProject = project.browser || [...globalServer.children][0];
|
|
456
449
|
if (!browserProject) {
|
|
457
450
|
res.statusCode = 400;
|
|
@@ -462,13 +455,12 @@ async function resolveTester(globalServer, url, res, next) {
|
|
|
462
455
|
const injector = replacer(injectorJs, {
|
|
463
456
|
__VITEST_PROVIDER__: JSON.stringify(project.browser.provider.name),
|
|
464
457
|
__VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
|
|
465
|
-
__VITEST_FILES__: JSON.stringify(files),
|
|
466
458
|
__VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root }),
|
|
467
459
|
__VITEST_TYPE__: "\"tester\"",
|
|
468
|
-
__VITEST_METHOD__: JSON.stringify(
|
|
460
|
+
__VITEST_METHOD__: JSON.stringify("none"),
|
|
469
461
|
__VITEST_SESSION_ID__: JSON.stringify(sessionId),
|
|
470
462
|
__VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
|
|
471
|
-
__VITEST_PROVIDED_CONTEXT__:
|
|
463
|
+
__VITEST_PROVIDED_CONTEXT__: "{}",
|
|
472
464
|
__VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
|
|
473
465
|
});
|
|
474
466
|
const testerHtml = typeof browserProject.testerHtml === "string" ? browserProject.testerHtml : await browserProject.testerHtml;
|
|
@@ -477,17 +469,11 @@ async function resolveTester(globalServer, url, res, next) {
|
|
|
477
469
|
const indexhtml = await browserProject.vite.transformIndexHtml(url, testerHtml);
|
|
478
470
|
const html = replacer(indexhtml, {
|
|
479
471
|
__VITEST_FAVICON__: globalServer.faviconUrl,
|
|
480
|
-
__VITEST_INJECTOR__: injector
|
|
481
|
-
__VITEST_APPEND__: `
|
|
482
|
-
__vitest_browser_runner__.runningFiles = ${tests}
|
|
483
|
-
__vitest_browser_runner__.iframeId = ${iframeId}
|
|
484
|
-
__vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
|
|
485
|
-
document.querySelector('script[data-vitest-append]').remove()
|
|
486
|
-
`
|
|
472
|
+
__VITEST_INJECTOR__: injector
|
|
487
473
|
});
|
|
488
474
|
return html;
|
|
489
475
|
} catch (err) {
|
|
490
|
-
session.
|
|
476
|
+
session.fail(err);
|
|
491
477
|
next(err);
|
|
492
478
|
}
|
|
493
479
|
}
|
|
@@ -498,7 +484,7 @@ function createTesterMiddleware(browserServer) {
|
|
|
498
484
|
return next();
|
|
499
485
|
}
|
|
500
486
|
const url = new URL(req.url, "http://localhost");
|
|
501
|
-
if (!url.pathname.startsWith(browserServer.prefixTesterUrl)) {
|
|
487
|
+
if (!url.pathname.startsWith(browserServer.prefixTesterUrl) || !url.searchParams.has("sessionId")) {
|
|
502
488
|
return next();
|
|
503
489
|
}
|
|
504
490
|
const html = await resolveTester(browserServer, url, res, next);
|
|
@@ -961,16 +947,7 @@ body {
|
|
|
961
947
|
injectTo: "head"
|
|
962
948
|
} : null,
|
|
963
949
|
...parentServer.testerScripts,
|
|
964
|
-
...testerTags
|
|
965
|
-
{
|
|
966
|
-
tag: "script",
|
|
967
|
-
attrs: {
|
|
968
|
-
"type": "module",
|
|
969
|
-
"data-vitest-append": ""
|
|
970
|
-
},
|
|
971
|
-
children: "{__VITEST_APPEND__}",
|
|
972
|
-
injectTo: "body"
|
|
973
|
-
}
|
|
950
|
+
...testerTags
|
|
974
951
|
].filter((s) => s != null);
|
|
975
952
|
}
|
|
976
953
|
},
|
|
@@ -1216,6 +1193,7 @@ const types = {
|
|
|
1216
1193
|
'application/dash+xml': ['mpd'],
|
|
1217
1194
|
'application/dash-patch+xml': ['mpp'],
|
|
1218
1195
|
'application/davmount+xml': ['davmount'],
|
|
1196
|
+
'application/dicom': ['dcm'],
|
|
1219
1197
|
'application/docbook+xml': ['dbk'],
|
|
1220
1198
|
'application/dssc+der': ['dssc'],
|
|
1221
1199
|
'application/dssc+xml': ['xdssc'],
|
|
@@ -1302,7 +1280,14 @@ const types = {
|
|
|
1302
1280
|
'application/oebps-package+xml': ['opf'],
|
|
1303
1281
|
'application/ogg': ['ogx'],
|
|
1304
1282
|
'application/omdoc+xml': ['omdoc'],
|
|
1305
|
-
'application/onenote': [
|
|
1283
|
+
'application/onenote': [
|
|
1284
|
+
'onetoc',
|
|
1285
|
+
'onetoc2',
|
|
1286
|
+
'onetmp',
|
|
1287
|
+
'onepkg',
|
|
1288
|
+
'one',
|
|
1289
|
+
'onea',
|
|
1290
|
+
],
|
|
1306
1291
|
'application/oxps': ['oxps'],
|
|
1307
1292
|
'application/p2p-overlay+xml': ['relo'],
|
|
1308
1293
|
'application/patch-ops-error+xml': ['xer'],
|
|
@@ -1398,6 +1383,7 @@ const types = {
|
|
|
1398
1383
|
'application/yang': ['yang'],
|
|
1399
1384
|
'application/yin+xml': ['yin'],
|
|
1400
1385
|
'application/zip': ['zip'],
|
|
1386
|
+
'application/zip+dotlottie': ['lottie'],
|
|
1401
1387
|
'audio/3gpp': ['*3gpp'],
|
|
1402
1388
|
'audio/aac': ['adts', 'aac'],
|
|
1403
1389
|
'audio/adpcm': ['adp'],
|
|
@@ -1406,7 +1392,7 @@ const types = {
|
|
|
1406
1392
|
'audio/midi': ['mid', 'midi', 'kar', 'rmi'],
|
|
1407
1393
|
'audio/mobile-xmf': ['mxmf'],
|
|
1408
1394
|
'audio/mp3': ['*mp3'],
|
|
1409
|
-
'audio/mp4': ['m4a', 'mp4a'],
|
|
1395
|
+
'audio/mp4': ['m4a', 'mp4a', 'm4b'],
|
|
1410
1396
|
'audio/mpeg': ['mpga', 'mp2', 'mp2a', 'mp3', 'm2a', 'm3a'],
|
|
1411
1397
|
'audio/ogg': ['oga', 'ogg', 'spx', 'opus'],
|
|
1412
1398
|
'audio/s3m': ['s3m'],
|
|
@@ -1438,11 +1424,12 @@ const types = {
|
|
|
1438
1424
|
'image/heif': ['heif'],
|
|
1439
1425
|
'image/heif-sequence': ['heifs'],
|
|
1440
1426
|
'image/hej2k': ['hej2'],
|
|
1441
|
-
'image/hsj2': ['hsj2'],
|
|
1442
1427
|
'image/ief': ['ief'],
|
|
1428
|
+
'image/jaii': ['jaii'],
|
|
1429
|
+
'image/jais': ['jais'],
|
|
1443
1430
|
'image/jls': ['jls'],
|
|
1444
1431
|
'image/jp2': ['jp2', 'jpg2'],
|
|
1445
|
-
'image/jpeg': ['
|
|
1432
|
+
'image/jpeg': ['jpg', 'jpeg', 'jpe'],
|
|
1446
1433
|
'image/jph': ['jph'],
|
|
1447
1434
|
'image/jphc': ['jhc'],
|
|
1448
1435
|
'image/jpm': ['jpm', 'jpgm'],
|
|
@@ -1457,6 +1444,7 @@ const types = {
|
|
|
1457
1444
|
'image/jxss': ['jxss'],
|
|
1458
1445
|
'image/ktx': ['ktx'],
|
|
1459
1446
|
'image/ktx2': ['ktx2'],
|
|
1447
|
+
'image/pjpeg': ['jfif'],
|
|
1460
1448
|
'image/png': ['png'],
|
|
1461
1449
|
'image/sgi': ['sgi'],
|
|
1462
1450
|
'image/svg+xml': ['svg', 'svgz'],
|
|
@@ -1470,7 +1458,7 @@ const types = {
|
|
|
1470
1458
|
'message/global-delivery-status': ['u8dsn'],
|
|
1471
1459
|
'message/global-disposition-notification': ['u8mdn'],
|
|
1472
1460
|
'message/global-headers': ['u8hdr'],
|
|
1473
|
-
'message/rfc822': ['eml', 'mime'],
|
|
1461
|
+
'message/rfc822': ['eml', 'mime', 'mht', 'mhtml'],
|
|
1474
1462
|
'model/3mf': ['3mf'],
|
|
1475
1463
|
'model/gltf+json': ['gltf'],
|
|
1476
1464
|
'model/gltf-binary': ['glb'],
|
|
@@ -1480,6 +1468,7 @@ const types = {
|
|
|
1480
1468
|
'model/mtl': ['mtl'],
|
|
1481
1469
|
'model/obj': ['obj'],
|
|
1482
1470
|
'model/prc': ['prc'],
|
|
1471
|
+
'model/step': ['step', 'stp', 'stpnc', 'p21', '210'],
|
|
1483
1472
|
'model/step+xml': ['stpx'],
|
|
1484
1473
|
'model/step+zip': ['stpz'],
|
|
1485
1474
|
'model/step-xml+zip': ['stpxz'],
|
|
@@ -1586,8 +1575,8 @@ class Mime {
|
|
|
1586
1575
|
getType(path) {
|
|
1587
1576
|
if (typeof path !== 'string')
|
|
1588
1577
|
return null;
|
|
1589
|
-
const last = path.replace(/^.*[/\\]
|
|
1590
|
-
const ext = last.replace(
|
|
1578
|
+
const last = path.replace(/^.*[/\\]/s, '').toLowerCase();
|
|
1579
|
+
const ext = last.replace(/^.*\./s, '').toLowerCase();
|
|
1591
1580
|
const hasPath = last.length < path.length;
|
|
1592
1581
|
const hasDot = ext.length < last.length - 1;
|
|
1593
1582
|
if (!hasDot && hasPath)
|
|
@@ -2718,14 +2707,13 @@ class ParentBrowserProject {
|
|
|
2718
2707
|
if (!provider.getCDPSession) {
|
|
2719
2708
|
throw new Error(`CDP is not supported by the provider "${provider.name}".`);
|
|
2720
2709
|
}
|
|
2721
|
-
const
|
|
2710
|
+
const session = await this.cdpSessionsPromises.get(rpcId) ?? await (async () => {
|
|
2722
2711
|
const promise = provider.getCDPSession(sessionId).finally(() => {
|
|
2723
2712
|
this.cdpSessionsPromises.delete(rpcId);
|
|
2724
2713
|
});
|
|
2725
2714
|
this.cdpSessionsPromises.set(rpcId, promise);
|
|
2726
2715
|
return promise;
|
|
2727
2716
|
})();
|
|
2728
|
-
const session = await promise;
|
|
2729
2717
|
const rpc = browser.state.testers.get(rpcId);
|
|
2730
2718
|
if (!rpc) {
|
|
2731
2719
|
throw new Error(`Tester RPC "${rpcId}" was not established.`);
|
|
@@ -2781,6 +2769,8 @@ class ParentBrowserProject {
|
|
|
2781
2769
|
}
|
|
2782
2770
|
}
|
|
2783
2771
|
|
|
2772
|
+
const TYPE_REQUEST = "q";
|
|
2773
|
+
const TYPE_RESPONSE = "s";
|
|
2784
2774
|
const DEFAULT_TIMEOUT = 6e4;
|
|
2785
2775
|
function defaultSerialize(i) {
|
|
2786
2776
|
return i;
|
|
@@ -2813,7 +2803,7 @@ function createBirpc(functions, options) {
|
|
|
2813
2803
|
if (method === "then" && !eventNames.includes("then") && !("then" in functions))
|
|
2814
2804
|
return void 0;
|
|
2815
2805
|
const sendEvent = (...args) => {
|
|
2816
|
-
post(serialize({ m: method, a: args, t:
|
|
2806
|
+
post(serialize({ m: method, a: args, t: TYPE_REQUEST }));
|
|
2817
2807
|
};
|
|
2818
2808
|
if (eventNames.includes(method)) {
|
|
2819
2809
|
sendEvent.asEvent = sendEvent;
|
|
@@ -2835,8 +2825,9 @@ function createBirpc(functions, options) {
|
|
|
2835
2825
|
if (timeout >= 0) {
|
|
2836
2826
|
timeoutId = setTimeout(() => {
|
|
2837
2827
|
try {
|
|
2838
|
-
options.onTimeoutError?.(method, args);
|
|
2839
|
-
|
|
2828
|
+
const handleResult = options.onTimeoutError?.(method, args);
|
|
2829
|
+
if (handleResult !== true)
|
|
2830
|
+
throw new Error(`[birpc] timeout on calling "${method}"`);
|
|
2840
2831
|
} catch (e) {
|
|
2841
2832
|
reject(e);
|
|
2842
2833
|
}
|
|
@@ -2853,17 +2844,24 @@ function createBirpc(functions, options) {
|
|
|
2853
2844
|
return sendCall;
|
|
2854
2845
|
}
|
|
2855
2846
|
});
|
|
2856
|
-
function close() {
|
|
2847
|
+
function close(error) {
|
|
2857
2848
|
closed = true;
|
|
2858
2849
|
rpcPromiseMap.forEach(({ reject, method }) => {
|
|
2859
|
-
reject(new Error(`[birpc] rpc is closed, cannot call "${method}"`));
|
|
2850
|
+
reject(error || new Error(`[birpc] rpc is closed, cannot call "${method}"`));
|
|
2860
2851
|
});
|
|
2861
2852
|
rpcPromiseMap.clear();
|
|
2862
2853
|
off(onMessage);
|
|
2863
2854
|
}
|
|
2864
2855
|
async function onMessage(data, ...extra) {
|
|
2865
|
-
|
|
2866
|
-
|
|
2856
|
+
let msg;
|
|
2857
|
+
try {
|
|
2858
|
+
msg = deserialize(data);
|
|
2859
|
+
} catch (e) {
|
|
2860
|
+
if (options.onGeneralError?.(e) !== true)
|
|
2861
|
+
throw e;
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
if (msg.t === TYPE_REQUEST) {
|
|
2867
2865
|
const { m: method, a: args } = msg;
|
|
2868
2866
|
let result, error;
|
|
2869
2867
|
const fn = resolver ? resolver(method, functions[method]) : functions[method];
|
|
@@ -2879,7 +2877,26 @@ function createBirpc(functions, options) {
|
|
|
2879
2877
|
if (msg.i) {
|
|
2880
2878
|
if (error && options.onError)
|
|
2881
2879
|
options.onError(error, method, args);
|
|
2882
|
-
|
|
2880
|
+
if (error && options.onFunctionError) {
|
|
2881
|
+
if (options.onFunctionError(error, method, args) === true)
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
if (!error) {
|
|
2885
|
+
try {
|
|
2886
|
+
post(serialize({ t: TYPE_RESPONSE, i: msg.i, r: result }), ...extra);
|
|
2887
|
+
return;
|
|
2888
|
+
} catch (e) {
|
|
2889
|
+
error = e;
|
|
2890
|
+
if (options.onGeneralError?.(e, method, args) !== true)
|
|
2891
|
+
throw e;
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
try {
|
|
2895
|
+
post(serialize({ t: TYPE_RESPONSE, i: msg.i, e: error }), ...extra);
|
|
2896
|
+
} catch (e) {
|
|
2897
|
+
if (options.onGeneralError?.(e, method, args) !== true)
|
|
2898
|
+
throw e;
|
|
2899
|
+
}
|
|
2883
2900
|
}
|
|
2884
2901
|
} else {
|
|
2885
2902
|
const { i: ack, r: result, e: error } = msg;
|
|
@@ -2934,10 +2951,6 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
2934
2951
|
if (!sessionId || !rpcId || projectName == null) {
|
|
2935
2952
|
return error(new Error(`[vitest] Invalid URL ${request.url}. "projectName", "sessionId" and "rpcId" queries are required.`));
|
|
2936
2953
|
}
|
|
2937
|
-
const method = searchParams.get("method");
|
|
2938
|
-
if (method !== "run" && method !== "collect") {
|
|
2939
|
-
return error(new Error(`[vitest] Method query in ${request.url} is invalid. Method should be either "run" or "collect".`));
|
|
2940
|
-
}
|
|
2941
2954
|
if (type === "orchestrator") {
|
|
2942
2955
|
const session = vitest._browserSessions.getSession(sessionId);
|
|
2943
2956
|
session?.connected();
|
|
@@ -2948,7 +2961,7 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
2948
2961
|
}
|
|
2949
2962
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
2950
2963
|
wss.emit("connection", ws, request);
|
|
2951
|
-
const rpc = setupClient(project, rpcId, ws
|
|
2964
|
+
const rpc = setupClient(project, rpcId, ws);
|
|
2952
2965
|
const state = project.browser.state;
|
|
2953
2966
|
const clients = type === "tester" ? state.testers : state.orchestrators;
|
|
2954
2967
|
clients.set(rpcId, rpc);
|
|
@@ -2957,6 +2970,10 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
2957
2970
|
debug$1?.("[%s] Browser API disconnected from %s", rpcId, type);
|
|
2958
2971
|
clients.delete(rpcId);
|
|
2959
2972
|
globalServer.removeCDPHandler(rpcId);
|
|
2973
|
+
if (type === "orchestrator") {
|
|
2974
|
+
vitest._browserSessions.destroySession(sessionId);
|
|
2975
|
+
}
|
|
2976
|
+
rpc.$close(new Error(`[vitest] Browser connection was closed while running tests. Was the page closed unexpectedly?`));
|
|
2960
2977
|
});
|
|
2961
2978
|
});
|
|
2962
2979
|
});
|
|
@@ -2969,7 +2986,7 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
2969
2986
|
throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
|
|
2970
2987
|
}
|
|
2971
2988
|
}
|
|
2972
|
-
function setupClient(project, rpcId, ws
|
|
2989
|
+
function setupClient(project, rpcId, ws) {
|
|
2973
2990
|
const mockResolver = new ServerMockResolver(globalServer.vite, { moduleDirectories: project.config.server?.deps?.moduleDirectories });
|
|
2974
2991
|
const mocker = project.browser?.provider.mocker;
|
|
2975
2992
|
const rpc = createBirpc({
|
|
@@ -2980,21 +2997,21 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
2980
2997
|
}
|
|
2981
2998
|
vitest.state.catchError(error, type);
|
|
2982
2999
|
},
|
|
2983
|
-
async onQueued(file) {
|
|
3000
|
+
async onQueued(method, file) {
|
|
2984
3001
|
if (method === "collect") {
|
|
2985
3002
|
vitest.state.collectFiles(project, [file]);
|
|
2986
3003
|
} else {
|
|
2987
3004
|
await vitest._testRun.enqueued(project, file);
|
|
2988
3005
|
}
|
|
2989
3006
|
},
|
|
2990
|
-
async onCollected(files) {
|
|
3007
|
+
async onCollected(method, files) {
|
|
2991
3008
|
if (method === "collect") {
|
|
2992
3009
|
vitest.state.collectFiles(project, files);
|
|
2993
3010
|
} else {
|
|
2994
3011
|
await vitest._testRun.collected(project, files);
|
|
2995
3012
|
}
|
|
2996
3013
|
},
|
|
2997
|
-
async onTaskUpdate(packs, events) {
|
|
3014
|
+
async onTaskUpdate(method, packs, events) {
|
|
2998
3015
|
if (method === "collect") {
|
|
2999
3016
|
vitest.state.updateTasks(packs);
|
|
3000
3017
|
} else {
|
|
@@ -3004,7 +3021,7 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
3004
3021
|
onAfterSuiteRun(meta) {
|
|
3005
3022
|
vitest.coverageProvider?.onAfterSuiteRun(meta);
|
|
3006
3023
|
},
|
|
3007
|
-
async sendLog(log) {
|
|
3024
|
+
async sendLog(method, log) {
|
|
3008
3025
|
if (method === "collect") {
|
|
3009
3026
|
vitest.state.updateUserLog(log);
|
|
3010
3027
|
} else {
|
|
@@ -3088,10 +3105,6 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
3088
3105
|
}, provider.getCommandsContext(sessionId));
|
|
3089
3106
|
return await commands[command](context, ...payload);
|
|
3090
3107
|
},
|
|
3091
|
-
finishBrowserTests(sessionId) {
|
|
3092
|
-
debug$1?.("[%s] Finishing browser tests for session", sessionId);
|
|
3093
|
-
return vitest._browserSessions.getSession(sessionId)?.resolve();
|
|
3094
|
-
},
|
|
3095
3108
|
resolveMock(rawId, importer, options) {
|
|
3096
3109
|
return mockResolver.resolveMock(rawId, importer, options);
|
|
3097
3110
|
},
|
|
@@ -3160,6 +3173,7 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
|
3160
3173
|
on: (fn) => ws.on("message", fn),
|
|
3161
3174
|
eventNames: ["onCancel", "cdpEvent"],
|
|
3162
3175
|
serialize: (data) => stringify(data, stringifyReplace),
|
|
3176
|
+
timeout: -1,
|
|
3163
3177
|
deserialize: parse,
|
|
3164
3178
|
onTimeoutError(functionName) {
|
|
3165
3179
|
throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
|
|
@@ -3194,69 +3208,30 @@ function stringifyReplace(key, value) {
|
|
|
3194
3208
|
}
|
|
3195
3209
|
|
|
3196
3210
|
const debug = createDebugger("vitest:browser:pool");
|
|
3197
|
-
async function waitForTests(method, sessionId, project, files) {
|
|
3198
|
-
const context = project.vitest._browserSessions.createAsyncSession(method, sessionId, files, project);
|
|
3199
|
-
return await context;
|
|
3200
|
-
}
|
|
3201
3211
|
function createBrowserPool(vitest) {
|
|
3202
3212
|
const providers = new Set();
|
|
3203
|
-
const
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3213
|
+
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
|
|
3214
|
+
const threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
|
|
3215
|
+
const projectPools = new WeakMap();
|
|
3216
|
+
const ensurePool = (project) => {
|
|
3217
|
+
if (projectPools.has(project)) {
|
|
3218
|
+
return projectPools.get(project);
|
|
3219
|
+
}
|
|
3220
|
+
debug?.("creating pool for project %s", project.name);
|
|
3221
|
+
const resolvedUrls = project.browser.vite.resolvedUrls;
|
|
3210
3222
|
const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
|
|
3211
3223
|
if (!origin) {
|
|
3212
|
-
throw new Error(`Can't find browser origin URL for project "${project.name}"
|
|
3224
|
+
throw new Error(`Can't find browser origin URL for project "${project.name}"`);
|
|
3213
3225
|
}
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
}
|
|
3218
|
-
if (!provider.getCDPSession) {
|
|
3219
|
-
throw new Error("Unable to set breakpoint, CDP not supported");
|
|
3220
|
-
}
|
|
3221
|
-
const session = await provider.getCDPSession(sessionId);
|
|
3222
|
-
await session.send("Debugger.enable", {});
|
|
3223
|
-
await session.send("Debugger.setBreakpointByUrl", {
|
|
3224
|
-
lineNumber: 0,
|
|
3225
|
-
urlRegex: escapePathToRegexp(file)
|
|
3226
|
-
});
|
|
3227
|
-
}
|
|
3228
|
-
const filesPerThread = Math.ceil(files.length / threadsCount);
|
|
3229
|
-
const chunks = [];
|
|
3230
|
-
for (let i = 0; i < files.length; i += filesPerThread) {
|
|
3231
|
-
const chunk = files.slice(i, i + filesPerThread);
|
|
3232
|
-
chunks.push(chunk);
|
|
3233
|
-
}
|
|
3234
|
-
debug?.(`[%s] Running %s tests in %s chunks (%s threads)`, project.name || "core", files.length, chunks.length, threadsCount);
|
|
3235
|
-
const orchestrators = [...browser.state.orchestrators.entries()];
|
|
3236
|
-
const promises = [];
|
|
3237
|
-
chunks.forEach((files, index) => {
|
|
3238
|
-
if (orchestrators[index]) {
|
|
3239
|
-
const [sessionId, orchestrator] = orchestrators[index];
|
|
3240
|
-
debug?.("Reusing orchestrator (session %s) for files: %s", sessionId, [...files.map((f) => relative(project.config.root, f))].join(", "));
|
|
3241
|
-
const promise = waitForTests(method, sessionId, project, files);
|
|
3242
|
-
const tester = orchestrator.createTesters(files).catch((error) => {
|
|
3243
|
-
if (error instanceof Error && error.message.startsWith("[birpc] rpc is closed")) {
|
|
3244
|
-
return;
|
|
3245
|
-
}
|
|
3246
|
-
return Promise.reject(error);
|
|
3247
|
-
});
|
|
3248
|
-
promises.push(promise, tester);
|
|
3249
|
-
} else {
|
|
3250
|
-
const sessionId = crypto.randomUUID();
|
|
3251
|
-
const waitPromise = waitForTests(method, sessionId, project, files);
|
|
3252
|
-
debug?.("Opening a new session %s for files: %s", sessionId, [...files.map((f) => relative(project.config.root, f))].join(", "));
|
|
3253
|
-
const url = new URL("/", origin);
|
|
3254
|
-
url.searchParams.set("sessionId", sessionId);
|
|
3255
|
-
const page = provider.openPage(sessionId, url.toString(), () => setBreakpoint(sessionId, files[0]));
|
|
3256
|
-
promises.push(page, waitPromise);
|
|
3257
|
-
}
|
|
3226
|
+
const pool = new BrowserPool(project, {
|
|
3227
|
+
maxWorkers: getThreadsCount(project),
|
|
3228
|
+
origin
|
|
3258
3229
|
});
|
|
3259
|
-
|
|
3230
|
+
projectPools.set(project, pool);
|
|
3231
|
+
vitest.onCancel(() => {
|
|
3232
|
+
pool.cancel();
|
|
3233
|
+
});
|
|
3234
|
+
return pool;
|
|
3260
3235
|
};
|
|
3261
3236
|
const runWorkspaceTests = async (method, specs) => {
|
|
3262
3237
|
const groupedFiles = new Map();
|
|
@@ -3269,30 +3244,29 @@ function createBrowserPool(vitest) {
|
|
|
3269
3244
|
vitest.onCancel(() => {
|
|
3270
3245
|
isCancelled = true;
|
|
3271
3246
|
});
|
|
3272
|
-
|
|
3273
|
-
if (isCancelled) {
|
|
3274
|
-
break;
|
|
3275
|
-
}
|
|
3247
|
+
await Promise.all([...groupedFiles.entries()].map(async ([project, files]) => {
|
|
3276
3248
|
await project._initBrowserProvider();
|
|
3277
3249
|
if (!project.browser) {
|
|
3278
3250
|
throw new TypeError(`The browser server was not initialized${project.name ? ` for the "${project.name}" project` : ""}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
|
|
3279
3251
|
}
|
|
3280
|
-
|
|
3281
|
-
|
|
3252
|
+
if (isCancelled) {
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
const pool = ensurePool(project);
|
|
3256
|
+
vitest.state.clearFiles(project, files);
|
|
3257
|
+
providers.add(project.browser.provider);
|
|
3258
|
+
await pool.runTests(method, files);
|
|
3259
|
+
}));
|
|
3282
3260
|
};
|
|
3283
|
-
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
|
|
3284
3261
|
function getThreadsCount(project) {
|
|
3285
3262
|
const config = project.config.browser;
|
|
3286
|
-
if (!config.headless || !project.browser.provider.supportsParallelism) {
|
|
3287
|
-
return 1;
|
|
3288
|
-
}
|
|
3289
|
-
if (!config.fileParallelism) {
|
|
3263
|
+
if (!config.headless || !config.fileParallelism || !project.browser.provider.supportsParallelism) {
|
|
3290
3264
|
return 1;
|
|
3291
3265
|
}
|
|
3292
3266
|
if (project.config.maxWorkers) {
|
|
3293
3267
|
return project.config.maxWorkers;
|
|
3294
3268
|
}
|
|
3295
|
-
return
|
|
3269
|
+
return threadsCount;
|
|
3296
3270
|
}
|
|
3297
3271
|
return {
|
|
3298
3272
|
name: "browser",
|
|
@@ -3312,6 +3286,132 @@ function createBrowserPool(vitest) {
|
|
|
3312
3286
|
function escapePathToRegexp(path) {
|
|
3313
3287
|
return path.replace(/[/\\.?*()^${}|[\]+]/g, "\\$&");
|
|
3314
3288
|
}
|
|
3289
|
+
class BrowserPool {
|
|
3290
|
+
_queue = [];
|
|
3291
|
+
_promise;
|
|
3292
|
+
_providedContext;
|
|
3293
|
+
readySessions = new Set();
|
|
3294
|
+
constructor(project, options) {
|
|
3295
|
+
this.project = project;
|
|
3296
|
+
this.options = options;
|
|
3297
|
+
}
|
|
3298
|
+
cancel() {
|
|
3299
|
+
this._queue = [];
|
|
3300
|
+
}
|
|
3301
|
+
reject(error) {
|
|
3302
|
+
this._promise?.reject(error);
|
|
3303
|
+
this._promise = undefined;
|
|
3304
|
+
this.cancel();
|
|
3305
|
+
}
|
|
3306
|
+
get orchestrators() {
|
|
3307
|
+
return this.project.browser.state.orchestrators;
|
|
3308
|
+
}
|
|
3309
|
+
async runTests(method, files) {
|
|
3310
|
+
this._promise ??= createDefer();
|
|
3311
|
+
if (!files.length) {
|
|
3312
|
+
this._promise.resolve();
|
|
3313
|
+
return this._promise;
|
|
3314
|
+
}
|
|
3315
|
+
this._providedContext = stringify(this.project.getProvidedContext());
|
|
3316
|
+
this._queue.push(...files);
|
|
3317
|
+
this.readySessions.forEach((sessionId) => {
|
|
3318
|
+
if (this._queue.length) {
|
|
3319
|
+
this.readySessions.delete(sessionId);
|
|
3320
|
+
this.runNextTest(method, sessionId);
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3323
|
+
if (this.orchestrators.size >= this.options.maxWorkers) {
|
|
3324
|
+
return this._promise;
|
|
3325
|
+
}
|
|
3326
|
+
const workerCount = Math.min(this.options.maxWorkers - this.orchestrators.size, files.length);
|
|
3327
|
+
const promises = [];
|
|
3328
|
+
for (let i = 0; i < workerCount; i++) {
|
|
3329
|
+
const sessionId = crypto.randomUUID();
|
|
3330
|
+
const page = this.openPage(sessionId).then(() => {
|
|
3331
|
+
this.runNextTest(method, sessionId);
|
|
3332
|
+
});
|
|
3333
|
+
promises.push(page);
|
|
3334
|
+
}
|
|
3335
|
+
await Promise.all(promises);
|
|
3336
|
+
return this._promise;
|
|
3337
|
+
}
|
|
3338
|
+
async openPage(sessionId) {
|
|
3339
|
+
const sessionPromise = this.project.vitest._browserSessions.createSession(sessionId, this.project, this);
|
|
3340
|
+
const url = new URL("/", this.options.origin);
|
|
3341
|
+
url.searchParams.set("sessionId", sessionId);
|
|
3342
|
+
const pagePromise = this.project.browser.provider.openPage(sessionId, url.toString());
|
|
3343
|
+
await Promise.all([sessionPromise, pagePromise]);
|
|
3344
|
+
}
|
|
3345
|
+
getOrchestrator(sessionId) {
|
|
3346
|
+
const orchestrator = this.orchestrators.get(sessionId);
|
|
3347
|
+
if (!orchestrator) {
|
|
3348
|
+
throw new Error(`Orchestrator not found for session ${sessionId}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
|
|
3349
|
+
}
|
|
3350
|
+
return orchestrator;
|
|
3351
|
+
}
|
|
3352
|
+
finishSession(sessionId) {
|
|
3353
|
+
this.readySessions.add(sessionId);
|
|
3354
|
+
if (this.readySessions.size === this.orchestrators.size) {
|
|
3355
|
+
this._promise?.resolve();
|
|
3356
|
+
this._promise = undefined;
|
|
3357
|
+
debug?.("all tests finished running");
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
runNextTest(method, sessionId) {
|
|
3361
|
+
const file = this._queue.shift();
|
|
3362
|
+
if (!file) {
|
|
3363
|
+
debug?.("[%s] no more tests to run", sessionId);
|
|
3364
|
+
const isolate = this.project.config.browser.isolate;
|
|
3365
|
+
if (isolate) {
|
|
3366
|
+
this.finishSession(sessionId);
|
|
3367
|
+
return;
|
|
3368
|
+
}
|
|
3369
|
+
const orchestrator = this.getOrchestrator(sessionId);
|
|
3370
|
+
orchestrator.cleanupTesters().catch((error) => this.reject(error)).finally(() => this.finishSession(sessionId));
|
|
3371
|
+
return;
|
|
3372
|
+
}
|
|
3373
|
+
if (!this._promise) {
|
|
3374
|
+
throw new Error(`Unexpected empty queue`);
|
|
3375
|
+
}
|
|
3376
|
+
const orchestrator = this.getOrchestrator(sessionId);
|
|
3377
|
+
debug?.("[%s] run test %s", sessionId, file);
|
|
3378
|
+
this.setBreakpoint(sessionId, file).then(() => {
|
|
3379
|
+
orchestrator.createTesters({
|
|
3380
|
+
method,
|
|
3381
|
+
files: [file],
|
|
3382
|
+
providedContext: this._providedContext || "[{}]"
|
|
3383
|
+
}).then(() => {
|
|
3384
|
+
debug?.("[%s] test %s finished running", sessionId, file);
|
|
3385
|
+
this.runNextTest(method, sessionId);
|
|
3386
|
+
}).catch((error) => {
|
|
3387
|
+
if (this.project.vitest.isCancelling && error instanceof Error && error.message.startsWith("Browser connection was closed while running tests")) {
|
|
3388
|
+
this.cancel();
|
|
3389
|
+
this._promise?.resolve();
|
|
3390
|
+
this._promise = undefined;
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
debug?.("[%s] error during %s test run: %s", sessionId, file, error);
|
|
3394
|
+
this.reject(error);
|
|
3395
|
+
});
|
|
3396
|
+
}).catch((err) => this.reject(err));
|
|
3397
|
+
}
|
|
3398
|
+
async setBreakpoint(sessionId, file) {
|
|
3399
|
+
if (!this.project.config.inspector.waitForDebugger) {
|
|
3400
|
+
return;
|
|
3401
|
+
}
|
|
3402
|
+
const provider = this.project.browser.provider;
|
|
3403
|
+
if (!provider.getCDPSession) {
|
|
3404
|
+
throw new Error("Unable to set breakpoint, CDP not supported");
|
|
3405
|
+
}
|
|
3406
|
+
debug?.("[%s] set breakpoint for %s", sessionId, file);
|
|
3407
|
+
const session = await provider.getCDPSession(sessionId);
|
|
3408
|
+
await session.send("Debugger.enable", {});
|
|
3409
|
+
await session.send("Debugger.setBreakpointByUrl", {
|
|
3410
|
+
lineNumber: 0,
|
|
3411
|
+
urlRegex: escapePathToRegexp(file)
|
|
3412
|
+
});
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3315
3415
|
|
|
3316
3416
|
async function createBrowserServer(project, configFile, prePlugins = [], postPlugins = []) {
|
|
3317
3417
|
if (project.vitest.version !== version) {
|