@vitest/browser 3.1.0-beta.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/context.d.ts +24 -1
- package/dist/client/.vite/manifest.json +1 -1
- package/dist/client/__vitest__/assets/index-B0KEk_KY.css +1 -0
- package/dist/client/__vitest__/assets/index-BLZJq7cG.js +52 -0
- package/dist/client/__vitest__/index.html +2 -2
- package/dist/client/__vitest_browser__/tester-DiLSqOx4.js +3431 -0
- package/dist/client/tester/tester.html +1 -1
- package/dist/client.js +109 -105
- package/dist/context.js +374 -390
- package/dist/expect-element.js +25 -0
- package/dist/index-DjDyxzt8.js +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +1991 -1887
- package/dist/locators/index.d.ts +64 -0
- package/dist/locators/index.js +1 -4
- package/dist/locators/playwright.js +1 -123
- package/dist/locators/preview.js +1 -85
- package/dist/locators/webdriverio.js +1 -157
- package/dist/providers.js +35 -37
- package/dist/public-utils-xf4CCUzp.js +6 -0
- package/dist/state.js +170 -1
- package/dist/utils.js +1 -3
- package/dist/webdriver-2iYWIzBv.js +403 -0
- package/jest-dom.d.ts +623 -712
- package/matchers.d.ts +4 -4
- package/package.json +14 -16
- package/dist/client/__vitest__/assets/index-Bne9c1R6.css +0 -1
- package/dist/client/__vitest__/assets/index-CsZqQx26.js +0 -52
- package/dist/client/__vitest_browser__/tester-lo_P6U-u.js +0 -15577
- package/dist/index-DrTP5i7N.js +0 -195
- package/dist/public-utils-J4vwTaki.js +0 -5561
- package/dist/utils-VCysLhWp.js +0 -115
- package/dist/webdriver-C5-VI7VH.js +0 -275
package/dist/index.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
|
+
import { ManualMockedModule, RedirectedModule, AutomockedModule, AutospiedModule, MockerRegistry } from '@vitest/mocker';
|
|
2
|
+
import { dynamicImportPlugin, ServerMockResolver, interceptorPlugin } from '@vitest/mocker/node';
|
|
1
3
|
import c from 'tinyrainbow';
|
|
2
4
|
import { getFilePoolName, distDir, resolveApiServerConfig, resolveFsAllow, isFileServingAllowed, createDebugger, isValidApiRequest, createViteLogger, createViteServer } from 'vitest/node';
|
|
3
5
|
import fs, { readFileSync, lstatSync, promises, existsSync } from 'node:fs';
|
|
4
6
|
import { createRequire } from 'node:module';
|
|
5
|
-
import { dynamicImportPlugin, ServerMockResolver } from '@vitest/mocker/node';
|
|
6
7
|
import { slash as slash$1, toArray } from '@vitest/utils';
|
|
7
8
|
import MagicString from 'magic-string';
|
|
8
9
|
import sirv from 'sirv';
|
|
10
|
+
import * as vite from 'vite';
|
|
9
11
|
import { coverageConfigDefaults } from 'vitest/config';
|
|
10
12
|
import { fileURLToPath } from 'node:url';
|
|
11
13
|
import crypto from 'node:crypto';
|
|
12
14
|
import { mkdir, readFile as readFile$1 } from 'node:fs/promises';
|
|
13
15
|
import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
|
|
14
|
-
import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-
|
|
16
|
+
import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-2iYWIzBv.js';
|
|
15
17
|
import { resolve as resolve$1, dirname as dirname$1, basename as basename$1, normalize as normalize$1 } from 'node:path';
|
|
16
18
|
import { WebSocketServer } from 'ws';
|
|
17
19
|
import * as nodeos from 'node:os';
|
|
18
20
|
|
|
19
|
-
var version = "3.1.0
|
|
21
|
+
var version = "3.1.0";
|
|
20
22
|
|
|
21
23
|
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
22
24
|
function normalizeWindowsPath(input = "") {
|
|
@@ -212,130 +214,117 @@ const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
|
212
214
|
const distRoot = resolve(pkgRoot, "dist");
|
|
213
215
|
|
|
214
216
|
function replacer(code, values) {
|
|
215
|
-
|
|
217
|
+
return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
|
|
216
218
|
}
|
|
217
|
-
const builtinProviders = [
|
|
219
|
+
const builtinProviders = [
|
|
220
|
+
"webdriverio",
|
|
221
|
+
"playwright",
|
|
222
|
+
"preview"
|
|
223
|
+
];
|
|
218
224
|
async function getBrowserProvider(options, project) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
if (customProviderModule.default == null) {
|
|
236
|
-
throw new Error(
|
|
237
|
-
`Custom BrowserProvider loaded from ${options.provider} was not the default export`
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
return customProviderModule.default;
|
|
225
|
+
if (options.provider == null || builtinProviders.includes(options.provider)) {
|
|
226
|
+
const providers = await import('./providers.js');
|
|
227
|
+
const provider = options.provider || "preview";
|
|
228
|
+
return providers[provider];
|
|
229
|
+
}
|
|
230
|
+
let customProviderModule;
|
|
231
|
+
try {
|
|
232
|
+
customProviderModule = await project.import(options.provider);
|
|
233
|
+
} catch (error) {
|
|
234
|
+
throw new Error(`Failed to load custom BrowserProvider from ${options.provider}`, { cause: error });
|
|
235
|
+
}
|
|
236
|
+
if (customProviderModule.default == null) {
|
|
237
|
+
throw new Error(`Custom BrowserProvider loaded from ${options.provider} was not the default export`);
|
|
238
|
+
}
|
|
239
|
+
return customProviderModule.default;
|
|
241
240
|
}
|
|
242
241
|
function slash(path) {
|
|
243
|
-
|
|
242
|
+
return path.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
244
243
|
}
|
|
245
244
|
|
|
246
245
|
async function resolveOrchestrator(globalServer, url, res) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
__VITEST_FAVICON__: globalServer.faviconUrl,
|
|
303
|
-
__VITEST_TITLE__: "Vitest Browser Runner",
|
|
304
|
-
__VITEST_SCRIPTS__: globalServer.orchestratorScripts,
|
|
305
|
-
__VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
|
|
306
|
-
__VITEST_ERROR_CATCHER__: `<script type="module" src="${globalServer.errorCatcherUrl}"><\/script>`,
|
|
307
|
-
__VITEST_SESSION_ID__: JSON.stringify(sessionId)
|
|
308
|
-
});
|
|
246
|
+
let sessionId = url.searchParams.get("sessionId");
|
|
247
|
+
if (!sessionId) {
|
|
248
|
+
const contexts = [...globalServer.children].flatMap((p) => [...p.state.orchestrators.keys()]);
|
|
249
|
+
sessionId = contexts[contexts.length - 1] ?? "none";
|
|
250
|
+
}
|
|
251
|
+
const session = globalServer.vitest._browserSessions.getSession(sessionId);
|
|
252
|
+
const files = session?.files ?? [];
|
|
253
|
+
const browserProject = session?.project.browser || [...globalServer.children][0];
|
|
254
|
+
if (!browserProject) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const injectorJs = typeof globalServer.injectorJs === "string" ? globalServer.injectorJs : await globalServer.injectorJs;
|
|
258
|
+
const injector = replacer(injectorJs, {
|
|
259
|
+
__VITEST_PROVIDER__: JSON.stringify(browserProject.config.browser.provider || "preview"),
|
|
260
|
+
__VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
|
|
261
|
+
__VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root }),
|
|
262
|
+
__VITEST_METHOD__: JSON.stringify(session?.method || "run"),
|
|
263
|
+
__VITEST_FILES__: JSON.stringify(files),
|
|
264
|
+
__VITEST_TYPE__: "\"orchestrator\"",
|
|
265
|
+
__VITEST_SESSION_ID__: JSON.stringify(sessionId),
|
|
266
|
+
__VITEST_TESTER_ID__: "\"none\"",
|
|
267
|
+
__VITEST_PROVIDED_CONTEXT__: "{}",
|
|
268
|
+
__VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
|
|
269
|
+
});
|
|
270
|
+
res.removeHeader("Content-Security-Policy");
|
|
271
|
+
if (!globalServer.orchestratorScripts) {
|
|
272
|
+
globalServer.orchestratorScripts = (await globalServer.formatScripts(globalServer.config.browser.orchestratorScripts)).map((script) => {
|
|
273
|
+
let html = "<script ";
|
|
274
|
+
for (const attr in script.attrs || {}) {
|
|
275
|
+
html += `${attr}="${script.attrs[attr]}" `;
|
|
276
|
+
}
|
|
277
|
+
html += `>${script.children}</script>`;
|
|
278
|
+
return html;
|
|
279
|
+
}).join("\n");
|
|
280
|
+
}
|
|
281
|
+
let baseHtml = typeof globalServer.orchestratorHtml === "string" ? globalServer.orchestratorHtml : await globalServer.orchestratorHtml;
|
|
282
|
+
if (globalServer.config.browser.ui) {
|
|
283
|
+
const manifestContent = globalServer.manifest instanceof Promise ? await globalServer.manifest : globalServer.manifest;
|
|
284
|
+
const jsEntry = manifestContent["orchestrator.html"].file;
|
|
285
|
+
const base = browserProject.parent.vite.config.base || "/";
|
|
286
|
+
baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace("<!-- !LOAD_METADATA! -->", [
|
|
287
|
+
"{__VITEST_INJECTOR__}",
|
|
288
|
+
"{__VITEST_ERROR_CATCHER__}",
|
|
289
|
+
"{__VITEST_SCRIPTS__}",
|
|
290
|
+
`<script type="module" crossorigin src="${base}${jsEntry}"></script>`
|
|
291
|
+
].join("\n"));
|
|
292
|
+
}
|
|
293
|
+
return replacer(baseHtml, {
|
|
294
|
+
__VITEST_FAVICON__: globalServer.faviconUrl,
|
|
295
|
+
__VITEST_TITLE__: "Vitest Browser Runner",
|
|
296
|
+
__VITEST_SCRIPTS__: globalServer.orchestratorScripts,
|
|
297
|
+
__VITEST_INJECTOR__: `<script type="module">${injector}</script>`,
|
|
298
|
+
__VITEST_ERROR_CATCHER__: `<script type="module" src="${globalServer.errorCatcherUrl}"></script>`,
|
|
299
|
+
__VITEST_SESSION_ID__: JSON.stringify(sessionId)
|
|
300
|
+
});
|
|
309
301
|
}
|
|
310
302
|
|
|
311
303
|
function disableCache(res) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
"no-cache, max-age=0, must-revalidate"
|
|
315
|
-
);
|
|
316
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
304
|
+
res.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate");
|
|
305
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
317
306
|
}
|
|
318
307
|
function allowIframes(res) {
|
|
319
|
-
|
|
308
|
+
res.removeHeader("X-Frame-Options");
|
|
320
309
|
}
|
|
321
310
|
|
|
322
311
|
function createOrchestratorMiddleware(parentServer) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
312
|
+
return async function vitestOrchestratorMiddleware(req, res, next) {
|
|
313
|
+
if (!req.url) {
|
|
314
|
+
return next();
|
|
315
|
+
}
|
|
316
|
+
const url = new URL(req.url, "http://localhost");
|
|
317
|
+
if (url.pathname !== parentServer.base) {
|
|
318
|
+
return next();
|
|
319
|
+
}
|
|
320
|
+
const html = await resolveOrchestrator(parentServer, url, res);
|
|
321
|
+
if (html) {
|
|
322
|
+
disableCache(res);
|
|
323
|
+
allowIframes(res);
|
|
324
|
+
res.write(html, "utf-8");
|
|
325
|
+
res.end();
|
|
326
|
+
}
|
|
327
|
+
};
|
|
339
328
|
}
|
|
340
329
|
|
|
341
330
|
/// <reference types="../types/index.d.ts" />
|
|
@@ -446,119 +435,111 @@ const stringify = (value, replacer, space) => {
|
|
|
446
435
|
};
|
|
447
436
|
|
|
448
437
|
async function resolveTester(globalServer, url, res, next) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const indexhtml = await browserProject.vite.transformIndexHtml(url2, testerHtml);
|
|
494
|
-
const html = replacer(indexhtml, {
|
|
495
|
-
__VITEST_FAVICON__: globalServer.faviconUrl,
|
|
496
|
-
__VITEST_INJECTOR__: injector,
|
|
497
|
-
__VITEST_APPEND__: `
|
|
438
|
+
const csp = res.getHeader("Content-Security-Policy");
|
|
439
|
+
if (typeof csp === "string") {
|
|
440
|
+
res.setHeader("Content-Security-Policy", csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *"));
|
|
441
|
+
}
|
|
442
|
+
const { sessionId, testFile } = globalServer.resolveTesterUrl(url.pathname);
|
|
443
|
+
const session = globalServer.vitest._browserSessions.getSession(sessionId);
|
|
444
|
+
if (!session) {
|
|
445
|
+
res.statusCode = 400;
|
|
446
|
+
res.end("Invalid session ID");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
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
|
+
const browserProject = project.browser || [...globalServer.children][0];
|
|
456
|
+
if (!browserProject) {
|
|
457
|
+
res.statusCode = 400;
|
|
458
|
+
res.end("Invalid session ID");
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const injectorJs = typeof globalServer.injectorJs === "string" ? globalServer.injectorJs : await globalServer.injectorJs;
|
|
462
|
+
const injector = replacer(injectorJs, {
|
|
463
|
+
__VITEST_PROVIDER__: JSON.stringify(project.browser.provider.name),
|
|
464
|
+
__VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
|
|
465
|
+
__VITEST_FILES__: JSON.stringify(files),
|
|
466
|
+
__VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root }),
|
|
467
|
+
__VITEST_TYPE__: "\"tester\"",
|
|
468
|
+
__VITEST_METHOD__: JSON.stringify(method),
|
|
469
|
+
__VITEST_SESSION_ID__: JSON.stringify(sessionId),
|
|
470
|
+
__VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
|
|
471
|
+
__VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext())),
|
|
472
|
+
__VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
|
|
473
|
+
});
|
|
474
|
+
const testerHtml = typeof browserProject.testerHtml === "string" ? browserProject.testerHtml : await browserProject.testerHtml;
|
|
475
|
+
try {
|
|
476
|
+
const url = join("/@fs/", browserProject.testerFilepath);
|
|
477
|
+
const indexhtml = await browserProject.vite.transformIndexHtml(url, testerHtml);
|
|
478
|
+
const html = replacer(indexhtml, {
|
|
479
|
+
__VITEST_FAVICON__: globalServer.faviconUrl,
|
|
480
|
+
__VITEST_INJECTOR__: injector,
|
|
481
|
+
__VITEST_APPEND__: `
|
|
498
482
|
__vitest_browser_runner__.runningFiles = ${tests}
|
|
499
483
|
__vitest_browser_runner__.iframeId = ${iframeId}
|
|
500
484
|
__vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
|
|
501
485
|
document.querySelector('script[data-vitest-append]').remove()
|
|
502
486
|
`
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
487
|
+
});
|
|
488
|
+
return html;
|
|
489
|
+
} catch (err) {
|
|
490
|
+
session.reject(err);
|
|
491
|
+
next(err);
|
|
492
|
+
}
|
|
509
493
|
}
|
|
510
494
|
|
|
511
495
|
function createTesterMiddleware(browserServer) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
496
|
+
return async function vitestTesterMiddleware(req, res, next) {
|
|
497
|
+
if (!req.url) {
|
|
498
|
+
return next();
|
|
499
|
+
}
|
|
500
|
+
const url = new URL(req.url, "http://localhost");
|
|
501
|
+
if (!url.pathname.startsWith(browserServer.prefixTesterUrl)) {
|
|
502
|
+
return next();
|
|
503
|
+
}
|
|
504
|
+
const html = await resolveTester(browserServer, url, res, next);
|
|
505
|
+
if (html) {
|
|
506
|
+
disableCache(res);
|
|
507
|
+
allowIframes(res);
|
|
508
|
+
res.write(html, "utf-8");
|
|
509
|
+
res.end();
|
|
510
|
+
}
|
|
511
|
+
};
|
|
528
512
|
}
|
|
529
513
|
|
|
530
514
|
const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
|
|
531
515
|
const ID_CONTEXT = "@vitest/browser/context";
|
|
532
516
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
533
517
|
function BrowserContext(globalServer) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
518
|
+
return {
|
|
519
|
+
name: "vitest:browser:virtual-module:context",
|
|
520
|
+
enforce: "pre",
|
|
521
|
+
resolveId(id) {
|
|
522
|
+
if (id === ID_CONTEXT) {
|
|
523
|
+
return VIRTUAL_ID_CONTEXT;
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
load(id) {
|
|
527
|
+
if (id === VIRTUAL_ID_CONTEXT) {
|
|
528
|
+
return generateContextFile.call(this, globalServer);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
};
|
|
548
532
|
}
|
|
549
533
|
async function generateContextFile(globalServer) {
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
);
|
|
560
|
-
const distContextPath = slash$1(`/@fs/${resolve(__dirname, "context.js")}`);
|
|
561
|
-
return `
|
|
534
|
+
const commands = Object.keys(globalServer.commands);
|
|
535
|
+
const provider = [...globalServer.children][0].provider || { name: "preview" };
|
|
536
|
+
const providerName = provider.name;
|
|
537
|
+
const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
|
|
538
|
+
return ` ["${command}"]: (...args) => __vitest_browser_runner__.commands.triggerCommand("${command}", args),`;
|
|
539
|
+
}).join("\n");
|
|
540
|
+
const userEventNonProviderImport = await getUserEventImport(providerName, this.resolve.bind(this));
|
|
541
|
+
const distContextPath = slash$1(`/@fs/${resolve(__dirname, "context.js")}`);
|
|
542
|
+
return `
|
|
562
543
|
import { page, createUserEvent, cdp } from '${distContextPath}'
|
|
563
544
|
${userEventNonProviderImport}
|
|
564
545
|
|
|
@@ -577,410 +558,361 @@ export const userEvent = createUserEvent(_userEventSetup)
|
|
|
577
558
|
export { page, cdp }
|
|
578
559
|
`;
|
|
579
560
|
}
|
|
580
|
-
async function getUserEventImport(provider,
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
561
|
+
async function getUserEventImport(provider, resolve) {
|
|
562
|
+
if (provider !== "preview") {
|
|
563
|
+
return "const _userEventSetup = undefined";
|
|
564
|
+
}
|
|
565
|
+
const resolved = await resolve("@testing-library/user-event", __dirname);
|
|
566
|
+
if (!resolved) {
|
|
567
|
+
throw new Error(`Failed to resolve user-event package from ${__dirname}`);
|
|
568
|
+
}
|
|
569
|
+
return `\
|
|
570
|
+
import { userEvent as __vitest_user_event__ } from '${slash$1(`/@fs/${resolved.id}`)}'
|
|
589
571
|
const _userEventSetup = __vitest_user_event__
|
|
590
572
|
`;
|
|
591
573
|
}
|
|
592
574
|
|
|
593
575
|
const versionRegexp = /(?:\?|&)v=\w{8}/;
|
|
594
576
|
var BrowserPlugin = (parentServer, base = "/") => {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
`
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
name: "vitest:browser:transform-tester-html",
|
|
935
|
-
enforce: "pre",
|
|
936
|
-
async transformIndexHtml(html, ctx) {
|
|
937
|
-
const projectBrowser = [...parentServer.children].find((server) => {
|
|
938
|
-
return ctx.filename === server.testerFilepath;
|
|
939
|
-
});
|
|
940
|
-
if (!projectBrowser) {
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
if (!parentServer.testerScripts) {
|
|
944
|
-
const testerScripts = await parentServer.formatScripts(
|
|
945
|
-
parentServer.config.browser.testerScripts
|
|
946
|
-
);
|
|
947
|
-
parentServer.testerScripts = testerScripts;
|
|
948
|
-
}
|
|
949
|
-
const stateJs = typeof parentServer.stateJs === "string" ? parentServer.stateJs : await parentServer.stateJs;
|
|
950
|
-
const testerTags = [];
|
|
951
|
-
const isDefaultTemplate = resolve(distRoot, "client/tester/tester.html") === projectBrowser.testerFilepath;
|
|
952
|
-
if (!isDefaultTemplate) {
|
|
953
|
-
const manifestContent = parentServer.manifest instanceof Promise ? await parentServer.manifest : parentServer.manifest;
|
|
954
|
-
const testerEntry = manifestContent["tester/tester.html"];
|
|
955
|
-
testerTags.push({
|
|
956
|
-
tag: "script",
|
|
957
|
-
attrs: {
|
|
958
|
-
type: "module",
|
|
959
|
-
crossorigin: "",
|
|
960
|
-
src: `${parentServer.base}${testerEntry.file}`
|
|
961
|
-
},
|
|
962
|
-
injectTo: "head"
|
|
963
|
-
});
|
|
964
|
-
for (const importName of testerEntry.imports || []) {
|
|
965
|
-
const entryManifest = manifestContent[importName];
|
|
966
|
-
if (entryManifest) {
|
|
967
|
-
testerTags.push(
|
|
968
|
-
{
|
|
969
|
-
tag: "link",
|
|
970
|
-
attrs: {
|
|
971
|
-
href: `${parentServer.base}${entryManifest.file}`,
|
|
972
|
-
rel: "modulepreload",
|
|
973
|
-
crossorigin: ""
|
|
974
|
-
},
|
|
975
|
-
injectTo: "head"
|
|
976
|
-
}
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
} else {
|
|
981
|
-
testerTags.push({
|
|
982
|
-
tag: "style",
|
|
983
|
-
children: `
|
|
577
|
+
function isPackageExists(pkg, root) {
|
|
578
|
+
return parentServer.vitest.packageInstaller.isPackageExists?.(pkg, { paths: [root] });
|
|
579
|
+
}
|
|
580
|
+
return [
|
|
581
|
+
{
|
|
582
|
+
enforce: "pre",
|
|
583
|
+
name: "vitest:browser",
|
|
584
|
+
async configureServer(server) {
|
|
585
|
+
parentServer.setServer(server);
|
|
586
|
+
server.middlewares.use(function vitestHeaders(_req, res, next) {
|
|
587
|
+
const headers = server.config.server.headers;
|
|
588
|
+
if (headers) {
|
|
589
|
+
for (const name in headers) {
|
|
590
|
+
res.setHeader(name, headers[name]);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
next();
|
|
594
|
+
});
|
|
595
|
+
server.middlewares.use(createOrchestratorMiddleware(parentServer));
|
|
596
|
+
server.middlewares.use(createTesterMiddleware(parentServer));
|
|
597
|
+
server.middlewares.use(`${base}favicon.svg`, (_, res) => {
|
|
598
|
+
const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
|
|
599
|
+
res.write(content, "utf-8");
|
|
600
|
+
res.end();
|
|
601
|
+
});
|
|
602
|
+
const coverageFolder = resolveCoverageFolder(parentServer.vitest);
|
|
603
|
+
const coveragePath = coverageFolder ? coverageFolder[1] : undefined;
|
|
604
|
+
if (coveragePath && base === coveragePath) {
|
|
605
|
+
throw new Error(`The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`);
|
|
606
|
+
}
|
|
607
|
+
if (coverageFolder) {
|
|
608
|
+
server.middlewares.use(coveragePath, sirv(coverageFolder[0], {
|
|
609
|
+
single: true,
|
|
610
|
+
dev: true,
|
|
611
|
+
setHeaders: (res) => {
|
|
612
|
+
res.setHeader("Cache-Control", "public,max-age=0,must-revalidate");
|
|
613
|
+
}
|
|
614
|
+
}));
|
|
615
|
+
}
|
|
616
|
+
const uiEnabled = parentServer.config.browser.ui;
|
|
617
|
+
if (uiEnabled) {
|
|
618
|
+
server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
|
|
619
|
+
if (!req.url) {
|
|
620
|
+
res.statusCode = 404;
|
|
621
|
+
res.end();
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
const url = new URL(req.url, "http://localhost");
|
|
625
|
+
const id = url.searchParams.get("id");
|
|
626
|
+
if (!id) {
|
|
627
|
+
res.statusCode = 404;
|
|
628
|
+
res.end();
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const task = parentServer.vitest.state.idMap.get(id);
|
|
632
|
+
const file = task?.meta.failScreenshotPath;
|
|
633
|
+
if (!file) {
|
|
634
|
+
res.statusCode = 404;
|
|
635
|
+
res.end();
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
let stat;
|
|
639
|
+
try {
|
|
640
|
+
stat = lstatSync(file);
|
|
641
|
+
} catch {}
|
|
642
|
+
if (!stat?.isFile()) {
|
|
643
|
+
res.statusCode = 404;
|
|
644
|
+
res.end();
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
const ext = extname(file);
|
|
648
|
+
const buffer = readFileSync(file);
|
|
649
|
+
res.setHeader("Cache-Control", "public,max-age=0,must-revalidate");
|
|
650
|
+
res.setHeader("Content-Length", buffer.length);
|
|
651
|
+
res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
|
|
652
|
+
res.end(buffer);
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
server.middlewares.use((req, res, next) => {
|
|
656
|
+
if (req.url && versionRegexp.test(req.url) && !req.url.includes("chunk-")) {
|
|
657
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
658
|
+
const setHeader = res.setHeader.bind(res);
|
|
659
|
+
res.setHeader = function(name, value) {
|
|
660
|
+
if (name === "Cache-Control") {
|
|
661
|
+
return res;
|
|
662
|
+
}
|
|
663
|
+
return setHeader(name, value);
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
next();
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
name: "vitest:browser:tests",
|
|
672
|
+
enforce: "pre",
|
|
673
|
+
async config() {
|
|
674
|
+
const project = parentServer.project;
|
|
675
|
+
const { testFiles: allTestFiles } = await project.globTestFiles();
|
|
676
|
+
const browserTestFiles = allTestFiles.filter((file) => getFilePoolName(project, file) === "browser");
|
|
677
|
+
const setupFiles = toArray(project.config.setupFiles);
|
|
678
|
+
const define = {};
|
|
679
|
+
for (const env in project.config.env || {}) {
|
|
680
|
+
const stringValue = JSON.stringify(project.config.env[env]);
|
|
681
|
+
define[`import.meta.env.${env}`] = stringValue;
|
|
682
|
+
}
|
|
683
|
+
const entries = [
|
|
684
|
+
...browserTestFiles,
|
|
685
|
+
...setupFiles,
|
|
686
|
+
resolve(distDir, "index.js"),
|
|
687
|
+
resolve(distDir, "browser.js"),
|
|
688
|
+
resolve(distDir, "runners.js"),
|
|
689
|
+
resolve(distDir, "utils.js"),
|
|
690
|
+
...project.config.snapshotSerializers || []
|
|
691
|
+
];
|
|
692
|
+
const exclude = [
|
|
693
|
+
"vitest",
|
|
694
|
+
"vitest/utils",
|
|
695
|
+
"vitest/browser",
|
|
696
|
+
"vitest/runners",
|
|
697
|
+
"@vitest/browser",
|
|
698
|
+
"@vitest/browser/client",
|
|
699
|
+
"@vitest/utils",
|
|
700
|
+
"@vitest/utils/source-map",
|
|
701
|
+
"@vitest/runner",
|
|
702
|
+
"@vitest/spy",
|
|
703
|
+
"@vitest/utils/error",
|
|
704
|
+
"@vitest/snapshot",
|
|
705
|
+
"@vitest/expect",
|
|
706
|
+
"std-env",
|
|
707
|
+
"tinybench",
|
|
708
|
+
"tinyspy",
|
|
709
|
+
"tinyrainbow",
|
|
710
|
+
"pathe",
|
|
711
|
+
"msw",
|
|
712
|
+
"msw/browser"
|
|
713
|
+
];
|
|
714
|
+
if (typeof project.config.diff === "string") {
|
|
715
|
+
entries.push(project.config.diff);
|
|
716
|
+
}
|
|
717
|
+
if (parentServer.vitest.coverageProvider) {
|
|
718
|
+
const coverage = parentServer.vitest.config.coverage;
|
|
719
|
+
const provider = coverage.provider;
|
|
720
|
+
if (provider === "v8") {
|
|
721
|
+
const path = tryResolve("@vitest/coverage-v8", [parentServer.config.root]);
|
|
722
|
+
if (path) {
|
|
723
|
+
entries.push(path);
|
|
724
|
+
exclude.push("@vitest/coverage-v8/browser");
|
|
725
|
+
}
|
|
726
|
+
} else if (provider === "istanbul") {
|
|
727
|
+
const path = tryResolve("@vitest/coverage-istanbul", [parentServer.config.root]);
|
|
728
|
+
if (path) {
|
|
729
|
+
entries.push(path);
|
|
730
|
+
exclude.push("@vitest/coverage-istanbul");
|
|
731
|
+
}
|
|
732
|
+
} else if (provider === "custom" && coverage.customProviderModule) {
|
|
733
|
+
entries.push(coverage.customProviderModule);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const include = [
|
|
737
|
+
"vitest > expect-type",
|
|
738
|
+
"vitest > @vitest/snapshot > magic-string",
|
|
739
|
+
"vitest > chai",
|
|
740
|
+
"vitest > chai > loupe",
|
|
741
|
+
"vitest > @vitest/utils > loupe",
|
|
742
|
+
"@vitest/browser > @testing-library/user-event",
|
|
743
|
+
"@vitest/browser > @testing-library/dom"
|
|
744
|
+
];
|
|
745
|
+
const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root;
|
|
746
|
+
const svelte = isPackageExists("vitest-browser-svelte", fileRoot);
|
|
747
|
+
if (svelte) {
|
|
748
|
+
exclude.push("vitest-browser-svelte");
|
|
749
|
+
}
|
|
750
|
+
const vue = isPackageExists("vitest-browser-vue", fileRoot);
|
|
751
|
+
if (vue) {
|
|
752
|
+
include.push("vitest-browser-vue", "vitest-browser-vue > @vue/test-utils", "vitest-browser-vue > @vue/test-utils > @vue/compiler-core");
|
|
753
|
+
}
|
|
754
|
+
const vueTestUtils = isPackageExists("@vue/test-utils", fileRoot);
|
|
755
|
+
if (vueTestUtils) {
|
|
756
|
+
include.push("@vue/test-utils");
|
|
757
|
+
}
|
|
758
|
+
return {
|
|
759
|
+
define,
|
|
760
|
+
resolve: { dedupe: ["vitest"] },
|
|
761
|
+
optimizeDeps: {
|
|
762
|
+
entries,
|
|
763
|
+
exclude,
|
|
764
|
+
include
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
},
|
|
768
|
+
async resolveId(id) {
|
|
769
|
+
if (!/\?browserv=\w+$/.test(id)) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
let useId = id.slice(0, id.lastIndexOf("?"));
|
|
773
|
+
if (useId.startsWith("/@fs/")) {
|
|
774
|
+
useId = useId.slice(5);
|
|
775
|
+
}
|
|
776
|
+
if (/^\w:/.test(useId)) {
|
|
777
|
+
useId = useId.replace(/\\/g, "/");
|
|
778
|
+
}
|
|
779
|
+
return useId;
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: "vitest:browser:resolve-virtual",
|
|
784
|
+
async resolveId(rawId) {
|
|
785
|
+
if (rawId === "/mockServiceWorker.js") {
|
|
786
|
+
return this.resolve("msw/mockServiceWorker.js", distRoot, { skipSelf: true });
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
name: "vitest:browser:assets",
|
|
792
|
+
configureServer(server) {
|
|
793
|
+
server.middlewares.use("/__vitest__", sirv(resolve(distRoot, "client/__vitest__")));
|
|
794
|
+
},
|
|
795
|
+
resolveId(id) {
|
|
796
|
+
if (id.startsWith("/__vitest_browser__/")) {
|
|
797
|
+
return resolve(distRoot, "client", id.slice(1));
|
|
798
|
+
}
|
|
799
|
+
},
|
|
800
|
+
transform(code, id) {
|
|
801
|
+
if (id.includes(parentServer.vite.config.cacheDir) && id.includes("loupe.js")) {
|
|
802
|
+
const utilRequire = "nodeUtil = require_util();";
|
|
803
|
+
return code.replace(utilRequire, " ".repeat(utilRequire.length));
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
BrowserContext(parentServer),
|
|
808
|
+
dynamicImportPlugin({
|
|
809
|
+
globalThisAccessor: "\"__vitest_browser_runner__\"",
|
|
810
|
+
filter(id) {
|
|
811
|
+
if (id.includes(distRoot)) {
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
816
|
+
}),
|
|
817
|
+
{
|
|
818
|
+
name: "vitest:browser:config",
|
|
819
|
+
enforce: "post",
|
|
820
|
+
async config(viteConfig) {
|
|
821
|
+
// Enables using ignore hint for coverage providers with @preserve keyword
|
|
822
|
+
if (viteConfig.esbuild !== false) {
|
|
823
|
+
viteConfig.esbuild ||= {};
|
|
824
|
+
viteConfig.esbuild.legalComments = "inline";
|
|
825
|
+
}
|
|
826
|
+
const defaultPort = parentServer.vitest._browserLastPort++;
|
|
827
|
+
const api = resolveApiServerConfig(viteConfig.test?.browser || {}, defaultPort);
|
|
828
|
+
viteConfig.server = {
|
|
829
|
+
...viteConfig.server,
|
|
830
|
+
port: defaultPort,
|
|
831
|
+
...api,
|
|
832
|
+
middlewareMode: false,
|
|
833
|
+
open: false
|
|
834
|
+
};
|
|
835
|
+
viteConfig.server.fs ??= {};
|
|
836
|
+
viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
|
|
837
|
+
viteConfig.server.fs.allow.push(...resolveFsAllow(parentServer.vitest.config.root, parentServer.vitest.vite.config.configFile), distRoot);
|
|
838
|
+
return { resolve: { alias: viteConfig.test?.alias } };
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
{
|
|
842
|
+
name: "vitest:browser:in-source-tests",
|
|
843
|
+
transform(code, id) {
|
|
844
|
+
const project = parentServer.vitest.getProjectByName(parentServer.config.name);
|
|
845
|
+
if (!project._isCachedTestFile(id) || !code.includes("import.meta.vitest")) {
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const s = new MagicString(code, { filename: cleanUrl(id) });
|
|
849
|
+
s.prepend(`import.meta.vitest = __vitest_index__;\n`);
|
|
850
|
+
return {
|
|
851
|
+
code: s.toString(),
|
|
852
|
+
map: s.generateMap({ hires: true })
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
name: "vitest:browser:worker",
|
|
858
|
+
transform(code, id, _options) {
|
|
859
|
+
if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
|
|
860
|
+
const s = new MagicString(code);
|
|
861
|
+
s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
|
|
862
|
+
return {
|
|
863
|
+
code: s.toString(),
|
|
864
|
+
map: s.generateMap({ hires: "boundary" })
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
name: "vitest:browser:transform-tester-html",
|
|
871
|
+
enforce: "pre",
|
|
872
|
+
async transformIndexHtml(html, ctx) {
|
|
873
|
+
const projectBrowser = [...parentServer.children].find((server) => {
|
|
874
|
+
return ctx.filename === server.testerFilepath;
|
|
875
|
+
});
|
|
876
|
+
if (!projectBrowser) {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (!parentServer.testerScripts) {
|
|
880
|
+
const testerScripts = await parentServer.formatScripts(parentServer.config.browser.testerScripts);
|
|
881
|
+
parentServer.testerScripts = testerScripts;
|
|
882
|
+
}
|
|
883
|
+
const stateJs = typeof parentServer.stateJs === "string" ? parentServer.stateJs : await parentServer.stateJs;
|
|
884
|
+
const testerTags = [];
|
|
885
|
+
const isDefaultTemplate = resolve(distRoot, "client/tester/tester.html") === projectBrowser.testerFilepath;
|
|
886
|
+
if (!isDefaultTemplate) {
|
|
887
|
+
const manifestContent = parentServer.manifest instanceof Promise ? await parentServer.manifest : parentServer.manifest;
|
|
888
|
+
const testerEntry = manifestContent["tester/tester.html"];
|
|
889
|
+
testerTags.push({
|
|
890
|
+
tag: "script",
|
|
891
|
+
attrs: {
|
|
892
|
+
type: "module",
|
|
893
|
+
crossorigin: "",
|
|
894
|
+
src: `${parentServer.base}${testerEntry.file}`
|
|
895
|
+
},
|
|
896
|
+
injectTo: "head"
|
|
897
|
+
});
|
|
898
|
+
for (const importName of testerEntry.imports || []) {
|
|
899
|
+
const entryManifest = manifestContent[importName];
|
|
900
|
+
if (entryManifest) {
|
|
901
|
+
testerTags.push({
|
|
902
|
+
tag: "link",
|
|
903
|
+
attrs: {
|
|
904
|
+
href: `${parentServer.base}${entryManifest.file}`,
|
|
905
|
+
rel: "modulepreload",
|
|
906
|
+
crossorigin: ""
|
|
907
|
+
},
|
|
908
|
+
injectTo: "head"
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
} else {
|
|
913
|
+
testerTags.push({
|
|
914
|
+
tag: "style",
|
|
915
|
+
children: `
|
|
984
916
|
html {
|
|
985
917
|
padding: 0;
|
|
986
918
|
margin: 0;
|
|
@@ -990,244 +922,268 @@ body {
|
|
|
990
922
|
margin: 0;
|
|
991
923
|
min-height: 100vh;
|
|
992
924
|
}`,
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
925
|
+
injectTo: "head"
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
return [
|
|
929
|
+
{
|
|
930
|
+
tag: "script",
|
|
931
|
+
children: "{__VITEST_INJECTOR__}",
|
|
932
|
+
injectTo: "head-prepend"
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
tag: "script",
|
|
936
|
+
children: stateJs,
|
|
937
|
+
injectTo: "head-prepend"
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
tag: "script",
|
|
941
|
+
attrs: {
|
|
942
|
+
type: "module",
|
|
943
|
+
src: parentServer.errorCatcherUrl
|
|
944
|
+
},
|
|
945
|
+
injectTo: "head"
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
tag: "script",
|
|
949
|
+
attrs: {
|
|
950
|
+
type: "module",
|
|
951
|
+
src: parentServer.matchersUrl
|
|
952
|
+
},
|
|
953
|
+
injectTo: "head"
|
|
954
|
+
},
|
|
955
|
+
parentServer.locatorsUrl ? {
|
|
956
|
+
tag: "script",
|
|
957
|
+
attrs: {
|
|
958
|
+
type: "module",
|
|
959
|
+
src: parentServer.locatorsUrl
|
|
960
|
+
},
|
|
961
|
+
injectTo: "head"
|
|
962
|
+
} : null,
|
|
963
|
+
...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
|
+
}
|
|
974
|
+
].filter((s) => s != null);
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
name: "vitest:browser:support-testing-library",
|
|
979
|
+
config() {
|
|
980
|
+
const rolldownPlugin = {
|
|
981
|
+
name: "vue-test-utils-rewrite",
|
|
982
|
+
resolveId: {
|
|
983
|
+
filter: { id: /^@vue\/(test-utils|compiler-core)$/ },
|
|
984
|
+
handler(source, importer) {
|
|
985
|
+
const resolved = getRequire().resolve(source, { paths: [importer] });
|
|
986
|
+
return resolved;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
const esbuildPlugin = {
|
|
991
|
+
name: "test-utils-rewrite",
|
|
992
|
+
setup(build) {
|
|
993
|
+
build.onResolve({ filter: /^@vue\/(test-utils|compiler-core)$/ }, (args) => {
|
|
994
|
+
const resolved = getRequire().resolve(args.path, { paths: [args.importer] });
|
|
995
|
+
return { path: resolved };
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
return { optimizeDeps: "rolldownVersion" in vite ? { rollupOptions: { plugins: [rolldownPlugin] } } : { esbuildOptions: { plugins: [esbuildPlugin] } } };
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
];
|
|
1062
1003
|
};
|
|
1063
1004
|
function tryResolve(path, paths) {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1005
|
+
try {
|
|
1006
|
+
const _require = getRequire();
|
|
1007
|
+
return _require.resolve(path, { paths });
|
|
1008
|
+
} catch {
|
|
1009
|
+
return undefined;
|
|
1010
|
+
}
|
|
1070
1011
|
}
|
|
1071
1012
|
let _require;
|
|
1072
1013
|
function getRequire() {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1014
|
+
if (!_require) {
|
|
1015
|
+
_require = createRequire(import.meta.url);
|
|
1016
|
+
}
|
|
1017
|
+
return _require;
|
|
1077
1018
|
}
|
|
1078
1019
|
function resolveCoverageFolder(vitest) {
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return [root, `/${basename(root)}/`];
|
|
1096
|
-
}
|
|
1097
|
-
return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
|
|
1020
|
+
const options = vitest.config;
|
|
1021
|
+
const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
|
|
1022
|
+
if (typeof reporter === "string") {
|
|
1023
|
+
return reporter === "html";
|
|
1024
|
+
}
|
|
1025
|
+
return reporter[0] === "html";
|
|
1026
|
+
}) : undefined;
|
|
1027
|
+
if (!htmlReporter) {
|
|
1028
|
+
return undefined;
|
|
1029
|
+
}
|
|
1030
|
+
const root = resolve(options.root || process.cwd(), options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory);
|
|
1031
|
+
const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : undefined;
|
|
1032
|
+
if (!subdir || typeof subdir !== "string") {
|
|
1033
|
+
return [root, `/${basename(root)}/`];
|
|
1034
|
+
}
|
|
1035
|
+
return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
|
|
1098
1036
|
}
|
|
1099
1037
|
const postfixRE = /[?#].*$/;
|
|
1100
1038
|
function cleanUrl(url) {
|
|
1101
|
-
|
|
1039
|
+
return url.replace(postfixRE, "");
|
|
1102
1040
|
}
|
|
1103
1041
|
|
|
1104
1042
|
class BrowserServerCDPHandler {
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
once(event, listener) {
|
|
1143
|
-
this.on(event, listener, true);
|
|
1144
|
-
}
|
|
1043
|
+
listenerIds = {};
|
|
1044
|
+
listeners = {};
|
|
1045
|
+
constructor(session, tester) {
|
|
1046
|
+
this.session = session;
|
|
1047
|
+
this.tester = tester;
|
|
1048
|
+
}
|
|
1049
|
+
send(method, params) {
|
|
1050
|
+
return this.session.send(method, params);
|
|
1051
|
+
}
|
|
1052
|
+
on(event, id, once = false) {
|
|
1053
|
+
if (!this.listenerIds[event]) {
|
|
1054
|
+
this.listenerIds[event] = [];
|
|
1055
|
+
}
|
|
1056
|
+
this.listenerIds[event].push(id);
|
|
1057
|
+
if (!this.listeners[event]) {
|
|
1058
|
+
this.listeners[event] = (payload) => {
|
|
1059
|
+
this.tester.cdpEvent(event, payload);
|
|
1060
|
+
if (once) {
|
|
1061
|
+
this.off(event, id);
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
this.session.on(event, this.listeners[event]);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
off(event, id) {
|
|
1068
|
+
if (!this.listenerIds[event]) {
|
|
1069
|
+
this.listenerIds[event] = [];
|
|
1070
|
+
}
|
|
1071
|
+
this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
|
|
1072
|
+
if (!this.listenerIds[event].length) {
|
|
1073
|
+
this.session.off(event, this.listeners[event]);
|
|
1074
|
+
delete this.listeners[event];
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
once(event, listener) {
|
|
1078
|
+
this.on(event, listener, true);
|
|
1079
|
+
}
|
|
1145
1080
|
}
|
|
1146
1081
|
|
|
1147
1082
|
const clear = async (context, selector) => {
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1083
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1084
|
+
const { iframe } = context;
|
|
1085
|
+
const element = iframe.locator(selector);
|
|
1086
|
+
await element.clear();
|
|
1087
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1088
|
+
const browser = context.browser;
|
|
1089
|
+
const element = await browser.$(selector);
|
|
1090
|
+
await element.clearValue();
|
|
1091
|
+
} else {
|
|
1092
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
|
|
1093
|
+
}
|
|
1159
1094
|
};
|
|
1160
1095
|
|
|
1161
1096
|
const click = async (context, selector, options = {}) => {
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1097
|
+
const provider = context.provider;
|
|
1098
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
1099
|
+
const tester = context.iframe;
|
|
1100
|
+
await tester.locator(selector).click(options);
|
|
1101
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
1102
|
+
const browser = context.browser;
|
|
1103
|
+
await browser.$(selector).click(options);
|
|
1104
|
+
} else {
|
|
1105
|
+
throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
|
|
1106
|
+
}
|
|
1172
1107
|
};
|
|
1173
1108
|
const dblClick = async (context, selector, options = {}) => {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1109
|
+
const provider = context.provider;
|
|
1110
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
1111
|
+
const tester = context.iframe;
|
|
1112
|
+
await tester.locator(selector).dblclick(options);
|
|
1113
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
1114
|
+
const browser = context.browser;
|
|
1115
|
+
await browser.$(selector).doubleClick();
|
|
1116
|
+
} else {
|
|
1117
|
+
throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
|
|
1118
|
+
}
|
|
1184
1119
|
};
|
|
1185
1120
|
const tripleClick = async (context, selector, options = {}) => {
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1121
|
+
const provider = context.provider;
|
|
1122
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
1123
|
+
const tester = context.iframe;
|
|
1124
|
+
await tester.locator(selector).click({
|
|
1125
|
+
...options,
|
|
1126
|
+
clickCount: 3
|
|
1127
|
+
});
|
|
1128
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
1129
|
+
const browser = context.browser;
|
|
1130
|
+
await browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: browser.$(selector) }).down().up().pause(50).down().up().pause(50).down().up().pause(50).perform();
|
|
1131
|
+
} else {
|
|
1132
|
+
throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
|
|
1133
|
+
}
|
|
1199
1134
|
};
|
|
1200
1135
|
|
|
1201
1136
|
const dragAndDrop = async (context, source, target, options_) => {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1137
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1138
|
+
const frame = await context.frame();
|
|
1139
|
+
await frame.dragAndDrop(source, target, options_);
|
|
1140
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1141
|
+
const $source = context.browser.$(source);
|
|
1142
|
+
const $target = context.browser.$(target);
|
|
1143
|
+
const options = options_ || {};
|
|
1144
|
+
const duration = options.duration ?? 10;
|
|
1145
|
+
await context.browser.action("pointer").move({
|
|
1146
|
+
duration: 0,
|
|
1147
|
+
origin: $source,
|
|
1148
|
+
x: options.sourceX ?? 0,
|
|
1149
|
+
y: options.sourceY ?? 0
|
|
1150
|
+
}).down({ button: 0 }).move({
|
|
1151
|
+
duration: 0,
|
|
1152
|
+
origin: "pointer",
|
|
1153
|
+
x: 0,
|
|
1154
|
+
y: 0
|
|
1155
|
+
}).pause(duration).move({
|
|
1156
|
+
duration: 0,
|
|
1157
|
+
origin: $target,
|
|
1158
|
+
x: options.targetX ?? 0,
|
|
1159
|
+
y: options.targetY ?? 0
|
|
1160
|
+
}).move({
|
|
1161
|
+
duration: 0,
|
|
1162
|
+
origin: "pointer",
|
|
1163
|
+
x: 1,
|
|
1164
|
+
y: 0
|
|
1165
|
+
}).move({
|
|
1166
|
+
duration: 0,
|
|
1167
|
+
origin: "pointer",
|
|
1168
|
+
x: -1,
|
|
1169
|
+
y: 0
|
|
1170
|
+
}).up({ button: 0 }).perform();
|
|
1171
|
+
} else {
|
|
1172
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support dragging elements`);
|
|
1173
|
+
}
|
|
1218
1174
|
};
|
|
1219
1175
|
|
|
1220
1176
|
const fill = async (context, selector, text, options = {}) => {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1177
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1178
|
+
const { iframe } = context;
|
|
1179
|
+
const element = iframe.locator(selector);
|
|
1180
|
+
await element.fill(text, options);
|
|
1181
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1182
|
+
const browser = context.browser;
|
|
1183
|
+
await browser.$(selector).setValue(text);
|
|
1184
|
+
} else {
|
|
1185
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support filling inputs`);
|
|
1186
|
+
}
|
|
1231
1187
|
};
|
|
1232
1188
|
|
|
1233
1189
|
const types = {
|
|
@@ -1671,54 +1627,52 @@ _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _M
|
|
|
1671
1627
|
var mime = new Mime(types)._freeze();
|
|
1672
1628
|
|
|
1673
1629
|
function assertFileAccess(path, project) {
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
);
|
|
1678
|
-
}
|
|
1630
|
+
if (!isFileServingAllowed(path, project.vite) && !isFileServingAllowed(path, project.vitest.server)) {
|
|
1631
|
+
throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
|
|
1632
|
+
}
|
|
1679
1633
|
}
|
|
1680
1634
|
const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1635
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1636
|
+
assertFileAccess(filepath, project);
|
|
1637
|
+
if (typeof options === "object" && !options.encoding) {
|
|
1638
|
+
options.encoding = "utf-8";
|
|
1639
|
+
}
|
|
1640
|
+
return promises.readFile(filepath, options);
|
|
1687
1641
|
};
|
|
1688
1642
|
const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1643
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1644
|
+
assertFileAccess(filepath, project);
|
|
1645
|
+
const dir = dirname$1(filepath);
|
|
1646
|
+
if (!fs.existsSync(dir)) {
|
|
1647
|
+
await promises.mkdir(dir, { recursive: true });
|
|
1648
|
+
}
|
|
1649
|
+
await promises.writeFile(filepath, data, options);
|
|
1696
1650
|
};
|
|
1697
1651
|
const removeFile = async ({ project, testPath = process.cwd() }, path) => {
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1652
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1653
|
+
assertFileAccess(filepath, project);
|
|
1654
|
+
await promises.rm(filepath);
|
|
1701
1655
|
};
|
|
1702
1656
|
const _fileInfo = async ({ project, testPath = process.cwd() }, path, encoding) => {
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1657
|
+
const filepath = resolve$1(dirname$1(testPath), path);
|
|
1658
|
+
assertFileAccess(filepath, project);
|
|
1659
|
+
const content = await promises.readFile(filepath, encoding || "base64");
|
|
1660
|
+
return {
|
|
1661
|
+
content,
|
|
1662
|
+
basename: basename$1(filepath),
|
|
1663
|
+
mime: mime.getType(filepath)
|
|
1664
|
+
};
|
|
1711
1665
|
};
|
|
1712
1666
|
|
|
1713
1667
|
const hover = async (context, selector, options = {}) => {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1668
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1669
|
+
await context.iframe.locator(selector).hover(options);
|
|
1670
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1671
|
+
const browser = context.browser;
|
|
1672
|
+
await browser.$(selector).moveTo(options);
|
|
1673
|
+
} else {
|
|
1674
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
|
|
1675
|
+
}
|
|
1722
1676
|
};
|
|
1723
1677
|
|
|
1724
1678
|
var DOM_KEY_LOCATION = /*#__PURE__*/ function(DOM_KEY_LOCATION) {
|
|
@@ -2031,648 +1985,800 @@ function getErrorMessage(expected, found, text, context) {
|
|
|
2031
1985
|
}
|
|
2032
1986
|
|
|
2033
1987
|
const keyboard = async (context, text, state) => {
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
} else {
|
|
2053
|
-
throw new TypeError(`Provider "${context.provider.name}" does not support selecting all text`);
|
|
2054
|
-
}
|
|
2055
|
-
},
|
|
2056
|
-
true
|
|
2057
|
-
);
|
|
2058
|
-
return {
|
|
2059
|
-
unreleased: Array.from(pressed)
|
|
2060
|
-
};
|
|
1988
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1989
|
+
const frame = await context.frame();
|
|
1990
|
+
await frame.evaluate(focusIframe);
|
|
1991
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
1992
|
+
await context.browser.execute(focusIframe);
|
|
1993
|
+
}
|
|
1994
|
+
const pressed = new Set(state.unreleased);
|
|
1995
|
+
await keyboardImplementation(pressed, context.provider, context.sessionId, text, async () => {
|
|
1996
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
1997
|
+
const frame = await context.frame();
|
|
1998
|
+
await frame.evaluate(selectAll);
|
|
1999
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
2000
|
+
await context.browser.execute(selectAll);
|
|
2001
|
+
} else {
|
|
2002
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support selecting all text`);
|
|
2003
|
+
}
|
|
2004
|
+
}, true);
|
|
2005
|
+
return { unreleased: Array.from(pressed) };
|
|
2061
2006
|
};
|
|
2062
2007
|
const keyboardCleanup = async (context, state) => {
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2008
|
+
const { provider, sessionId } = context;
|
|
2009
|
+
if (!state.unreleased) {
|
|
2010
|
+
return;
|
|
2011
|
+
}
|
|
2012
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
2013
|
+
const page = provider.getPage(sessionId);
|
|
2014
|
+
for (const key of state.unreleased) {
|
|
2015
|
+
await page.keyboard.up(key);
|
|
2016
|
+
}
|
|
2017
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
2018
|
+
const keyboard = provider.browser.action("key");
|
|
2019
|
+
for (const key of state.unreleased) {
|
|
2020
|
+
keyboard.up(key);
|
|
2021
|
+
}
|
|
2022
|
+
await keyboard.perform();
|
|
2023
|
+
} else {
|
|
2024
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support keyboard api`);
|
|
2025
|
+
}
|
|
2081
2026
|
};
|
|
2082
|
-
const VALID_KEYS =
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2027
|
+
const VALID_KEYS = new Set([
|
|
2028
|
+
"Escape",
|
|
2029
|
+
"F1",
|
|
2030
|
+
"F2",
|
|
2031
|
+
"F3",
|
|
2032
|
+
"F4",
|
|
2033
|
+
"F5",
|
|
2034
|
+
"F6",
|
|
2035
|
+
"F7",
|
|
2036
|
+
"F8",
|
|
2037
|
+
"F9",
|
|
2038
|
+
"F10",
|
|
2039
|
+
"F11",
|
|
2040
|
+
"F12",
|
|
2041
|
+
"Backquote",
|
|
2042
|
+
"`",
|
|
2043
|
+
"~",
|
|
2044
|
+
"Digit1",
|
|
2045
|
+
"1",
|
|
2046
|
+
"!",
|
|
2047
|
+
"Digit2",
|
|
2048
|
+
"2",
|
|
2049
|
+
"@",
|
|
2050
|
+
"Digit3",
|
|
2051
|
+
"3",
|
|
2052
|
+
"#",
|
|
2053
|
+
"Digit4",
|
|
2054
|
+
"4",
|
|
2055
|
+
"$",
|
|
2056
|
+
"Digit5",
|
|
2057
|
+
"5",
|
|
2058
|
+
"%",
|
|
2059
|
+
"Digit6",
|
|
2060
|
+
"6",
|
|
2061
|
+
"^",
|
|
2062
|
+
"Digit7",
|
|
2063
|
+
"7",
|
|
2064
|
+
"&",
|
|
2065
|
+
"Digit8",
|
|
2066
|
+
"8",
|
|
2067
|
+
"*",
|
|
2068
|
+
"Digit9",
|
|
2069
|
+
"9",
|
|
2070
|
+
"(",
|
|
2071
|
+
"Digit0",
|
|
2072
|
+
"0",
|
|
2073
|
+
")",
|
|
2074
|
+
"Minus",
|
|
2075
|
+
"-",
|
|
2076
|
+
"_",
|
|
2077
|
+
"Equal",
|
|
2078
|
+
"=",
|
|
2079
|
+
"+",
|
|
2080
|
+
"Backslash",
|
|
2081
|
+
"\\",
|
|
2082
|
+
"|",
|
|
2083
|
+
"Backspace",
|
|
2084
|
+
"Tab",
|
|
2085
|
+
"KeyQ",
|
|
2086
|
+
"q",
|
|
2087
|
+
"Q",
|
|
2088
|
+
"KeyW",
|
|
2089
|
+
"w",
|
|
2090
|
+
"W",
|
|
2091
|
+
"KeyE",
|
|
2092
|
+
"e",
|
|
2093
|
+
"E",
|
|
2094
|
+
"KeyR",
|
|
2095
|
+
"r",
|
|
2096
|
+
"R",
|
|
2097
|
+
"KeyT",
|
|
2098
|
+
"t",
|
|
2099
|
+
"T",
|
|
2100
|
+
"KeyY",
|
|
2101
|
+
"y",
|
|
2102
|
+
"Y",
|
|
2103
|
+
"KeyU",
|
|
2104
|
+
"u",
|
|
2105
|
+
"U",
|
|
2106
|
+
"KeyI",
|
|
2107
|
+
"i",
|
|
2108
|
+
"I",
|
|
2109
|
+
"KeyO",
|
|
2110
|
+
"o",
|
|
2111
|
+
"O",
|
|
2112
|
+
"KeyP",
|
|
2113
|
+
"p",
|
|
2114
|
+
"P",
|
|
2115
|
+
"BracketLeft",
|
|
2116
|
+
"[",
|
|
2117
|
+
"{",
|
|
2118
|
+
"BracketRight",
|
|
2119
|
+
"]",
|
|
2120
|
+
"}",
|
|
2121
|
+
"CapsLock",
|
|
2122
|
+
"KeyA",
|
|
2123
|
+
"a",
|
|
2124
|
+
"A",
|
|
2125
|
+
"KeyS",
|
|
2126
|
+
"s",
|
|
2127
|
+
"S",
|
|
2128
|
+
"KeyD",
|
|
2129
|
+
"d",
|
|
2130
|
+
"D",
|
|
2131
|
+
"KeyF",
|
|
2132
|
+
"f",
|
|
2133
|
+
"F",
|
|
2134
|
+
"KeyG",
|
|
2135
|
+
"g",
|
|
2136
|
+
"G",
|
|
2137
|
+
"KeyH",
|
|
2138
|
+
"h",
|
|
2139
|
+
"H",
|
|
2140
|
+
"KeyJ",
|
|
2141
|
+
"j",
|
|
2142
|
+
"J",
|
|
2143
|
+
"KeyK",
|
|
2144
|
+
"k",
|
|
2145
|
+
"K",
|
|
2146
|
+
"KeyL",
|
|
2147
|
+
"l",
|
|
2148
|
+
"L",
|
|
2149
|
+
"Semicolon",
|
|
2150
|
+
";",
|
|
2151
|
+
":",
|
|
2152
|
+
"Quote",
|
|
2153
|
+
"'",
|
|
2154
|
+
"\"",
|
|
2155
|
+
"Enter",
|
|
2156
|
+
"\n",
|
|
2157
|
+
"\r",
|
|
2158
|
+
"ShiftLeft",
|
|
2159
|
+
"Shift",
|
|
2160
|
+
"KeyZ",
|
|
2161
|
+
"z",
|
|
2162
|
+
"Z",
|
|
2163
|
+
"KeyX",
|
|
2164
|
+
"x",
|
|
2165
|
+
"X",
|
|
2166
|
+
"KeyC",
|
|
2167
|
+
"c",
|
|
2168
|
+
"C",
|
|
2169
|
+
"KeyV",
|
|
2170
|
+
"v",
|
|
2171
|
+
"V",
|
|
2172
|
+
"KeyB",
|
|
2173
|
+
"b",
|
|
2174
|
+
"B",
|
|
2175
|
+
"KeyN",
|
|
2176
|
+
"n",
|
|
2177
|
+
"N",
|
|
2178
|
+
"KeyM",
|
|
2179
|
+
"m",
|
|
2180
|
+
"M",
|
|
2181
|
+
"Comma",
|
|
2182
|
+
",",
|
|
2183
|
+
"<",
|
|
2184
|
+
"Period",
|
|
2185
|
+
".",
|
|
2186
|
+
">",
|
|
2187
|
+
"Slash",
|
|
2188
|
+
"/",
|
|
2189
|
+
"?",
|
|
2190
|
+
"ShiftRight",
|
|
2191
|
+
"ControlLeft",
|
|
2192
|
+
"Control",
|
|
2193
|
+
"MetaLeft",
|
|
2194
|
+
"Meta",
|
|
2195
|
+
"AltLeft",
|
|
2196
|
+
"Alt",
|
|
2197
|
+
"Space",
|
|
2198
|
+
" ",
|
|
2199
|
+
"AltRight",
|
|
2200
|
+
"AltGraph",
|
|
2201
|
+
"MetaRight",
|
|
2202
|
+
"ContextMenu",
|
|
2203
|
+
"ControlRight",
|
|
2204
|
+
"PrintScreen",
|
|
2205
|
+
"ScrollLock",
|
|
2206
|
+
"Pause",
|
|
2207
|
+
"PageUp",
|
|
2208
|
+
"PageDown",
|
|
2209
|
+
"Insert",
|
|
2210
|
+
"Delete",
|
|
2211
|
+
"Home",
|
|
2212
|
+
"End",
|
|
2213
|
+
"ArrowLeft",
|
|
2214
|
+
"ArrowUp",
|
|
2215
|
+
"ArrowRight",
|
|
2216
|
+
"ArrowDown",
|
|
2217
|
+
"NumLock",
|
|
2218
|
+
"NumpadDivide",
|
|
2219
|
+
"NumpadMultiply",
|
|
2220
|
+
"NumpadSubtract",
|
|
2221
|
+
"Numpad7",
|
|
2222
|
+
"Numpad8",
|
|
2223
|
+
"Numpad9",
|
|
2224
|
+
"Numpad4",
|
|
2225
|
+
"Numpad5",
|
|
2226
|
+
"Numpad6",
|
|
2227
|
+
"NumpadAdd",
|
|
2228
|
+
"Numpad1",
|
|
2229
|
+
"Numpad2",
|
|
2230
|
+
"Numpad3",
|
|
2231
|
+
"Numpad0",
|
|
2232
|
+
"NumpadDecimal",
|
|
2233
|
+
"NumpadEnter",
|
|
2234
|
+
"ControlOrMeta"
|
|
2235
|
+
]);
|
|
2236
|
+
async function keyboardImplementation(pressed, provider, sessionId, text, selectAll, skipRelease) {
|
|
2237
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
2238
|
+
const page = provider.getPage(sessionId);
|
|
2239
|
+
const actions = parseKeyDef(defaultKeyMap, text);
|
|
2240
|
+
for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
|
|
2241
|
+
const key = keyDef.key;
|
|
2242
|
+
if (pressed.has(key)) {
|
|
2243
|
+
if (VALID_KEYS.has(key)) {
|
|
2244
|
+
await page.keyboard.up(key);
|
|
2245
|
+
}
|
|
2246
|
+
pressed.delete(key);
|
|
2247
|
+
}
|
|
2248
|
+
if (!releasePrevious) {
|
|
2249
|
+
if (key === "selectall") {
|
|
2250
|
+
await selectAll();
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2253
|
+
for (let i = 1; i <= repeat; i++) {
|
|
2254
|
+
if (VALID_KEYS.has(key)) {
|
|
2255
|
+
await page.keyboard.down(key);
|
|
2256
|
+
} else {
|
|
2257
|
+
await page.keyboard.insertText(key);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
if (releaseSelf) {
|
|
2261
|
+
if (VALID_KEYS.has(key)) {
|
|
2262
|
+
await page.keyboard.up(key);
|
|
2263
|
+
}
|
|
2264
|
+
} else {
|
|
2265
|
+
pressed.add(key);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
if (!skipRelease && pressed.size) {
|
|
2270
|
+
for (const key of pressed) {
|
|
2271
|
+
if (VALID_KEYS.has(key)) {
|
|
2272
|
+
await page.keyboard.up(key);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
} else if (provider instanceof WebdriverBrowserProvider) {
|
|
2277
|
+
const { Key } = await import('webdriverio');
|
|
2278
|
+
const browser = provider.browser;
|
|
2279
|
+
const actions = parseKeyDef(defaultKeyMap, text);
|
|
2280
|
+
let keyboard = browser.action("key");
|
|
2281
|
+
for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
|
|
2282
|
+
let key = keyDef.key;
|
|
2283
|
+
const special = Key[key];
|
|
2284
|
+
if (special) {
|
|
2285
|
+
key = special;
|
|
2286
|
+
}
|
|
2287
|
+
if (pressed.has(key)) {
|
|
2288
|
+
keyboard.up(key);
|
|
2289
|
+
pressed.delete(key);
|
|
2290
|
+
}
|
|
2291
|
+
if (!releasePrevious) {
|
|
2292
|
+
if (key === "selectall") {
|
|
2293
|
+
await keyboard.perform();
|
|
2294
|
+
keyboard = browser.action("key");
|
|
2295
|
+
await selectAll();
|
|
2296
|
+
continue;
|
|
2297
|
+
}
|
|
2298
|
+
for (let i = 1; i <= repeat; i++) {
|
|
2299
|
+
keyboard.down(key);
|
|
2300
|
+
}
|
|
2301
|
+
if (releaseSelf) {
|
|
2302
|
+
keyboard.up(key);
|
|
2303
|
+
} else {
|
|
2304
|
+
pressed.add(key);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
const allRelease = keyboard.toJSON().actions.every((action) => action.type === "keyUp");
|
|
2309
|
+
await keyboard.perform(allRelease ? false : skipRelease);
|
|
2310
|
+
}
|
|
2311
|
+
return { pressed };
|
|
2161
2312
|
}
|
|
2162
2313
|
function focusIframe() {
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2314
|
+
if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
|
|
2315
|
+
window.focus();
|
|
2316
|
+
}
|
|
2166
2317
|
}
|
|
2167
2318
|
function selectAll() {
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2319
|
+
const element = document.activeElement;
|
|
2320
|
+
if (element && typeof element.select === "function") {
|
|
2321
|
+
element.select();
|
|
2322
|
+
}
|
|
2172
2323
|
}
|
|
2173
2324
|
|
|
2174
2325
|
const screenshot = async (context, name, options = {}) => {
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
const buffer = await element.saveScreenshot(savePath);
|
|
2210
|
-
return returnResult(options, path, buffer);
|
|
2211
|
-
}
|
|
2212
|
-
throw new Error(
|
|
2213
|
-
`Provider "${context.provider.name}" does not support screenshots`
|
|
2214
|
-
);
|
|
2326
|
+
if (!context.testPath) {
|
|
2327
|
+
throw new Error(`Cannot take a screenshot without a test path`);
|
|
2328
|
+
}
|
|
2329
|
+
const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(context.testPath, name, context.project.config);
|
|
2330
|
+
const savePath = normalize$1(path);
|
|
2331
|
+
await mkdir(dirname(path), { recursive: true });
|
|
2332
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
2333
|
+
if (options.element) {
|
|
2334
|
+
const { element: selector,...config } = options;
|
|
2335
|
+
const element = context.iframe.locator(`${selector}`);
|
|
2336
|
+
const buffer = await element.screenshot({
|
|
2337
|
+
...config,
|
|
2338
|
+
path: savePath
|
|
2339
|
+
});
|
|
2340
|
+
return returnResult(options, path, buffer);
|
|
2341
|
+
}
|
|
2342
|
+
const buffer = await context.iframe.locator("body").screenshot({
|
|
2343
|
+
...options,
|
|
2344
|
+
path: savePath
|
|
2345
|
+
});
|
|
2346
|
+
return returnResult(options, path, buffer);
|
|
2347
|
+
}
|
|
2348
|
+
if (context.provider instanceof WebdriverBrowserProvider) {
|
|
2349
|
+
const page = context.provider.browser;
|
|
2350
|
+
if (!options.element) {
|
|
2351
|
+
const body = await page.$("body");
|
|
2352
|
+
const buffer = await body.saveScreenshot(savePath);
|
|
2353
|
+
return returnResult(options, path, buffer);
|
|
2354
|
+
}
|
|
2355
|
+
const element = await page.$(`${options.element}`);
|
|
2356
|
+
const buffer = await element.saveScreenshot(savePath);
|
|
2357
|
+
return returnResult(options, path, buffer);
|
|
2358
|
+
}
|
|
2359
|
+
throw new Error(`Provider "${context.provider.name}" does not support screenshots`);
|
|
2215
2360
|
};
|
|
2216
2361
|
function resolveScreenshotPath(testPath, name, config) {
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
base,
|
|
2224
|
-
name
|
|
2225
|
-
);
|
|
2226
|
-
}
|
|
2227
|
-
return resolve(dir, "__screenshots__", base, name);
|
|
2362
|
+
const dir = dirname(testPath);
|
|
2363
|
+
const base = basename(testPath);
|
|
2364
|
+
if (config.browser.screenshotDirectory) {
|
|
2365
|
+
return resolve(config.browser.screenshotDirectory, relative(config.root, dir), base, name);
|
|
2366
|
+
}
|
|
2367
|
+
return resolve(dir, "__screenshots__", base, name);
|
|
2228
2368
|
}
|
|
2229
2369
|
function returnResult(options, path, buffer) {
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2370
|
+
if (options.base64) {
|
|
2371
|
+
return {
|
|
2372
|
+
path,
|
|
2373
|
+
base64: buffer.toString("base64")
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
return path;
|
|
2234
2377
|
}
|
|
2235
2378
|
|
|
2236
2379
|
const selectOptions = async (context, selector, userValues, options = {}) => {
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2380
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
2381
|
+
const value = userValues;
|
|
2382
|
+
const { iframe } = context;
|
|
2383
|
+
const selectElement = iframe.locator(selector);
|
|
2384
|
+
const values = await Promise.all(value.map(async (v) => {
|
|
2385
|
+
if (typeof v === "string") {
|
|
2386
|
+
return v;
|
|
2387
|
+
}
|
|
2388
|
+
const elementHandler = await iframe.locator(v.element).elementHandle();
|
|
2389
|
+
if (!elementHandler) {
|
|
2390
|
+
throw new Error(`Element not found: ${v.element}`);
|
|
2391
|
+
}
|
|
2392
|
+
return elementHandler;
|
|
2393
|
+
}));
|
|
2394
|
+
await selectElement.selectOption(values, options);
|
|
2395
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
2396
|
+
const values = userValues;
|
|
2397
|
+
if (!values.length) {
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
const browser = context.browser;
|
|
2401
|
+
if (values.length === 1 && "index" in values[0]) {
|
|
2402
|
+
const selectElement = browser.$(selector);
|
|
2403
|
+
await selectElement.selectByIndex(values[0].index);
|
|
2404
|
+
} else {
|
|
2405
|
+
throw new Error("Provider \"webdriverio\" doesn't support selecting multiple values at once");
|
|
2406
|
+
}
|
|
2407
|
+
} else {
|
|
2408
|
+
throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
|
|
2409
|
+
}
|
|
2267
2410
|
};
|
|
2268
2411
|
|
|
2269
2412
|
const tab = async (context, options = {}) => {
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2413
|
+
const provider = context.provider;
|
|
2414
|
+
if (provider instanceof PlaywrightBrowserProvider) {
|
|
2415
|
+
const page = context.page;
|
|
2416
|
+
await page.keyboard.press(options.shift === true ? "Shift+Tab" : "Tab");
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
if (provider instanceof WebdriverBrowserProvider) {
|
|
2420
|
+
const { Key } = await import('webdriverio');
|
|
2421
|
+
const browser = context.browser;
|
|
2422
|
+
await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab]);
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
throw new Error(`Provider "${provider.name}" doesn't support tab command`);
|
|
2283
2426
|
};
|
|
2284
2427
|
|
|
2285
2428
|
const type = async (context, selector, text, options = {}) => {
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
context.sessionId,
|
|
2312
|
-
text,
|
|
2313
|
-
() => browser.execute(() => {
|
|
2314
|
-
const element2 = document.activeElement;
|
|
2315
|
-
if (element2 && typeof element2.select === "function") {
|
|
2316
|
-
element2.select();
|
|
2317
|
-
}
|
|
2318
|
-
}),
|
|
2319
|
-
skipAutoClose
|
|
2320
|
-
);
|
|
2321
|
-
} else {
|
|
2322
|
-
throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
|
|
2323
|
-
}
|
|
2324
|
-
return {
|
|
2325
|
-
unreleased: Array.from(unreleased)
|
|
2326
|
-
};
|
|
2429
|
+
const { skipClick = false, skipAutoClose = false } = options;
|
|
2430
|
+
const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
|
|
2431
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
2432
|
+
const { iframe } = context;
|
|
2433
|
+
const element = iframe.locator(selector);
|
|
2434
|
+
if (!skipClick) {
|
|
2435
|
+
await element.focus();
|
|
2436
|
+
}
|
|
2437
|
+
await keyboardImplementation(unreleased, context.provider, context.sessionId, text, () => element.selectText(), skipAutoClose);
|
|
2438
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
2439
|
+
const browser = context.browser;
|
|
2440
|
+
const element = browser.$(selector);
|
|
2441
|
+
if (!skipClick && !await element.isFocused()) {
|
|
2442
|
+
await element.click();
|
|
2443
|
+
}
|
|
2444
|
+
await keyboardImplementation(unreleased, context.provider, context.sessionId, text, () => browser.execute(() => {
|
|
2445
|
+
const element = document.activeElement;
|
|
2446
|
+
if (element && typeof element.select === "function") {
|
|
2447
|
+
element.select();
|
|
2448
|
+
}
|
|
2449
|
+
}), skipAutoClose);
|
|
2450
|
+
} else {
|
|
2451
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
|
|
2452
|
+
}
|
|
2453
|
+
return { unreleased: Array.from(unreleased) };
|
|
2327
2454
|
};
|
|
2328
2455
|
|
|
2329
2456
|
const upload = async (context, selector, files) => {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2457
|
+
const testPath = context.testPath;
|
|
2458
|
+
if (!testPath) {
|
|
2459
|
+
throw new Error(`Cannot upload files outside of a test`);
|
|
2460
|
+
}
|
|
2461
|
+
const testDir = dirname(testPath);
|
|
2462
|
+
if (context.provider instanceof PlaywrightBrowserProvider) {
|
|
2463
|
+
const { iframe } = context;
|
|
2464
|
+
const playwrightFiles = files.map((file) => {
|
|
2465
|
+
if (typeof file === "string") {
|
|
2466
|
+
return resolve(testDir, file);
|
|
2467
|
+
}
|
|
2468
|
+
return {
|
|
2469
|
+
name: file.name,
|
|
2470
|
+
mimeType: file.mimeType,
|
|
2471
|
+
buffer: Buffer.from(file.base64, "base64")
|
|
2472
|
+
};
|
|
2473
|
+
});
|
|
2474
|
+
await iframe.locator(selector).setInputFiles(playwrightFiles);
|
|
2475
|
+
} else if (context.provider instanceof WebdriverBrowserProvider) {
|
|
2476
|
+
for (const file of files) {
|
|
2477
|
+
if (typeof file !== "string") {
|
|
2478
|
+
throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
const element = context.browser.$(selector);
|
|
2482
|
+
for (const file of files) {
|
|
2483
|
+
const filepath = resolve(testDir, file);
|
|
2484
|
+
const remoteFilePath = await context.browser.uploadFile(filepath);
|
|
2485
|
+
await element.addValue(remoteFilePath);
|
|
2486
|
+
}
|
|
2487
|
+
} else {
|
|
2488
|
+
throw new TypeError(`Provider "${context.provider.name}" does not support uploading files via userEvent.upload`);
|
|
2489
|
+
}
|
|
2363
2490
|
};
|
|
2364
2491
|
|
|
2365
2492
|
var builtinCommands = {
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2493
|
+
readFile,
|
|
2494
|
+
removeFile,
|
|
2495
|
+
writeFile,
|
|
2496
|
+
__vitest_fileInfo: _fileInfo,
|
|
2497
|
+
__vitest_upload: upload,
|
|
2498
|
+
__vitest_click: click,
|
|
2499
|
+
__vitest_dblClick: dblClick,
|
|
2500
|
+
__vitest_tripleClick: tripleClick,
|
|
2501
|
+
__vitest_screenshot: screenshot,
|
|
2502
|
+
__vitest_type: type,
|
|
2503
|
+
__vitest_clear: clear,
|
|
2504
|
+
__vitest_fill: fill,
|
|
2505
|
+
__vitest_tab: tab,
|
|
2506
|
+
__vitest_keyboard: keyboard,
|
|
2507
|
+
__vitest_selectOptions: selectOptions,
|
|
2508
|
+
__vitest_dragAndDrop: dragAndDrop,
|
|
2509
|
+
__vitest_hover: hover,
|
|
2510
|
+
__vitest_cleanup: keyboardCleanup
|
|
2384
2511
|
};
|
|
2385
2512
|
|
|
2386
2513
|
class BrowserServerState {
|
|
2387
|
-
|
|
2388
|
-
|
|
2514
|
+
orchestrators = new Map();
|
|
2515
|
+
testers = new Map();
|
|
2389
2516
|
}
|
|
2390
2517
|
|
|
2391
2518
|
class ProjectBrowser {
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
return this.parent.parseErrorStacktrace(e, options);
|
|
2454
|
-
}
|
|
2455
|
-
parseStacktrace(trace, options = {}) {
|
|
2456
|
-
return this.parent.parseStacktrace(trace, options);
|
|
2457
|
-
}
|
|
2458
|
-
async close() {
|
|
2459
|
-
await this.parent.vite.close();
|
|
2460
|
-
}
|
|
2519
|
+
testerHtml;
|
|
2520
|
+
testerFilepath;
|
|
2521
|
+
provider;
|
|
2522
|
+
vitest;
|
|
2523
|
+
config;
|
|
2524
|
+
children = new Set();
|
|
2525
|
+
parent;
|
|
2526
|
+
state = new BrowserServerState();
|
|
2527
|
+
constructor(project, base) {
|
|
2528
|
+
this.project = project;
|
|
2529
|
+
this.base = base;
|
|
2530
|
+
this.vitest = project.vitest;
|
|
2531
|
+
this.config = project.config;
|
|
2532
|
+
const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
|
|
2533
|
+
const distRoot = resolve(pkgRoot, "dist");
|
|
2534
|
+
const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
|
|
2535
|
+
if (!existsSync(testerHtmlPath)) {
|
|
2536
|
+
throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
|
|
2537
|
+
}
|
|
2538
|
+
this.testerFilepath = testerHtmlPath;
|
|
2539
|
+
this.testerHtml = readFile$1(testerHtmlPath, "utf8").then((html) => this.testerHtml = html);
|
|
2540
|
+
}
|
|
2541
|
+
get vite() {
|
|
2542
|
+
return this.parent.vite;
|
|
2543
|
+
}
|
|
2544
|
+
wrapSerializedConfig() {
|
|
2545
|
+
const config = wrapConfig(this.project.serializedConfig);
|
|
2546
|
+
config.env ??= {};
|
|
2547
|
+
config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
|
|
2548
|
+
return config;
|
|
2549
|
+
}
|
|
2550
|
+
async initBrowserProvider(project) {
|
|
2551
|
+
if (this.provider) {
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
const Provider = await getBrowserProvider(project.config.browser, project);
|
|
2555
|
+
this.provider = new Provider();
|
|
2556
|
+
const browser = project.config.browser.name;
|
|
2557
|
+
const name = project.name ? `[${project.name}] ` : "";
|
|
2558
|
+
if (!browser) {
|
|
2559
|
+
throw new Error(`${name}Browser name is required. Please, set \`test.browser.instances[].browser\` option manually.`);
|
|
2560
|
+
}
|
|
2561
|
+
const supportedBrowsers = this.provider.getSupportedBrowsers();
|
|
2562
|
+
if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
|
|
2563
|
+
throw new Error(`${name}Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`);
|
|
2564
|
+
}
|
|
2565
|
+
const providerOptions = project.config.browser.providerOptions;
|
|
2566
|
+
await this.provider.initialize(project, {
|
|
2567
|
+
browser,
|
|
2568
|
+
options: providerOptions
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
parseErrorStacktrace(e, options = {}) {
|
|
2572
|
+
return this.parent.parseErrorStacktrace(e, options);
|
|
2573
|
+
}
|
|
2574
|
+
parseStacktrace(trace, options = {}) {
|
|
2575
|
+
return this.parent.parseStacktrace(trace, options);
|
|
2576
|
+
}
|
|
2577
|
+
async close() {
|
|
2578
|
+
await this.parent.vite.close();
|
|
2579
|
+
}
|
|
2461
2580
|
}
|
|
2462
2581
|
function wrapConfig(config) {
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
};
|
|
2582
|
+
return {
|
|
2583
|
+
...config,
|
|
2584
|
+
testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : undefined
|
|
2585
|
+
};
|
|
2468
2586
|
}
|
|
2469
2587
|
|
|
2470
2588
|
class ParentBrowserProject {
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
}
|
|
2665
|
-
retrieveSourceMapURL(source) {
|
|
2666
|
-
const re = /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm;
|
|
2667
|
-
let lastMatch, match;
|
|
2668
|
-
while (match = re.exec(source)) {
|
|
2669
|
-
lastMatch = match;
|
|
2670
|
-
}
|
|
2671
|
-
if (!lastMatch) {
|
|
2672
|
-
return null;
|
|
2673
|
-
}
|
|
2674
|
-
return lastMatch[1];
|
|
2675
|
-
}
|
|
2589
|
+
orchestratorScripts;
|
|
2590
|
+
testerScripts;
|
|
2591
|
+
faviconUrl;
|
|
2592
|
+
prefixTesterUrl;
|
|
2593
|
+
manifest;
|
|
2594
|
+
vite;
|
|
2595
|
+
stackTraceOptions;
|
|
2596
|
+
orchestratorHtml;
|
|
2597
|
+
injectorJs;
|
|
2598
|
+
errorCatcherUrl;
|
|
2599
|
+
locatorsUrl;
|
|
2600
|
+
matchersUrl;
|
|
2601
|
+
stateJs;
|
|
2602
|
+
commands = {};
|
|
2603
|
+
children = new Set();
|
|
2604
|
+
vitest;
|
|
2605
|
+
config;
|
|
2606
|
+
sourceMapCache = new Map();
|
|
2607
|
+
constructor(project, base) {
|
|
2608
|
+
this.project = project;
|
|
2609
|
+
this.base = base;
|
|
2610
|
+
this.vitest = project.vitest;
|
|
2611
|
+
this.config = project.config;
|
|
2612
|
+
this.stackTraceOptions = {
|
|
2613
|
+
frameFilter: project.config.onStackTrace,
|
|
2614
|
+
getSourceMap: (id) => {
|
|
2615
|
+
if (this.sourceMapCache.has(id)) {
|
|
2616
|
+
return this.sourceMapCache.get(id);
|
|
2617
|
+
}
|
|
2618
|
+
const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
|
|
2619
|
+
if (result && !result.map) {
|
|
2620
|
+
const sourceMapUrl = this.retrieveSourceMapURL(result.code);
|
|
2621
|
+
if (!sourceMapUrl) {
|
|
2622
|
+
return null;
|
|
2623
|
+
}
|
|
2624
|
+
const filepathDir = dirname(id);
|
|
2625
|
+
const sourceMapPath = resolve(filepathDir, sourceMapUrl);
|
|
2626
|
+
const map = JSON.parse(readFileSync(sourceMapPath, "utf-8"));
|
|
2627
|
+
this.sourceMapCache.set(id, map);
|
|
2628
|
+
return map;
|
|
2629
|
+
}
|
|
2630
|
+
return result?.map;
|
|
2631
|
+
},
|
|
2632
|
+
getUrlId: (id) => {
|
|
2633
|
+
const mod = this.vite.moduleGraph.getModuleById(id);
|
|
2634
|
+
if (mod) {
|
|
2635
|
+
return id;
|
|
2636
|
+
}
|
|
2637
|
+
const resolvedPath = resolve(project.config.root, id.slice(1));
|
|
2638
|
+
const modUrl = this.vite.moduleGraph.getModuleById(resolvedPath);
|
|
2639
|
+
if (modUrl) {
|
|
2640
|
+
return resolvedPath;
|
|
2641
|
+
}
|
|
2642
|
+
const files = this.vite.moduleGraph.getModulesByFile(resolvedPath);
|
|
2643
|
+
if (files && files.size) {
|
|
2644
|
+
return files.values().next().value.id;
|
|
2645
|
+
}
|
|
2646
|
+
return id;
|
|
2647
|
+
}
|
|
2648
|
+
};
|
|
2649
|
+
for (const [name, command] of Object.entries(builtinCommands)) {
|
|
2650
|
+
this.commands[name] ??= command;
|
|
2651
|
+
}
|
|
2652
|
+
for (const command in project.config.browser.commands) {
|
|
2653
|
+
if (!/^[a-z_$][\w$]*$/i.test(command)) {
|
|
2654
|
+
throw new Error(`Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`);
|
|
2655
|
+
}
|
|
2656
|
+
this.commands[command] = project.config.browser.commands[command];
|
|
2657
|
+
}
|
|
2658
|
+
this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
|
|
2659
|
+
this.faviconUrl = `${base}__vitest__/favicon.svg`;
|
|
2660
|
+
this.manifest = (async () => {
|
|
2661
|
+
return JSON.parse(await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8"));
|
|
2662
|
+
})().then((manifest) => this.manifest = manifest);
|
|
2663
|
+
this.orchestratorHtml = (project.config.browser.ui ? readFile$1(resolve(distRoot, "client/__vitest__/index.html"), "utf8") : readFile$1(resolve(distRoot, "client/orchestrator.html"), "utf8")).then((html) => this.orchestratorHtml = html);
|
|
2664
|
+
this.injectorJs = readFile$1(resolve(distRoot, "client/esm-client-injector.js"), "utf8").then((js) => this.injectorJs = js);
|
|
2665
|
+
this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
|
|
2666
|
+
const builtinProviders = [
|
|
2667
|
+
"playwright",
|
|
2668
|
+
"webdriverio",
|
|
2669
|
+
"preview"
|
|
2670
|
+
];
|
|
2671
|
+
const providerName = project.config.browser.provider || "preview";
|
|
2672
|
+
if (builtinProviders.includes(providerName)) {
|
|
2673
|
+
this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
|
|
2674
|
+
}
|
|
2675
|
+
this.matchersUrl = join("/@fs/", distRoot, "expect-element.js");
|
|
2676
|
+
this.stateJs = readFile$1(resolve(distRoot, "state.js"), "utf-8").then((js) => this.stateJs = js);
|
|
2677
|
+
}
|
|
2678
|
+
setServer(vite) {
|
|
2679
|
+
this.vite = vite;
|
|
2680
|
+
}
|
|
2681
|
+
spawn(project) {
|
|
2682
|
+
if (!this.vite) {
|
|
2683
|
+
throw new Error(`Cannot spawn child server without a parent dev server.`);
|
|
2684
|
+
}
|
|
2685
|
+
const clone = new ProjectBrowser(project, "/");
|
|
2686
|
+
clone.parent = this;
|
|
2687
|
+
this.children.add(clone);
|
|
2688
|
+
return clone;
|
|
2689
|
+
}
|
|
2690
|
+
parseErrorStacktrace(e, options = {}) {
|
|
2691
|
+
return parseErrorStacktrace(e, {
|
|
2692
|
+
...this.stackTraceOptions,
|
|
2693
|
+
...options
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
parseStacktrace(trace, options = {}) {
|
|
2697
|
+
return parseStacktrace(trace, {
|
|
2698
|
+
...this.stackTraceOptions,
|
|
2699
|
+
...options
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
cdps = new Map();
|
|
2703
|
+
cdpSessionsPromises = new Map();
|
|
2704
|
+
async ensureCDPHandler(sessionId, rpcId) {
|
|
2705
|
+
const cachedHandler = this.cdps.get(rpcId);
|
|
2706
|
+
if (cachedHandler) {
|
|
2707
|
+
return cachedHandler;
|
|
2708
|
+
}
|
|
2709
|
+
const browserSession = this.vitest._browserSessions.getSession(sessionId);
|
|
2710
|
+
if (!browserSession) {
|
|
2711
|
+
throw new Error(`Session "${sessionId}" not found.`);
|
|
2712
|
+
}
|
|
2713
|
+
const browser = browserSession.project.browser;
|
|
2714
|
+
const provider = browser.provider;
|
|
2715
|
+
if (!provider) {
|
|
2716
|
+
throw new Error(`Browser provider is not defined for the project "${browserSession.project.name}".`);
|
|
2717
|
+
}
|
|
2718
|
+
if (!provider.getCDPSession) {
|
|
2719
|
+
throw new Error(`CDP is not supported by the provider "${provider.name}".`);
|
|
2720
|
+
}
|
|
2721
|
+
const promise = this.cdpSessionsPromises.get(rpcId) ?? await (async () => {
|
|
2722
|
+
const promise = provider.getCDPSession(sessionId).finally(() => {
|
|
2723
|
+
this.cdpSessionsPromises.delete(rpcId);
|
|
2724
|
+
});
|
|
2725
|
+
this.cdpSessionsPromises.set(rpcId, promise);
|
|
2726
|
+
return promise;
|
|
2727
|
+
})();
|
|
2728
|
+
const session = await promise;
|
|
2729
|
+
const rpc = browser.state.testers.get(rpcId);
|
|
2730
|
+
if (!rpc) {
|
|
2731
|
+
throw new Error(`Tester RPC "${rpcId}" was not established.`);
|
|
2732
|
+
}
|
|
2733
|
+
const handler = new BrowserServerCDPHandler(session, rpc);
|
|
2734
|
+
this.cdps.set(rpcId, handler);
|
|
2735
|
+
return handler;
|
|
2736
|
+
}
|
|
2737
|
+
removeCDPHandler(sessionId) {
|
|
2738
|
+
this.cdps.delete(sessionId);
|
|
2739
|
+
}
|
|
2740
|
+
async formatScripts(scripts) {
|
|
2741
|
+
if (!scripts?.length) {
|
|
2742
|
+
return [];
|
|
2743
|
+
}
|
|
2744
|
+
const server = this.vite;
|
|
2745
|
+
const promises = scripts.map(async ({ content, src, async, id, type = "module" }, index) => {
|
|
2746
|
+
const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : undefined) || src;
|
|
2747
|
+
const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
|
|
2748
|
+
await server.moduleGraph.ensureEntryFromUrl(transformId);
|
|
2749
|
+
const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
|
|
2750
|
+
return {
|
|
2751
|
+
tag: "script",
|
|
2752
|
+
attrs: {
|
|
2753
|
+
type,
|
|
2754
|
+
...async ? { async: "" } : {},
|
|
2755
|
+
...srcLink ? { src: srcLink.startsWith("http") ? srcLink : slash(`/@fs/${srcLink}`) } : {}
|
|
2756
|
+
},
|
|
2757
|
+
injectTo: "head",
|
|
2758
|
+
children: contentProcessed || ""
|
|
2759
|
+
};
|
|
2760
|
+
});
|
|
2761
|
+
return await Promise.all(promises);
|
|
2762
|
+
}
|
|
2763
|
+
resolveTesterUrl(pathname) {
|
|
2764
|
+
const [sessionId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
|
|
2765
|
+
const decodedTestFile = decodeURIComponent(testFile);
|
|
2766
|
+
return {
|
|
2767
|
+
sessionId,
|
|
2768
|
+
testFile: decodedTestFile
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
retrieveSourceMapURL(source) {
|
|
2772
|
+
const re = /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm;
|
|
2773
|
+
let lastMatch, match;
|
|
2774
|
+
while (match = re.exec(source)) {
|
|
2775
|
+
lastMatch = match;
|
|
2776
|
+
}
|
|
2777
|
+
if (!lastMatch) {
|
|
2778
|
+
return null;
|
|
2779
|
+
}
|
|
2780
|
+
return lastMatch[1];
|
|
2781
|
+
}
|
|
2676
2782
|
}
|
|
2677
2783
|
|
|
2678
2784
|
const DEFAULT_TIMEOUT = 6e4;
|
|
@@ -2802,453 +2908,451 @@ function nanoid(size = 21) {
|
|
|
2802
2908
|
|
|
2803
2909
|
const debug$1 = createDebugger("vitest:browser:api");
|
|
2804
2910
|
const BROWSER_API_PATH = "/__vitest_browser_api__";
|
|
2805
|
-
function setupBrowserRpc(globalServer) {
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
2911
|
+
function setupBrowserRpc(globalServer, defaultMockerRegistry) {
|
|
2912
|
+
const vite = globalServer.vite;
|
|
2913
|
+
const vitest = globalServer.vitest;
|
|
2914
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
2915
|
+
vite.httpServer?.on("upgrade", (request, socket, head) => {
|
|
2916
|
+
if (!request.url) {
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2919
|
+
const { pathname, searchParams } = new URL(request.url, "http://localhost");
|
|
2920
|
+
if (pathname !== BROWSER_API_PATH) {
|
|
2921
|
+
return;
|
|
2922
|
+
}
|
|
2923
|
+
if (!isValidApiRequest(vitest.config, request)) {
|
|
2924
|
+
socket.destroy();
|
|
2925
|
+
return;
|
|
2926
|
+
}
|
|
2927
|
+
const type = searchParams.get("type");
|
|
2928
|
+
const rpcId = searchParams.get("rpcId");
|
|
2929
|
+
const sessionId = searchParams.get("sessionId");
|
|
2930
|
+
const projectName = searchParams.get("projectName");
|
|
2931
|
+
if (type !== "tester" && type !== "orchestrator") {
|
|
2932
|
+
return error(new Error(`[vitest] Type query in ${request.url} is invalid. Type should be either "tester" or "orchestrator".`));
|
|
2933
|
+
}
|
|
2934
|
+
if (!sessionId || !rpcId || projectName == null) {
|
|
2935
|
+
return error(new Error(`[vitest] Invalid URL ${request.url}. "projectName", "sessionId" and "rpcId" queries are required.`));
|
|
2936
|
+
}
|
|
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
|
+
if (type === "orchestrator") {
|
|
2942
|
+
const session = vitest._browserSessions.getSession(sessionId);
|
|
2943
|
+
session?.connected();
|
|
2944
|
+
}
|
|
2945
|
+
const project = vitest.getProjectByName(projectName);
|
|
2946
|
+
if (!project) {
|
|
2947
|
+
return error(new Error(`[vitest] Project "${projectName}" not found.`));
|
|
2948
|
+
}
|
|
2949
|
+
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
2950
|
+
wss.emit("connection", ws, request);
|
|
2951
|
+
const rpc = setupClient(project, rpcId, ws, method);
|
|
2952
|
+
const state = project.browser.state;
|
|
2953
|
+
const clients = type === "tester" ? state.testers : state.orchestrators;
|
|
2954
|
+
clients.set(rpcId, rpc);
|
|
2955
|
+
debug$1?.("[%s] Browser API connected to %s", rpcId, type);
|
|
2956
|
+
ws.on("close", () => {
|
|
2957
|
+
debug$1?.("[%s] Browser API disconnected from %s", rpcId, type);
|
|
2958
|
+
clients.delete(rpcId);
|
|
2959
|
+
globalServer.removeCDPHandler(rpcId);
|
|
2960
|
+
});
|
|
2961
|
+
});
|
|
2962
|
+
});
|
|
2963
|
+
function error(err) {
|
|
2964
|
+
console.error(err);
|
|
2965
|
+
vitest.state.catchError(err, "RPC Error");
|
|
2966
|
+
}
|
|
2967
|
+
function checkFileAccess(path) {
|
|
2968
|
+
if (!isFileServingAllowed(path, vite)) {
|
|
2969
|
+
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
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
function setupClient(project, rpcId, ws, method) {
|
|
2973
|
+
const mockResolver = new ServerMockResolver(globalServer.vite, { moduleDirectories: project.config.server?.deps?.moduleDirectories });
|
|
2974
|
+
const mocker = project.browser?.provider.mocker;
|
|
2975
|
+
const rpc = createBirpc({
|
|
2976
|
+
async onUnhandledError(error, type) {
|
|
2977
|
+
if (error && typeof error === "object") {
|
|
2978
|
+
const _error = error;
|
|
2979
|
+
_error.stacks = globalServer.parseErrorStacktrace(_error);
|
|
2980
|
+
}
|
|
2981
|
+
vitest.state.catchError(error, type);
|
|
2982
|
+
},
|
|
2983
|
+
async onQueued(file) {
|
|
2984
|
+
if (method === "collect") {
|
|
2985
|
+
vitest.state.collectFiles(project, [file]);
|
|
2986
|
+
} else {
|
|
2987
|
+
await vitest._testRun.enqueued(project, file);
|
|
2988
|
+
}
|
|
2989
|
+
},
|
|
2990
|
+
async onCollected(files) {
|
|
2991
|
+
if (method === "collect") {
|
|
2992
|
+
vitest.state.collectFiles(project, files);
|
|
2993
|
+
} else {
|
|
2994
|
+
await vitest._testRun.collected(project, files);
|
|
2995
|
+
}
|
|
2996
|
+
},
|
|
2997
|
+
async onTaskUpdate(packs, events) {
|
|
2998
|
+
if (method === "collect") {
|
|
2999
|
+
vitest.state.updateTasks(packs);
|
|
3000
|
+
} else {
|
|
3001
|
+
await vitest._testRun.updated(packs, events);
|
|
3002
|
+
}
|
|
3003
|
+
},
|
|
3004
|
+
onAfterSuiteRun(meta) {
|
|
3005
|
+
vitest.coverageProvider?.onAfterSuiteRun(meta);
|
|
3006
|
+
},
|
|
3007
|
+
async sendLog(log) {
|
|
3008
|
+
if (method === "collect") {
|
|
3009
|
+
vitest.state.updateUserLog(log);
|
|
3010
|
+
} else {
|
|
3011
|
+
await vitest._testRun.log(log);
|
|
3012
|
+
}
|
|
3013
|
+
},
|
|
3014
|
+
resolveSnapshotPath(testPath) {
|
|
3015
|
+
return vitest.snapshot.resolvePath(testPath, { config: project.serializedConfig });
|
|
3016
|
+
},
|
|
3017
|
+
resolveSnapshotRawPath(testPath, rawPath) {
|
|
3018
|
+
return vitest.snapshot.resolveRawPath(testPath, rawPath);
|
|
3019
|
+
},
|
|
3020
|
+
snapshotSaved(snapshot) {
|
|
3021
|
+
vitest.snapshot.add(snapshot);
|
|
3022
|
+
},
|
|
3023
|
+
async readSnapshotFile(snapshotPath) {
|
|
3024
|
+
checkFileAccess(snapshotPath);
|
|
3025
|
+
if (!existsSync(snapshotPath)) {
|
|
3026
|
+
return null;
|
|
3027
|
+
}
|
|
3028
|
+
return promises.readFile(snapshotPath, "utf-8");
|
|
3029
|
+
},
|
|
3030
|
+
async saveSnapshotFile(id, content) {
|
|
3031
|
+
checkFileAccess(id);
|
|
3032
|
+
await promises.mkdir(dirname(id), { recursive: true });
|
|
3033
|
+
return promises.writeFile(id, content, "utf-8");
|
|
3034
|
+
},
|
|
3035
|
+
async removeSnapshotFile(id) {
|
|
3036
|
+
checkFileAccess(id);
|
|
3037
|
+
if (!existsSync(id)) {
|
|
3038
|
+
throw new Error(`Snapshot file "${id}" does not exist.`);
|
|
3039
|
+
}
|
|
3040
|
+
return promises.unlink(id);
|
|
3041
|
+
},
|
|
3042
|
+
getBrowserFileSourceMap(id) {
|
|
3043
|
+
const mod = globalServer.vite.moduleGraph.getModuleById(id);
|
|
3044
|
+
return mod?.transformResult?.map;
|
|
3045
|
+
},
|
|
3046
|
+
onCancel(reason) {
|
|
3047
|
+
vitest.cancelCurrentRun(reason);
|
|
3048
|
+
},
|
|
3049
|
+
async resolveId(id, importer) {
|
|
3050
|
+
return mockResolver.resolveId(id, importer);
|
|
3051
|
+
},
|
|
3052
|
+
debug(...args) {
|
|
3053
|
+
vitest.logger.console.debug(...args);
|
|
3054
|
+
},
|
|
3055
|
+
getCountOfFailedTests() {
|
|
3056
|
+
return vitest.state.getCountOfFailedTests();
|
|
3057
|
+
},
|
|
3058
|
+
async wdioSwitchContext(direction) {
|
|
3059
|
+
const provider = project.browser.provider;
|
|
3060
|
+
if (!provider) {
|
|
3061
|
+
throw new Error("Commands are only available for browser tests.");
|
|
3062
|
+
}
|
|
3063
|
+
if (provider.name !== "webdriverio") {
|
|
3064
|
+
throw new Error("Switch context is only available for WebDriverIO provider.");
|
|
3065
|
+
}
|
|
3066
|
+
if (direction === "iframe") {
|
|
3067
|
+
await provider.switchToTestFrame();
|
|
3068
|
+
} else {
|
|
3069
|
+
await provider.switchToMainFrame();
|
|
3070
|
+
}
|
|
3071
|
+
},
|
|
3072
|
+
async triggerCommand(sessionId, command, testPath, payload) {
|
|
3073
|
+
debug$1?.("[%s] Triggering command \"%s\"", sessionId, command);
|
|
3074
|
+
const provider = project.browser.provider;
|
|
3075
|
+
if (!provider) {
|
|
3076
|
+
throw new Error("Commands are only available for browser tests.");
|
|
3077
|
+
}
|
|
3078
|
+
const commands = globalServer.commands;
|
|
3079
|
+
if (!commands || !commands[command]) {
|
|
3080
|
+
throw new Error(`Unknown command "${command}".`);
|
|
3081
|
+
}
|
|
3082
|
+
const context = Object.assign({
|
|
3083
|
+
testPath,
|
|
3084
|
+
project,
|
|
3085
|
+
provider,
|
|
3086
|
+
contextId: sessionId,
|
|
3087
|
+
sessionId
|
|
3088
|
+
}, provider.getCommandsContext(sessionId));
|
|
3089
|
+
return await commands[command](context, ...payload);
|
|
3090
|
+
},
|
|
3091
|
+
finishBrowserTests(sessionId) {
|
|
3092
|
+
debug$1?.("[%s] Finishing browser tests for session", sessionId);
|
|
3093
|
+
return vitest._browserSessions.getSession(sessionId)?.resolve();
|
|
3094
|
+
},
|
|
3095
|
+
resolveMock(rawId, importer, options) {
|
|
3096
|
+
return mockResolver.resolveMock(rawId, importer, options);
|
|
3097
|
+
},
|
|
3098
|
+
invalidate(ids) {
|
|
3099
|
+
return mockResolver.invalidate(ids);
|
|
3100
|
+
},
|
|
3101
|
+
async registerMock(sessionId, module) {
|
|
3102
|
+
if (!mocker) {
|
|
3103
|
+
mockResolver.invalidate([module.id]);
|
|
3104
|
+
if (module.type === "manual") {
|
|
3105
|
+
const mock = ManualMockedModule.fromJSON(module, async () => {
|
|
3106
|
+
try {
|
|
3107
|
+
const { keys } = await rpc.resolveManualMock(module.url);
|
|
3108
|
+
return Object.fromEntries(keys.map((key) => [key, null]));
|
|
3109
|
+
} catch (err) {
|
|
3110
|
+
vitest.state.catchError(err, "Manual Mock Resolver Error");
|
|
3111
|
+
return {};
|
|
3112
|
+
}
|
|
3113
|
+
});
|
|
3114
|
+
defaultMockerRegistry.add(mock);
|
|
3115
|
+
} else {
|
|
3116
|
+
if (module.type === "redirect") {
|
|
3117
|
+
const redirectUrl = new URL(module.redirect);
|
|
3118
|
+
module.redirect = join(vite.config.root, redirectUrl.pathname);
|
|
3119
|
+
}
|
|
3120
|
+
defaultMockerRegistry.register(module);
|
|
3121
|
+
}
|
|
3122
|
+
return;
|
|
3123
|
+
}
|
|
3124
|
+
if (module.type === "manual") {
|
|
3125
|
+
const manualModule = ManualMockedModule.fromJSON(module, async () => {
|
|
3126
|
+
const { keys } = await rpc.resolveManualMock(module.url);
|
|
3127
|
+
return Object.fromEntries(keys.map((key) => [key, null]));
|
|
3128
|
+
});
|
|
3129
|
+
await mocker.register(sessionId, manualModule);
|
|
3130
|
+
} else if (module.type === "redirect") {
|
|
3131
|
+
await mocker.register(sessionId, RedirectedModule.fromJSON(module));
|
|
3132
|
+
} else if (module.type === "automock") {
|
|
3133
|
+
await mocker.register(sessionId, AutomockedModule.fromJSON(module));
|
|
3134
|
+
} else if (module.type === "autospy") {
|
|
3135
|
+
await mocker.register(sessionId, AutospiedModule.fromJSON(module));
|
|
3136
|
+
}
|
|
3137
|
+
},
|
|
3138
|
+
clearMocks(sessionId) {
|
|
3139
|
+
if (!mocker) {
|
|
3140
|
+
return defaultMockerRegistry.clear();
|
|
3141
|
+
}
|
|
3142
|
+
return mocker.clear(sessionId);
|
|
3143
|
+
},
|
|
3144
|
+
unregisterMock(sessionId, id) {
|
|
3145
|
+
if (!mocker) {
|
|
3146
|
+
return defaultMockerRegistry.delete(id);
|
|
3147
|
+
}
|
|
3148
|
+
return mocker.delete(sessionId, id);
|
|
3149
|
+
},
|
|
3150
|
+
async sendCdpEvent(sessionId, event, payload) {
|
|
3151
|
+
const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
|
|
3152
|
+
return cdp.send(event, payload);
|
|
3153
|
+
},
|
|
3154
|
+
async trackCdpEvent(sessionId, type, event, listenerId) {
|
|
3155
|
+
const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
|
|
3156
|
+
cdp[type](event, listenerId);
|
|
3157
|
+
}
|
|
3158
|
+
}, {
|
|
3159
|
+
post: (msg) => ws.send(msg),
|
|
3160
|
+
on: (fn) => ws.on("message", fn),
|
|
3161
|
+
eventNames: ["onCancel", "cdpEvent"],
|
|
3162
|
+
serialize: (data) => stringify(data, stringifyReplace),
|
|
3163
|
+
deserialize: parse,
|
|
3164
|
+
onTimeoutError(functionName) {
|
|
3165
|
+
throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
|
|
3166
|
+
}
|
|
3167
|
+
});
|
|
3168
|
+
vitest.onCancel((reason) => rpc.onCancel(reason));
|
|
3169
|
+
return rpc;
|
|
3170
|
+
}
|
|
3036
3171
|
}
|
|
3037
3172
|
function cloneByOwnProperties(value) {
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
}),
|
|
3043
|
-
{}
|
|
3044
|
-
);
|
|
3173
|
+
return Object.getOwnPropertyNames(value).reduce((clone, prop) => ({
|
|
3174
|
+
...clone,
|
|
3175
|
+
[prop]: value[prop]
|
|
3176
|
+
}), {});
|
|
3045
3177
|
}
|
|
3178
|
+
/**
|
|
3179
|
+
* Replacer function for serialization methods such as JS.stringify() or
|
|
3180
|
+
* flatted.stringify().
|
|
3181
|
+
*/
|
|
3046
3182
|
function stringifyReplace(key, value) {
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3183
|
+
if (value instanceof Error) {
|
|
3184
|
+
const cloned = cloneByOwnProperties(value);
|
|
3185
|
+
return {
|
|
3186
|
+
name: value.name,
|
|
3187
|
+
message: value.message,
|
|
3188
|
+
stack: value.stack,
|
|
3189
|
+
...cloned
|
|
3190
|
+
};
|
|
3191
|
+
} else {
|
|
3192
|
+
return value;
|
|
3193
|
+
}
|
|
3058
3194
|
}
|
|
3059
3195
|
|
|
3060
3196
|
const debug = createDebugger("vitest:browser:pool");
|
|
3061
3197
|
async function waitForTests(method, sessionId, project, files) {
|
|
3062
|
-
|
|
3063
|
-
|
|
3198
|
+
const context = project.vitest._browserSessions.createAsyncSession(method, sessionId, files, project);
|
|
3199
|
+
return await context;
|
|
3064
3200
|
}
|
|
3065
3201
|
function createBrowserPool(vitest) {
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
return vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
|
|
3176
|
-
}
|
|
3177
|
-
return {
|
|
3178
|
-
name: "browser",
|
|
3179
|
-
async close() {
|
|
3180
|
-
await Promise.all([...providers].map((provider) => provider.close()));
|
|
3181
|
-
providers.clear();
|
|
3182
|
-
vitest.resolvedProjects.forEach((project) => {
|
|
3183
|
-
project.browser?.state.orchestrators.forEach((orchestrator) => {
|
|
3184
|
-
orchestrator.$close();
|
|
3185
|
-
});
|
|
3186
|
-
});
|
|
3187
|
-
},
|
|
3188
|
-
runTests: (files) => runWorkspaceTests("run", files),
|
|
3189
|
-
collectTests: (files) => runWorkspaceTests("collect", files)
|
|
3190
|
-
};
|
|
3202
|
+
const providers = new Set();
|
|
3203
|
+
const executeTests = async (method, project, files) => {
|
|
3204
|
+
vitest.state.clearFiles(project, files);
|
|
3205
|
+
const browser = project.browser;
|
|
3206
|
+
const threadsCount = getThreadsCount(project);
|
|
3207
|
+
const provider = browser.provider;
|
|
3208
|
+
providers.add(provider);
|
|
3209
|
+
const resolvedUrls = browser.vite.resolvedUrls;
|
|
3210
|
+
const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
|
|
3211
|
+
if (!origin) {
|
|
3212
|
+
throw new Error(`Can't find browser origin URL for project "${project.name}" when running tests for files "${files.join("\", \"")}"`);
|
|
3213
|
+
}
|
|
3214
|
+
async function setBreakpoint(sessionId, file) {
|
|
3215
|
+
if (!project.config.inspector.waitForDebugger) {
|
|
3216
|
+
return;
|
|
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
|
+
}
|
|
3258
|
+
});
|
|
3259
|
+
await Promise.all(promises);
|
|
3260
|
+
};
|
|
3261
|
+
const runWorkspaceTests = async (method, specs) => {
|
|
3262
|
+
const groupedFiles = new Map();
|
|
3263
|
+
for (const { project, moduleId } of specs) {
|
|
3264
|
+
const files = groupedFiles.get(project) || [];
|
|
3265
|
+
files.push(moduleId);
|
|
3266
|
+
groupedFiles.set(project, files);
|
|
3267
|
+
}
|
|
3268
|
+
let isCancelled = false;
|
|
3269
|
+
vitest.onCancel(() => {
|
|
3270
|
+
isCancelled = true;
|
|
3271
|
+
});
|
|
3272
|
+
for (const [project, files] of groupedFiles.entries()) {
|
|
3273
|
+
if (isCancelled) {
|
|
3274
|
+
break;
|
|
3275
|
+
}
|
|
3276
|
+
await project._initBrowserProvider();
|
|
3277
|
+
if (!project.browser) {
|
|
3278
|
+
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
|
+
}
|
|
3280
|
+
await executeTests(method, project, files);
|
|
3281
|
+
}
|
|
3282
|
+
};
|
|
3283
|
+
const numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length;
|
|
3284
|
+
function getThreadsCount(project) {
|
|
3285
|
+
const config = project.config.browser;
|
|
3286
|
+
if (!config.headless || !project.browser.provider.supportsParallelism) {
|
|
3287
|
+
return 1;
|
|
3288
|
+
}
|
|
3289
|
+
if (!config.fileParallelism) {
|
|
3290
|
+
return 1;
|
|
3291
|
+
}
|
|
3292
|
+
if (project.config.maxWorkers) {
|
|
3293
|
+
return project.config.maxWorkers;
|
|
3294
|
+
}
|
|
3295
|
+
return vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
|
|
3296
|
+
}
|
|
3297
|
+
return {
|
|
3298
|
+
name: "browser",
|
|
3299
|
+
async close() {
|
|
3300
|
+
await Promise.all([...providers].map((provider) => provider.close()));
|
|
3301
|
+
providers.clear();
|
|
3302
|
+
vitest.projects.forEach((project) => {
|
|
3303
|
+
project.browser?.state.orchestrators.forEach((orchestrator) => {
|
|
3304
|
+
orchestrator.$close();
|
|
3305
|
+
});
|
|
3306
|
+
});
|
|
3307
|
+
},
|
|
3308
|
+
runTests: (files) => runWorkspaceTests("run", files),
|
|
3309
|
+
collectTests: (files) => runWorkspaceTests("collect", files)
|
|
3310
|
+
};
|
|
3191
3311
|
}
|
|
3192
3312
|
function escapePathToRegexp(path) {
|
|
3193
|
-
|
|
3313
|
+
return path.replace(/[/\\.?*()^${}|[\]+]/g, "\\$&");
|
|
3194
3314
|
}
|
|
3195
3315
|
|
|
3196
3316
|
async function createBrowserServer(project, configFile, prePlugins = [], postPlugins = []) {
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
configFile: configPath,
|
|
3237
|
-
// watch is handled by Vitest
|
|
3238
|
-
server: {
|
|
3239
|
-
hmr: false,
|
|
3240
|
-
watch: null
|
|
3241
|
-
},
|
|
3242
|
-
plugins: [
|
|
3243
|
-
...prePlugins,
|
|
3244
|
-
...project.options?.plugins || [],
|
|
3245
|
-
BrowserPlugin(server),
|
|
3246
|
-
...postPlugins
|
|
3247
|
-
]
|
|
3248
|
-
});
|
|
3249
|
-
await vite.listen();
|
|
3250
|
-
setupBrowserRpc(server);
|
|
3251
|
-
return server;
|
|
3317
|
+
if (project.vitest.version !== version) {
|
|
3318
|
+
project.vitest.logger.warn(c.yellow(`Loaded ${c.inverse(c.yellow(` vitest@${project.vitest.version} `))} and ${c.inverse(c.yellow(` @vitest/browser@${version} `))}.` + "\nRunning mixed versions is not supported and may lead into bugs" + "\nUpdate your dependencies and make sure the versions match."));
|
|
3319
|
+
}
|
|
3320
|
+
const server = new ParentBrowserProject(project, "/");
|
|
3321
|
+
const configPath = typeof configFile === "string" ? configFile : false;
|
|
3322
|
+
const logLevel = process.env.VITEST_BROWSER_DEBUG ?? "info";
|
|
3323
|
+
const logger = createViteLogger(project.vitest.logger, logLevel, { allowClearScreen: false });
|
|
3324
|
+
const mockerRegistry = new MockerRegistry();
|
|
3325
|
+
const vite = await createViteServer({
|
|
3326
|
+
...project.options,
|
|
3327
|
+
base: "/",
|
|
3328
|
+
logLevel,
|
|
3329
|
+
customLogger: {
|
|
3330
|
+
...logger,
|
|
3331
|
+
info(msg, options) {
|
|
3332
|
+
logger.info(msg, options);
|
|
3333
|
+
if (msg.includes("optimized dependencies changed. reloading")) {
|
|
3334
|
+
logger.warn([c.yellow(`\n${c.bold("[vitest]")} Vite unexpectedly reloaded a test. This may cause tests to fail, lead to flaky behaviour or duplicated test runs.\n`), c.yellow(`For a stable experience, please add mentioned dependencies to your config\'s ${c.bold("`optimizeDeps.include`")} field manually.\n\n`)].join(""));
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
},
|
|
3338
|
+
mode: project.config.mode,
|
|
3339
|
+
configFile: configPath,
|
|
3340
|
+
configLoader: project.vite.config.inlineConfig.configLoader,
|
|
3341
|
+
server: {
|
|
3342
|
+
hmr: false,
|
|
3343
|
+
watch: null
|
|
3344
|
+
},
|
|
3345
|
+
plugins: [
|
|
3346
|
+
...prePlugins,
|
|
3347
|
+
...project.options?.plugins || [],
|
|
3348
|
+
BrowserPlugin(server),
|
|
3349
|
+
interceptorPlugin({ registry: mockerRegistry }),
|
|
3350
|
+
...postPlugins
|
|
3351
|
+
]
|
|
3352
|
+
});
|
|
3353
|
+
await vite.listen();
|
|
3354
|
+
setupBrowserRpc(server, mockerRegistry);
|
|
3355
|
+
return server;
|
|
3252
3356
|
}
|
|
3253
3357
|
|
|
3254
3358
|
export { createBrowserPool, createBrowserServer, distRoot };
|