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