@vitest/browser 3.0.0-beta.1 → 3.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,22 +1,22 @@
1
1
  import c from 'tinyrainbow';
2
- import { isFileServingAllowed, getFilePoolName, distDir, resolveApiServerConfig, resolveFsAllow, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
3
- import fs, { promises, readFileSync, lstatSync, existsSync } from 'node:fs';
2
+ import { getFilePoolName, distDir, resolveApiServerConfig, resolveFsAllow, isFileServingAllowed, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
3
+ import fs, { readFileSync, lstatSync, promises, existsSync } from 'node:fs';
4
4
  import { createRequire } from 'node:module';
5
5
  import { dynamicImportPlugin, ServerMockResolver } from '@vitest/mocker/node';
6
- import { slash, toArray, createDefer } from '@vitest/utils';
6
+ import { slash as slash$1, toArray } from '@vitest/utils';
7
7
  import MagicString from 'magic-string';
8
8
  import sirv from 'sirv';
9
9
  import { coverageConfigDefaults } from 'vitest/config';
10
10
  import { fileURLToPath } from 'node:url';
11
- import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-JzwxG3Ek.js';
12
- import { resolve as resolve$1, dirname as dirname$1, basename as basename$1, normalize as normalize$1 } from 'node:path';
13
- import { mkdir, readFile as readFile$1 } from 'node:fs/promises';
14
11
  import crypto from 'node:crypto';
15
- import { WebSocketServer } from 'ws';
12
+ import { mkdir, readFile as readFile$1 } from 'node:fs/promises';
16
13
  import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
14
+ import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-dkCg9pjp.js';
15
+ import { resolve as resolve$1, dirname as dirname$1, basename as basename$1, normalize as normalize$1 } from 'node:path';
16
+ import { WebSocketServer } from 'ws';
17
17
  import * as nodeos from 'node:os';
18
18
 
19
- var version = "3.0.0-beta.1";
19
+ var version = "3.0.0-beta.3";
20
20
 
21
21
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
22
22
  function normalizeWindowsPath(input = "") {
@@ -200,1763 +200,2453 @@ const basename = function(p, extension) {
200
200
  const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
201
201
  const distRoot = resolve(pkgRoot, "dist");
202
202
 
203
- const clear = async (context, selector) => {
204
- if (context.provider instanceof PlaywrightBrowserProvider) {
205
- const { iframe } = context;
206
- const element = iframe.locator(selector);
207
- await element.clear();
208
- } else if (context.provider instanceof WebdriverBrowserProvider) {
209
- const browser = context.browser;
210
- const element = await browser.$(selector);
211
- await element.clearValue();
212
- } else {
213
- throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
214
- }
215
- };
216
-
217
- const click = async (context, selector, options = {}) => {
218
- const provider = context.provider;
219
- if (provider instanceof PlaywrightBrowserProvider) {
220
- const tester = context.iframe;
221
- await tester.locator(selector).click(options);
222
- } else if (provider instanceof WebdriverBrowserProvider) {
223
- const browser = context.browser;
224
- await browser.$(selector).click(options);
225
- } else {
226
- throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
203
+ function replacer(code, values) {
204
+ return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
205
+ }
206
+ const builtinProviders = ["webdriverio", "playwright", "preview"];
207
+ async function getBrowserProvider(options, project) {
208
+ if (options.provider == null || builtinProviders.includes(options.provider)) {
209
+ const providers = await import('./providers.js');
210
+ const provider = options.provider || "preview";
211
+ return providers[provider];
227
212
  }
228
- };
229
- const dblClick = async (context, selector, options = {}) => {
230
- const provider = context.provider;
231
- if (provider instanceof PlaywrightBrowserProvider) {
232
- const tester = context.iframe;
233
- await tester.locator(selector).dblclick(options);
234
- } else if (provider instanceof WebdriverBrowserProvider) {
235
- const browser = context.browser;
236
- await browser.$(selector).doubleClick();
237
- } else {
238
- throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
213
+ let customProviderModule;
214
+ try {
215
+ customProviderModule = await project.import(
216
+ options.provider
217
+ );
218
+ } catch (error) {
219
+ throw new Error(
220
+ `Failed to load custom BrowserProvider from ${options.provider}`,
221
+ { cause: error }
222
+ );
239
223
  }
240
- };
241
- const tripleClick = async (context, selector, options = {}) => {
242
- const provider = context.provider;
243
- if (provider instanceof PlaywrightBrowserProvider) {
244
- const tester = context.iframe;
245
- await tester.locator(selector).click({
246
- ...options,
247
- clickCount: 3
248
- });
249
- } else if (provider instanceof WebdriverBrowserProvider) {
250
- const browser = context.browser;
251
- await browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: await browser.$(selector) }).down().up().pause(50).down().up().pause(50).down().up().pause(50).perform();
252
- } else {
253
- throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
224
+ if (customProviderModule.default == null) {
225
+ throw new Error(
226
+ `Custom BrowserProvider loaded from ${options.provider} was not the default export`
227
+ );
254
228
  }
255
- };
229
+ return customProviderModule.default;
230
+ }
231
+ function slash(path) {
232
+ return path.replace(/\\/g, "/").replace(/\/+/g, "/");
233
+ }
256
234
 
257
- const dragAndDrop = async (context, source, target, options_) => {
258
- if (context.provider instanceof PlaywrightBrowserProvider) {
259
- const frame = await context.frame();
260
- await frame.dragAndDrop(
261
- source,
262
- target,
263
- options_
235
+ async function resolveOrchestrator(globalServer, url, res) {
236
+ let sessionId = url.searchParams.get("sessionId");
237
+ if (!sessionId) {
238
+ const contexts = [...globalServer.children].flatMap((p) => [...p.state.orchestrators.keys()]);
239
+ sessionId = contexts[contexts.length - 1] ?? "none";
240
+ }
241
+ const session = globalServer.vitest._browserSessions.getSession(sessionId);
242
+ const files = session?.files ?? [];
243
+ const browserProject = session?.project.browser || [...globalServer.children][0];
244
+ if (!browserProject) {
245
+ return;
246
+ }
247
+ const injectorJs = typeof globalServer.injectorJs === "string" ? globalServer.injectorJs : await globalServer.injectorJs;
248
+ const injector = replacer(injectorJs, {
249
+ __VITEST_PROVIDER__: JSON.stringify(browserProject.config.browser.provider || "preview"),
250
+ __VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
251
+ __VITEST_VITE_CONFIG__: JSON.stringify({
252
+ root: browserProject.vite.config.root
253
+ }),
254
+ __VITEST_FILES__: JSON.stringify(files),
255
+ __VITEST_TYPE__: '"orchestrator"',
256
+ __VITEST_SESSION_ID__: JSON.stringify(sessionId),
257
+ __VITEST_TESTER_ID__: '"none"',
258
+ __VITEST_PROVIDED_CONTEXT__: "{}"
259
+ });
260
+ res.removeHeader("Content-Security-Policy");
261
+ if (!globalServer.orchestratorScripts) {
262
+ globalServer.orchestratorScripts = (await globalServer.formatScripts(
263
+ globalServer.config.browser.orchestratorScripts
264
+ )).map((script) => {
265
+ let html = "<script ";
266
+ for (const attr in script.attrs || {}) {
267
+ html += `${attr}="${script.attrs[attr]}" `;
268
+ }
269
+ html += `>${script.children}<\/script>`;
270
+ return html;
271
+ }).join("\n");
272
+ }
273
+ let baseHtml = typeof globalServer.orchestratorHtml === "string" ? globalServer.orchestratorHtml : await globalServer.orchestratorHtml;
274
+ if (globalServer.config.browser.ui) {
275
+ const manifestContent = globalServer.manifest instanceof Promise ? await globalServer.manifest : globalServer.manifest;
276
+ const jsEntry = manifestContent["orchestrator.html"].file;
277
+ const base = browserProject.parent.vite.config.base || "/";
278
+ baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
279
+ "<!-- !LOAD_METADATA! -->",
280
+ [
281
+ "{__VITEST_INJECTOR__}",
282
+ "{__VITEST_ERROR_CATCHER__}",
283
+ "{__VITEST_SCRIPTS__}",
284
+ `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
285
+ ].join("\n")
264
286
  );
265
- } else if (context.provider instanceof WebdriverBrowserProvider) {
266
- const $source = context.browser.$(source);
267
- const $target = context.browser.$(target);
268
- const options = options_ || {};
269
- const duration = options.duration ?? 10;
270
- await context.browser.action("pointer").move({ duration: 0, origin: $source, x: options.sourceX ?? 0, y: options.sourceY ?? 0 }).down({ button: 0 }).move({ duration: 0, origin: "pointer", x: 0, y: 0 }).pause(duration).move({ duration: 0, origin: $target, x: options.targetX ?? 0, y: options.targetY ?? 0 }).move({ duration: 0, origin: "pointer", x: 1, y: 0 }).move({ duration: 0, origin: "pointer", x: -1, y: 0 }).up({ button: 0 }).perform();
271
- } else {
272
- throw new TypeError(`Provider "${context.provider.name}" does not support dragging elements`);
273
287
  }
274
- };
288
+ return replacer(baseHtml, {
289
+ __VITEST_FAVICON__: globalServer.faviconUrl,
290
+ __VITEST_TITLE__: "Vitest Browser Runner",
291
+ __VITEST_SCRIPTS__: globalServer.orchestratorScripts,
292
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
293
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${globalServer.errorCatcherUrl}"><\/script>`,
294
+ __VITEST_SESSION_ID__: JSON.stringify(sessionId)
295
+ });
296
+ }
275
297
 
276
- const fill = async (context, selector, text, options = {}) => {
277
- if (context.provider instanceof PlaywrightBrowserProvider) {
278
- const { iframe } = context;
279
- const element = iframe.locator(selector);
280
- await element.fill(text, options);
281
- } else if (context.provider instanceof WebdriverBrowserProvider) {
282
- const browser = context.browser;
283
- await browser.$(selector).setValue(text);
284
- } else {
285
- throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
298
+ function disableCache(res) {
299
+ res.setHeader(
300
+ "Cache-Control",
301
+ "no-cache, max-age=0, must-revalidate"
302
+ );
303
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
304
+ }
305
+ function allowIframes(res) {
306
+ res.removeHeader("X-Frame-Options");
307
+ }
308
+
309
+ function createOrchestratorMiddleware(parentServer) {
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
+ };
326
+ }
327
+
328
+ /// <reference types="../types/index.d.ts" />
329
+
330
+ // (c) 2020-present Andrea Giammarchi
331
+
332
+ const {parse: $parse, stringify: $stringify} = JSON;
333
+ const {keys} = Object;
334
+
335
+ const Primitive = String; // it could be Number
336
+ const primitive = 'string'; // it could be 'number'
337
+
338
+ const ignore = {};
339
+ const object = 'object';
340
+
341
+ const noop = (_, value) => value;
342
+
343
+ const primitives = value => (
344
+ value instanceof Primitive ? Primitive(value) : value
345
+ );
346
+
347
+ const Primitives = (_, value) => (
348
+ typeof value === primitive ? new Primitive(value) : value
349
+ );
350
+
351
+ const revive = (input, parsed, output, $) => {
352
+ const lazy = [];
353
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
354
+ const k = ke[y];
355
+ const value = output[k];
356
+ if (value instanceof Primitive) {
357
+ const tmp = input[value];
358
+ if (typeof tmp === object && !parsed.has(tmp)) {
359
+ parsed.add(tmp);
360
+ output[k] = ignore;
361
+ lazy.push({k, a: [input, parsed, tmp, $]});
362
+ }
363
+ else
364
+ output[k] = $.call(output, k, tmp);
365
+ }
366
+ else if (output[k] !== ignore)
367
+ output[k] = $.call(output, k, value);
368
+ }
369
+ for (let {length} = lazy, i = 0; i < length; i++) {
370
+ const {k, a} = lazy[i];
371
+ output[k] = $.call(output, k, revive.apply(null, a));
286
372
  }
373
+ return output;
287
374
  };
288
375
 
289
- const types = { "application/andrew-inset": ["ez"], "application/appinstaller": ["appinstaller"], "application/applixware": ["aw"], "application/appx": ["appx"], "application/appxbundle": ["appxbundle"], "application/atom+xml": ["atom"], "application/atomcat+xml": ["atomcat"], "application/atomdeleted+xml": ["atomdeleted"], "application/atomsvc+xml": ["atomsvc"], "application/atsc-dwd+xml": ["dwd"], "application/atsc-held+xml": ["held"], "application/atsc-rsat+xml": ["rsat"], "application/automationml-aml+xml": ["aml"], "application/automationml-amlx+zip": ["amlx"], "application/bdoc": ["bdoc"], "application/calendar+xml": ["xcs"], "application/ccxml+xml": ["ccxml"], "application/cdfx+xml": ["cdfx"], "application/cdmi-capability": ["cdmia"], "application/cdmi-container": ["cdmic"], "application/cdmi-domain": ["cdmid"], "application/cdmi-object": ["cdmio"], "application/cdmi-queue": ["cdmiq"], "application/cpl+xml": ["cpl"], "application/cu-seeme": ["cu"], "application/cwl": ["cwl"], "application/dash+xml": ["mpd"], "application/dash-patch+xml": ["mpp"], "application/davmount+xml": ["davmount"], "application/docbook+xml": ["dbk"], "application/dssc+der": ["dssc"], "application/dssc+xml": ["xdssc"], "application/ecmascript": ["ecma"], "application/emma+xml": ["emma"], "application/emotionml+xml": ["emotionml"], "application/epub+zip": ["epub"], "application/exi": ["exi"], "application/express": ["exp"], "application/fdf": ["fdf"], "application/fdt+xml": ["fdt"], "application/font-tdpfr": ["pfr"], "application/geo+json": ["geojson"], "application/gml+xml": ["gml"], "application/gpx+xml": ["gpx"], "application/gxf": ["gxf"], "application/gzip": ["gz"], "application/hjson": ["hjson"], "application/hyperstudio": ["stk"], "application/inkml+xml": ["ink", "inkml"], "application/ipfix": ["ipfix"], "application/its+xml": ["its"], "application/java-archive": ["jar", "war", "ear"], "application/java-serialized-object": ["ser"], "application/java-vm": ["class"], "application/javascript": ["*js"], "application/json": ["json", "map"], "application/json5": ["json5"], "application/jsonml+json": ["jsonml"], "application/ld+json": ["jsonld"], "application/lgr+xml": ["lgr"], "application/lost+xml": ["lostxml"], "application/mac-binhex40": ["hqx"], "application/mac-compactpro": ["cpt"], "application/mads+xml": ["mads"], "application/manifest+json": ["webmanifest"], "application/marc": ["mrc"], "application/marcxml+xml": ["mrcx"], "application/mathematica": ["ma", "nb", "mb"], "application/mathml+xml": ["mathml"], "application/mbox": ["mbox"], "application/media-policy-dataset+xml": ["mpf"], "application/mediaservercontrol+xml": ["mscml"], "application/metalink+xml": ["metalink"], "application/metalink4+xml": ["meta4"], "application/mets+xml": ["mets"], "application/mmt-aei+xml": ["maei"], "application/mmt-usd+xml": ["musd"], "application/mods+xml": ["mods"], "application/mp21": ["m21", "mp21"], "application/mp4": ["*mp4", "*mpg4", "mp4s", "m4p"], "application/msix": ["msix"], "application/msixbundle": ["msixbundle"], "application/msword": ["doc", "dot"], "application/mxf": ["mxf"], "application/n-quads": ["nq"], "application/n-triples": ["nt"], "application/node": ["cjs"], "application/octet-stream": ["bin", "dms", "lrf", "mar", "so", "dist", "distz", "pkg", "bpk", "dump", "elc", "deploy", "exe", "dll", "deb", "dmg", "iso", "img", "msi", "msp", "msm", "buffer"], "application/oda": ["oda"], "application/oebps-package+xml": ["opf"], "application/ogg": ["ogx"], "application/omdoc+xml": ["omdoc"], "application/onenote": ["onetoc", "onetoc2", "onetmp", "onepkg"], "application/oxps": ["oxps"], "application/p2p-overlay+xml": ["relo"], "application/patch-ops-error+xml": ["xer"], "application/pdf": ["pdf"], "application/pgp-encrypted": ["pgp"], "application/pgp-keys": ["asc"], "application/pgp-signature": ["sig", "*asc"], "application/pics-rules": ["prf"], "application/pkcs10": ["p10"], "application/pkcs7-mime": ["p7m", "p7c"], "application/pkcs7-signature": ["p7s"], "application/pkcs8": ["p8"], "application/pkix-attr-cert": ["ac"], "application/pkix-cert": ["cer"], "application/pkix-crl": ["crl"], "application/pkix-pkipath": ["pkipath"], "application/pkixcmp": ["pki"], "application/pls+xml": ["pls"], "application/postscript": ["ai", "eps", "ps"], "application/provenance+xml": ["provx"], "application/pskc+xml": ["pskcxml"], "application/raml+yaml": ["raml"], "application/rdf+xml": ["rdf", "owl"], "application/reginfo+xml": ["rif"], "application/relax-ng-compact-syntax": ["rnc"], "application/resource-lists+xml": ["rl"], "application/resource-lists-diff+xml": ["rld"], "application/rls-services+xml": ["rs"], "application/route-apd+xml": ["rapd"], "application/route-s-tsid+xml": ["sls"], "application/route-usd+xml": ["rusd"], "application/rpki-ghostbusters": ["gbr"], "application/rpki-manifest": ["mft"], "application/rpki-roa": ["roa"], "application/rsd+xml": ["rsd"], "application/rss+xml": ["rss"], "application/rtf": ["rtf"], "application/sbml+xml": ["sbml"], "application/scvp-cv-request": ["scq"], "application/scvp-cv-response": ["scs"], "application/scvp-vp-request": ["spq"], "application/scvp-vp-response": ["spp"], "application/sdp": ["sdp"], "application/senml+xml": ["senmlx"], "application/sensml+xml": ["sensmlx"], "application/set-payment-initiation": ["setpay"], "application/set-registration-initiation": ["setreg"], "application/shf+xml": ["shf"], "application/sieve": ["siv", "sieve"], "application/smil+xml": ["smi", "smil"], "application/sparql-query": ["rq"], "application/sparql-results+xml": ["srx"], "application/sql": ["sql"], "application/srgs": ["gram"], "application/srgs+xml": ["grxml"], "application/sru+xml": ["sru"], "application/ssdl+xml": ["ssdl"], "application/ssml+xml": ["ssml"], "application/swid+xml": ["swidtag"], "application/tei+xml": ["tei", "teicorpus"], "application/thraud+xml": ["tfi"], "application/timestamped-data": ["tsd"], "application/toml": ["toml"], "application/trig": ["trig"], "application/ttml+xml": ["ttml"], "application/ubjson": ["ubj"], "application/urc-ressheet+xml": ["rsheet"], "application/urc-targetdesc+xml": ["td"], "application/voicexml+xml": ["vxml"], "application/wasm": ["wasm"], "application/watcherinfo+xml": ["wif"], "application/widget": ["wgt"], "application/winhlp": ["hlp"], "application/wsdl+xml": ["wsdl"], "application/wspolicy+xml": ["wspolicy"], "application/xaml+xml": ["xaml"], "application/xcap-att+xml": ["xav"], "application/xcap-caps+xml": ["xca"], "application/xcap-diff+xml": ["xdf"], "application/xcap-el+xml": ["xel"], "application/xcap-ns+xml": ["xns"], "application/xenc+xml": ["xenc"], "application/xfdf": ["xfdf"], "application/xhtml+xml": ["xhtml", "xht"], "application/xliff+xml": ["xlf"], "application/xml": ["xml", "xsl", "xsd", "rng"], "application/xml-dtd": ["dtd"], "application/xop+xml": ["xop"], "application/xproc+xml": ["xpl"], "application/xslt+xml": ["*xsl", "xslt"], "application/xspf+xml": ["xspf"], "application/xv+xml": ["mxml", "xhvml", "xvml", "xvm"], "application/yang": ["yang"], "application/yin+xml": ["yin"], "application/zip": ["zip"], "audio/3gpp": ["*3gpp"], "audio/aac": ["adts", "aac"], "audio/adpcm": ["adp"], "audio/amr": ["amr"], "audio/basic": ["au", "snd"], "audio/midi": ["mid", "midi", "kar", "rmi"], "audio/mobile-xmf": ["mxmf"], "audio/mp3": ["*mp3"], "audio/mp4": ["m4a", "mp4a"], "audio/mpeg": ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"], "audio/ogg": ["oga", "ogg", "spx", "opus"], "audio/s3m": ["s3m"], "audio/silk": ["sil"], "audio/wav": ["wav"], "audio/wave": ["*wav"], "audio/webm": ["weba"], "audio/xm": ["xm"], "font/collection": ["ttc"], "font/otf": ["otf"], "font/ttf": ["ttf"], "font/woff": ["woff"], "font/woff2": ["woff2"], "image/aces": ["exr"], "image/apng": ["apng"], "image/avci": ["avci"], "image/avcs": ["avcs"], "image/avif": ["avif"], "image/bmp": ["bmp", "dib"], "image/cgm": ["cgm"], "image/dicom-rle": ["drle"], "image/dpx": ["dpx"], "image/emf": ["emf"], "image/fits": ["fits"], "image/g3fax": ["g3"], "image/gif": ["gif"], "image/heic": ["heic"], "image/heic-sequence": ["heics"], "image/heif": ["heif"], "image/heif-sequence": ["heifs"], "image/hej2k": ["hej2"], "image/hsj2": ["hsj2"], "image/ief": ["ief"], "image/jls": ["jls"], "image/jp2": ["jp2", "jpg2"], "image/jpeg": ["jpeg", "jpg", "jpe"], "image/jph": ["jph"], "image/jphc": ["jhc"], "image/jpm": ["jpm", "jpgm"], "image/jpx": ["jpx", "jpf"], "image/jxr": ["jxr"], "image/jxra": ["jxra"], "image/jxrs": ["jxrs"], "image/jxs": ["jxs"], "image/jxsc": ["jxsc"], "image/jxsi": ["jxsi"], "image/jxss": ["jxss"], "image/ktx": ["ktx"], "image/ktx2": ["ktx2"], "image/png": ["png"], "image/sgi": ["sgi"], "image/svg+xml": ["svg", "svgz"], "image/t38": ["t38"], "image/tiff": ["tif", "tiff"], "image/tiff-fx": ["tfx"], "image/webp": ["webp"], "image/wmf": ["wmf"], "message/disposition-notification": ["disposition-notification"], "message/global": ["u8msg"], "message/global-delivery-status": ["u8dsn"], "message/global-disposition-notification": ["u8mdn"], "message/global-headers": ["u8hdr"], "message/rfc822": ["eml", "mime"], "model/3mf": ["3mf"], "model/gltf+json": ["gltf"], "model/gltf-binary": ["glb"], "model/iges": ["igs", "iges"], "model/jt": ["jt"], "model/mesh": ["msh", "mesh", "silo"], "model/mtl": ["mtl"], "model/obj": ["obj"], "model/prc": ["prc"], "model/step+xml": ["stpx"], "model/step+zip": ["stpz"], "model/step-xml+zip": ["stpxz"], "model/stl": ["stl"], "model/u3d": ["u3d"], "model/vrml": ["wrl", "vrml"], "model/x3d+binary": ["*x3db", "x3dbz"], "model/x3d+fastinfoset": ["x3db"], "model/x3d+vrml": ["*x3dv", "x3dvz"], "model/x3d+xml": ["x3d", "x3dz"], "model/x3d-vrml": ["x3dv"], "text/cache-manifest": ["appcache", "manifest"], "text/calendar": ["ics", "ifb"], "text/coffeescript": ["coffee", "litcoffee"], "text/css": ["css"], "text/csv": ["csv"], "text/html": ["html", "htm", "shtml"], "text/jade": ["jade"], "text/javascript": ["js", "mjs"], "text/jsx": ["jsx"], "text/less": ["less"], "text/markdown": ["md", "markdown"], "text/mathml": ["mml"], "text/mdx": ["mdx"], "text/n3": ["n3"], "text/plain": ["txt", "text", "conf", "def", "list", "log", "in", "ini"], "text/richtext": ["rtx"], "text/rtf": ["*rtf"], "text/sgml": ["sgml", "sgm"], "text/shex": ["shex"], "text/slim": ["slim", "slm"], "text/spdx": ["spdx"], "text/stylus": ["stylus", "styl"], "text/tab-separated-values": ["tsv"], "text/troff": ["t", "tr", "roff", "man", "me", "ms"], "text/turtle": ["ttl"], "text/uri-list": ["uri", "uris", "urls"], "text/vcard": ["vcard"], "text/vtt": ["vtt"], "text/wgsl": ["wgsl"], "text/xml": ["*xml"], "text/yaml": ["yaml", "yml"], "video/3gpp": ["3gp", "3gpp"], "video/3gpp2": ["3g2"], "video/h261": ["h261"], "video/h263": ["h263"], "video/h264": ["h264"], "video/iso.segment": ["m4s"], "video/jpeg": ["jpgv"], "video/jpm": ["*jpm", "*jpgm"], "video/mj2": ["mj2", "mjp2"], "video/mp2t": ["ts"], "video/mp4": ["mp4", "mp4v", "mpg4"], "video/mpeg": ["mpeg", "mpg", "mpe", "m1v", "m2v"], "video/ogg": ["ogv"], "video/quicktime": ["qt", "mov"], "video/webm": ["webm"] };
290
- Object.freeze(types);
376
+ const set = (known, input, value) => {
377
+ const index = Primitive(input.push(value) - 1);
378
+ known.set(value, index);
379
+ return index;
380
+ };
291
381
 
292
- var __classPrivateFieldGet = (null && null.__classPrivateFieldGet) || function (receiver, state, kind, f) {
293
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
294
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
295
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
382
+ /**
383
+ * Converts a specialized flatted string into a JS value.
384
+ * @param {string} text
385
+ * @param {(this: any, key: string, value: any) => any} [reviver]
386
+ * @returns {any}
387
+ */
388
+ const parse = (text, reviver) => {
389
+ const input = $parse(text, Primitives).map(primitives);
390
+ const value = input[0];
391
+ const $ = reviver || noop;
392
+ const tmp = typeof value === object && value ?
393
+ revive(input, new Set, value, $) :
394
+ value;
395
+ return $.call({'': tmp}, '', tmp);
296
396
  };
297
- var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
298
- class Mime {
299
- constructor(...args) {
300
- _Mime_extensionToType.set(this, new Map());
301
- _Mime_typeToExtension.set(this, new Map());
302
- _Mime_typeToExtensions.set(this, new Map());
303
- for (const arg of args) {
304
- this.define(arg);
305
- }
397
+
398
+ /**
399
+ * Converts a JS value into a specialized flatted string.
400
+ * @param {any} value
401
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
402
+ * @param {string | number | undefined} [space]
403
+ * @returns {string}
404
+ */
405
+ const stringify = (value, replacer, space) => {
406
+ const $ = replacer && typeof replacer === object ?
407
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
408
+ (replacer || noop);
409
+ const known = new Map;
410
+ const input = [];
411
+ const output = [];
412
+ let i = +set(known, input, $.call({'': value}, '', value));
413
+ let firstRun = !i;
414
+ while (i < input.length) {
415
+ firstRun = true;
416
+ output[i] = $stringify(input[i++], replace, space);
417
+ }
418
+ return '[' + output.join(',') + ']';
419
+ function replace(key, value) {
420
+ if (firstRun) {
421
+ firstRun = !firstRun;
422
+ return value;
306
423
  }
307
- define(typeMap, force = false) {
308
- for (let [type, extensions] of Object.entries(typeMap)) {
309
- type = type.toLowerCase();
310
- extensions = extensions.map((ext) => ext.toLowerCase());
311
- if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) {
312
- __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, new Set());
313
- }
314
- const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
315
- let first = true;
316
- for (let extension of extensions) {
317
- const starred = extension.startsWith('*');
318
- extension = starred ? extension.slice(1) : extension;
319
- allExtensions?.add(extension);
320
- if (first) {
321
- __classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
322
- }
323
- first = false;
324
- if (starred)
325
- continue;
326
- const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
327
- if (currentType && currentType != type && !force) {
328
- throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
329
- }
330
- __classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
331
- }
332
- }
333
- return this;
334
- }
335
- getType(path) {
336
- if (typeof path !== 'string')
337
- return null;
338
- const last = path.replace(/^.*[/\\]/, '').toLowerCase();
339
- const ext = last.replace(/^.*\./, '').toLowerCase();
340
- const hasPath = last.length < path.length;
341
- const hasDot = ext.length < last.length - 1;
342
- if (!hasDot && hasPath)
343
- return null;
344
- return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
345
- }
346
- getExtension(type) {
347
- if (typeof type !== 'string')
348
- return null;
349
- type = type?.split?.(';')[0];
350
- return ((type && __classPrivateFieldGet(this, _Mime_typeToExtension, "f").get(type.trim().toLowerCase())) ?? null);
351
- }
352
- getAllExtensions(type) {
353
- if (typeof type !== 'string')
354
- return null;
355
- return __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type.toLowerCase()) ?? null;
356
- }
357
- _freeze() {
358
- this.define = () => {
359
- throw new Error('define() not allowed for built-in Mime objects. See https://github.com/broofa/mime/blob/main/README.md#custom-mime-instances');
360
- };
361
- Object.freeze(this);
362
- for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
363
- Object.freeze(extensions);
364
- }
365
- return this;
366
- }
367
- _getTestState() {
368
- return {
369
- types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
370
- extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f"),
371
- };
424
+ const after = $.call(this, key, value);
425
+ switch (typeof after) {
426
+ case object:
427
+ if (after === null) return after;
428
+ case primitive:
429
+ return known.get(after) || set(known, input, after);
372
430
  }
373
- }
374
- _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
375
-
376
- var mime = new Mime(types)._freeze();
431
+ return after;
432
+ }
433
+ };
377
434
 
378
- function assertFileAccess(path, project) {
379
- if (!isFileServingAllowed(path, project.vite) && !isFileServingAllowed(path, project.vitest.server)) {
380
- throw new Error(
381
- `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
435
+ async function resolveTester(globalServer, url, res, next) {
436
+ const csp = res.getHeader("Content-Security-Policy");
437
+ if (typeof csp === "string") {
438
+ res.setHeader(
439
+ "Content-Security-Policy",
440
+ csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *")
382
441
  );
383
442
  }
384
- }
385
- const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
386
- const filepath = resolve$1(dirname$1(testPath), path);
387
- assertFileAccess(filepath, project);
388
- if (typeof options === "object" && !options.encoding) {
389
- options.encoding = "utf-8";
443
+ const { sessionId, testFile } = globalServer.resolveTesterUrl(url.pathname);
444
+ const session = globalServer.vitest._browserSessions.getSession(sessionId);
445
+ if (!session) {
446
+ res.statusCode = 400;
447
+ res.end("Invalid session ID");
448
+ return;
390
449
  }
391
- return promises.readFile(filepath, options);
392
- };
393
- const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
394
- const filepath = resolve$1(dirname$1(testPath), path);
395
- assertFileAccess(filepath, project);
396
- const dir = dirname$1(filepath);
397
- if (!fs.existsSync(dir)) {
398
- await promises.mkdir(dir, { recursive: true });
450
+ const project = globalServer.vitest.getProjectByName(session.project.name || "");
451
+ const { testFiles } = await project.globTestFiles();
452
+ const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
453
+ const iframeId = JSON.stringify(testFile);
454
+ const files = session.files ?? [];
455
+ const method = session.method ?? "run";
456
+ const browserProject = project.browser || [...globalServer.children][0];
457
+ if (!browserProject) {
458
+ res.statusCode = 400;
459
+ res.end("Invalid session ID");
460
+ return;
399
461
  }
400
- await promises.writeFile(filepath, data, options);
401
- };
402
- const removeFile = async ({ project, testPath = process.cwd() }, path) => {
403
- const filepath = resolve$1(dirname$1(testPath), path);
404
- assertFileAccess(filepath, project);
405
- await promises.rm(filepath);
406
- };
407
- const _fileInfo = async ({ project, testPath = process.cwd() }, path, encoding) => {
408
- const filepath = resolve$1(dirname$1(testPath), path);
409
- assertFileAccess(filepath, project);
410
- const content = await promises.readFile(filepath, encoding || "base64");
411
- return {
412
- content,
413
- basename: basename$1(filepath),
414
- mime: mime.getType(filepath)
415
- };
416
- };
417
-
418
- const hover = async (context, selector, options = {}) => {
419
- if (context.provider instanceof PlaywrightBrowserProvider) {
420
- await context.iframe.locator(selector).hover(options);
421
- } else if (context.provider instanceof WebdriverBrowserProvider) {
422
- const browser = context.browser;
423
- await browser.$(selector).moveTo(options);
424
- } else {
425
- throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
462
+ const injectorJs = typeof globalServer.injectorJs === "string" ? globalServer.injectorJs : await globalServer.injectorJs;
463
+ const injector = replacer(injectorJs, {
464
+ __VITEST_PROVIDER__: JSON.stringify(project.browser.provider.name),
465
+ __VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
466
+ __VITEST_FILES__: JSON.stringify(files),
467
+ __VITEST_VITE_CONFIG__: JSON.stringify({
468
+ root: browserProject.vite.config.root
469
+ }),
470
+ __VITEST_TYPE__: '"tester"',
471
+ __VITEST_SESSION_ID__: JSON.stringify(sessionId),
472
+ __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
473
+ __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
474
+ });
475
+ const testerHtml = typeof browserProject.testerHtml === "string" ? browserProject.testerHtml : await browserProject.testerHtml;
476
+ try {
477
+ const url2 = join("/@fs/", browserProject.testerFilepath);
478
+ const indexhtml = await browserProject.vite.transformIndexHtml(url2, testerHtml);
479
+ const html = replacer(indexhtml, {
480
+ __VITEST_FAVICON__: globalServer.faviconUrl,
481
+ __VITEST_INJECTOR__: injector,
482
+ __VITEST_APPEND__: `
483
+ __vitest_browser_runner__.runningFiles = ${tests}
484
+ __vitest_browser_runner__.iframeId = ${iframeId}
485
+ __vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
486
+ document.querySelector('script[data-vitest-append]').remove()
487
+ `
488
+ });
489
+ return html;
490
+ } catch (err) {
491
+ session.reject(err);
492
+ next(err);
426
493
  }
427
- };
428
-
429
- var clickableInputTypes;
430
- (function(clickableInputTypes) {
431
- clickableInputTypes["button"] = "button";
432
- clickableInputTypes["color"] = "color";
433
- clickableInputTypes["file"] = "file";
434
- clickableInputTypes["image"] = "image";
435
- clickableInputTypes["reset"] = "reset";
436
- clickableInputTypes["submit"] = "submit";
437
- clickableInputTypes["checkbox"] = "checkbox";
438
- clickableInputTypes["radio"] = "radio";
439
- })(clickableInputTypes || (clickableInputTypes = {}));
494
+ }
440
495
 
441
- var editableInputTypes;
442
- (function(editableInputTypes) {
443
- editableInputTypes["text"] = "text";
444
- editableInputTypes["date"] = "date";
445
- editableInputTypes["datetime-local"] = "datetime-local";
446
- editableInputTypes["email"] = "email";
447
- editableInputTypes["month"] = "month";
448
- editableInputTypes["number"] = "number";
449
- editableInputTypes["password"] = "password";
450
- editableInputTypes["search"] = "search";
451
- editableInputTypes["tel"] = "tel";
452
- editableInputTypes["time"] = "time";
453
- editableInputTypes["url"] = "url";
454
- editableInputTypes["week"] = "week";
455
- })(editableInputTypes || (editableInputTypes = {}));
496
+ function createTesterMiddleware(browserServer) {
497
+ return async function vitestTesterMiddleware(req, res, next) {
498
+ if (!req.url) {
499
+ return next();
500
+ }
501
+ const url = new URL(req.url, "http://localhost");
502
+ if (!url.pathname.startsWith(browserServer.prefixTesterUrl)) {
503
+ return next();
504
+ }
505
+ const html = await resolveTester(browserServer, url, res, next);
506
+ if (html) {
507
+ disableCache(res);
508
+ allowIframes(res);
509
+ res.write(html, "utf-8");
510
+ res.end();
511
+ }
512
+ };
513
+ }
456
514
 
457
- var maxLengthSupportedTypes;
458
- (function(maxLengthSupportedTypes) {
459
- maxLengthSupportedTypes["email"] = "email";
460
- maxLengthSupportedTypes["password"] = "password";
461
- maxLengthSupportedTypes["search"] = "search";
462
- maxLengthSupportedTypes["telephone"] = "telephone";
463
- maxLengthSupportedTypes["text"] = "text";
464
- maxLengthSupportedTypes["url"] = "url";
465
- })(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
515
+ const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
516
+ const ID_CONTEXT = "@vitest/browser/context";
517
+ const __dirname = dirname(fileURLToPath(import.meta.url));
518
+ function BrowserContext(globalServer) {
519
+ return {
520
+ name: "vitest:browser:virtual-module:context",
521
+ enforce: "pre",
522
+ resolveId(id) {
523
+ if (id === ID_CONTEXT) {
524
+ return VIRTUAL_ID_CONTEXT;
525
+ }
526
+ },
527
+ load(id) {
528
+ if (id === VIRTUAL_ID_CONTEXT) {
529
+ return generateContextFile.call(this, globalServer);
530
+ }
531
+ }
532
+ };
533
+ }
534
+ async function generateContextFile(globalServer) {
535
+ const commands = Object.keys(globalServer.commands);
536
+ const filepathCode = "__vitest_worker__.filepath || __vitest_worker__.current?.file?.filepath || undefined";
537
+ const provider = [...globalServer.children][0].provider || { name: "preview" };
538
+ const providerName = provider.name;
539
+ const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
540
+ return ` ["${command}"]: (...args) => rpc().triggerCommand(sessionId, "${command}", filepath(), args),`;
541
+ }).join("\n");
542
+ const userEventNonProviderImport = await getUserEventImport(
543
+ providerName,
544
+ this.resolve.bind(this)
545
+ );
546
+ const distContextPath = slash$1(`/@fs/${resolve(__dirname, "context.js")}`);
547
+ return `
548
+ import { page, createUserEvent, cdp } from '${distContextPath}'
549
+ ${userEventNonProviderImport}
550
+ const filepath = () => ${filepathCode}
551
+ const rpc = () => __vitest_worker__.rpc
552
+ const sessionId = __vitest_browser_runner__.sessionId
466
553
 
467
- var bracketDict;
468
- (function(bracketDict) {
469
- bracketDict["{"] = "}";
470
- bracketDict["["] = "]";
471
- })(bracketDict || (bracketDict = {}));
472
- /**
473
- * Read the next key definition from user input
474
- *
475
- * Describe key per `{descriptor}` or `[descriptor]`.
476
- * Everything else will be interpreted as a single character as descriptor - e.g. `a`.
477
- * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
478
- * A previously pressed key can be released per `{/descriptor}`.
479
- * Keeping the key pressed can be written as `{descriptor>}`.
480
- * When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`.
481
- * You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key.
482
- */ function readNextDescriptor(text, context) {
483
- let pos = 0;
484
- const startBracket = text[pos] in bracketDict ? text[pos] : '';
485
- pos += startBracket.length;
486
- const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text);
487
- const type = isEscapedChar ? '' : startBracket;
488
- return {
489
- type,
490
- ...type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type)
491
- };
554
+ export const server = {
555
+ platform: ${JSON.stringify(process.platform)},
556
+ version: ${JSON.stringify(process.version)},
557
+ provider: ${JSON.stringify(providerName)},
558
+ browser: __vitest_browser_runner__.config.browser.name,
559
+ commands: {
560
+ ${commandsCode}
561
+ },
562
+ config: __vitest_browser_runner__.config,
492
563
  }
493
- function readPrintableChar(text, pos, context) {
494
- const descriptor = text[pos];
495
- assertDescriptor(descriptor, text, pos);
496
- pos += descriptor.length;
497
- return {
498
- consumedLength: pos,
499
- descriptor,
500
- releasePrevious: false,
501
- releaseSelf: true,
502
- repeat: 1
503
- };
504
- }
505
- function readTag(text, pos, startBracket, context) {
506
- var _text_slice_match, _text_slice_match1;
507
- const releasePreviousModifier = text[pos] === '/' ? '/' : '';
508
- pos += releasePreviousModifier.length;
509
- const escapedDescriptor = startBracket === '{' && text[pos] === '\\';
510
- pos += Number(escapedDescriptor);
511
- const descriptor = escapedDescriptor ? text[pos] : (_text_slice_match = text.slice(pos).match(startBracket === '{' ? /^\w+|^[^}>/]/ : /^\w+/)) === null || _text_slice_match === void 0 ? void 0 : _text_slice_match[0];
512
- assertDescriptor(descriptor, text, pos);
513
- pos += descriptor.length;
514
- var _text_slice_match_;
515
- const repeatModifier = (_text_slice_match_ = (_text_slice_match1 = text.slice(pos).match(/^>\d+/)) === null || _text_slice_match1 === void 0 ? void 0 : _text_slice_match1[0]) !== null && _text_slice_match_ !== void 0 ? _text_slice_match_ : '';
516
- pos += repeatModifier.length;
517
- const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
518
- pos += releaseSelfModifier.length;
519
- const expectedEndBracket = bracketDict[startBracket];
520
- const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
521
- if (!endBracket) {
522
- throw new Error(getErrorMessage([
523
- !repeatModifier && 'repeat modifier',
524
- !releaseSelfModifier && 'release modifier',
525
- `"${expectedEndBracket}"`
526
- ].filter(Boolean).join(' or '), text[pos], text));
527
- }
528
- pos += endBracket.length;
529
- return {
530
- consumedLength: pos,
531
- descriptor,
532
- releasePrevious: !!releasePreviousModifier,
533
- repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
534
- releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier)
535
- };
536
- }
537
- function assertDescriptor(descriptor, text, pos, context) {
538
- if (!descriptor) {
539
- throw new Error(getErrorMessage('key descriptor', text[pos], text));
540
- }
541
- }
542
- function hasReleaseSelf(releaseSelfModifier, repeatModifier) {
543
- if (releaseSelfModifier) {
544
- return releaseSelfModifier === '/';
545
- }
546
- if (repeatModifier) {
547
- return false;
548
- }
564
+ export const commands = server.commands
565
+ export const userEvent = createUserEvent(_userEventSetup)
566
+ export { page, cdp }
567
+ `;
549
568
  }
550
- function getErrorMessage(expected, found, text, context) {
551
- return `Expected ${expected} but found "${found !== null && found !== void 0 ? found : ''}" in "${text}"
552
- See ${`https://testing-library.com/docs/user-event/keyboard`}
553
- for more information about how userEvent parses your input.`;
569
+ async function getUserEventImport(provider, resolve2) {
570
+ if (provider !== "preview") {
571
+ return "const _userEventSetup = undefined";
572
+ }
573
+ const resolved = await resolve2("@testing-library/user-event", __dirname);
574
+ if (!resolved) {
575
+ throw new Error(`Failed to resolve user-event package from ${__dirname}`);
576
+ }
577
+ return `import { userEvent as __vitest_user_event__ } from '${slash$1(`/@fs/${resolved.id}`)}'
578
+ const _userEventSetup = __vitest_user_event__
579
+ `;
554
580
  }
555
581
 
556
- var ApiLevel;
557
- (function(ApiLevel) {
558
- ApiLevel[ApiLevel["Trigger"] = 2] = "Trigger";
559
- ApiLevel[ApiLevel["Call"] = 1] = "Call";
560
- })(ApiLevel || (ApiLevel = {}));
561
-
562
- var PointerEventsCheckLevel;
563
- (function(PointerEventsCheckLevel) {
564
- /**
565
- * Check pointer events on every user interaction that triggers a bunch of events.
566
- * E.g. once for releasing a mouse button even though this triggers `pointerup`, `mouseup`, `click`, etc...
567
- */ PointerEventsCheckLevel[PointerEventsCheckLevel["EachTrigger"] = 4] = "EachTrigger";
568
- /** Check each target once per call to pointer (related) API */ PointerEventsCheckLevel[PointerEventsCheckLevel["EachApiCall"] = 2] = "EachApiCall";
569
- /** Check each event target once */ PointerEventsCheckLevel[PointerEventsCheckLevel["EachTarget"] = 1] = "EachTarget";
570
- /** No pointer events check */ PointerEventsCheckLevel[PointerEventsCheckLevel["Never"] = 0] = "Never";
571
- })(PointerEventsCheckLevel || (PointerEventsCheckLevel = {}));
572
-
573
- var DOM_KEY_LOCATION;
574
- (function(DOM_KEY_LOCATION) {
575
- DOM_KEY_LOCATION[DOM_KEY_LOCATION["STANDARD"] = 0] = "STANDARD";
576
- DOM_KEY_LOCATION[DOM_KEY_LOCATION["LEFT"] = 1] = "LEFT";
577
- DOM_KEY_LOCATION[DOM_KEY_LOCATION["RIGHT"] = 2] = "RIGHT";
578
- DOM_KEY_LOCATION[DOM_KEY_LOCATION["NUMPAD"] = 3] = "NUMPAD";
579
- })(DOM_KEY_LOCATION || (DOM_KEY_LOCATION = {}));
580
-
581
- /**
582
- * Mapping for a default US-104-QWERTY keyboard
583
- */ const defaultKeyMap = [
584
- // alphanumeric keys
585
- ...'0123456789'.split('').map((c)=>({
586
- code: `Digit${c}`,
587
- key: c
588
- })),
589
- ...')!@#$%^&*('.split('').map((c, i)=>({
590
- code: `Digit${i}`,
591
- key: c,
592
- shiftKey: true
593
- })),
594
- ...'abcdefghijklmnopqrstuvwxyz'.split('').map((c)=>({
595
- code: `Key${c.toUpperCase()}`,
596
- key: c
597
- })),
598
- ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((c)=>({
599
- code: `Key${c}`,
600
- key: c,
601
- shiftKey: true
602
- })),
603
- // alphanumeric block - functional
604
- {
605
- code: 'Space',
606
- key: ' '
607
- },
608
- {
609
- code: 'AltLeft',
610
- key: 'Alt',
611
- location: DOM_KEY_LOCATION.LEFT
612
- },
613
- {
614
- code: 'AltRight',
615
- key: 'Alt',
616
- location: DOM_KEY_LOCATION.RIGHT
617
- },
618
- {
619
- code: 'ShiftLeft',
620
- key: 'Shift',
621
- location: DOM_KEY_LOCATION.LEFT
622
- },
623
- {
624
- code: 'ShiftRight',
625
- key: 'Shift',
626
- location: DOM_KEY_LOCATION.RIGHT
627
- },
628
- {
629
- code: 'ControlLeft',
630
- key: 'Control',
631
- location: DOM_KEY_LOCATION.LEFT
632
- },
633
- {
634
- code: 'ControlRight',
635
- key: 'Control',
636
- location: DOM_KEY_LOCATION.RIGHT
637
- },
638
- {
639
- code: 'MetaLeft',
640
- key: 'Meta',
641
- location: DOM_KEY_LOCATION.LEFT
642
- },
643
- {
644
- code: 'MetaRight',
645
- key: 'Meta',
646
- location: DOM_KEY_LOCATION.RIGHT
647
- },
582
+ const versionRegexp = /(?:\?|&)v=\w{8}/;
583
+ var BrowserPlugin = (parentServer, base = "/") => {
584
+ function isPackageExists(pkg, root) {
585
+ return parentServer.vitest.packageInstaller.isPackageExists?.(pkg, {
586
+ paths: [root]
587
+ });
588
+ }
589
+ return [
648
590
  {
649
- code: 'OSLeft',
650
- key: 'OS',
651
- location: DOM_KEY_LOCATION.LEFT
591
+ enforce: "pre",
592
+ name: "vitest:browser",
593
+ async configureServer(server) {
594
+ parentServer.setServer(server);
595
+ server.middlewares.use(function vitestHeaders(_req, res, next) {
596
+ const headers = server.config.server.headers;
597
+ if (headers) {
598
+ for (const name in headers) {
599
+ res.setHeader(name, headers[name]);
600
+ }
601
+ }
602
+ next();
603
+ });
604
+ server.middlewares.use(createOrchestratorMiddleware(parentServer));
605
+ server.middlewares.use(createTesterMiddleware(parentServer));
606
+ server.middlewares.use(
607
+ `${base}favicon.svg`,
608
+ (_, res) => {
609
+ const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
610
+ res.write(content, "utf-8");
611
+ res.end();
612
+ }
613
+ );
614
+ const coverageFolder = resolveCoverageFolder(parentServer.vitest);
615
+ const coveragePath = coverageFolder ? coverageFolder[1] : void 0;
616
+ if (coveragePath && base === coveragePath) {
617
+ throw new Error(
618
+ `The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`
619
+ );
620
+ }
621
+ if (coverageFolder) {
622
+ server.middlewares.use(
623
+ coveragePath,
624
+ sirv(coverageFolder[0], {
625
+ single: true,
626
+ dev: true,
627
+ setHeaders: (res) => {
628
+ res.setHeader(
629
+ "Cache-Control",
630
+ "public,max-age=0,must-revalidate"
631
+ );
632
+ }
633
+ })
634
+ );
635
+ }
636
+ const uiEnabled = parentServer.config.browser.ui;
637
+ if (uiEnabled) {
638
+ server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
639
+ if (!req.url) {
640
+ res.statusCode = 404;
641
+ res.end();
642
+ return;
643
+ }
644
+ const url = new URL(req.url, "http://localhost");
645
+ const file = url.searchParams.get("file");
646
+ if (!file) {
647
+ res.statusCode = 404;
648
+ res.end();
649
+ return;
650
+ }
651
+ let stat;
652
+ try {
653
+ stat = lstatSync(file);
654
+ } catch {
655
+ }
656
+ if (!stat?.isFile()) {
657
+ res.statusCode = 404;
658
+ res.end();
659
+ return;
660
+ }
661
+ const ext = extname(file);
662
+ const buffer = readFileSync(file);
663
+ res.setHeader(
664
+ "Cache-Control",
665
+ "public,max-age=0,must-revalidate"
666
+ );
667
+ res.setHeader("Content-Length", buffer.length);
668
+ res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
669
+ res.end(buffer);
670
+ });
671
+ }
672
+ server.middlewares.use((req, res, next) => {
673
+ if (req.url && versionRegexp.test(req.url) && !req.url.includes("chunk-")) {
674
+ res.setHeader("Cache-Control", "no-cache");
675
+ const setHeader = res.setHeader.bind(res);
676
+ res.setHeader = function(name, value) {
677
+ if (name === "Cache-Control") {
678
+ return res;
679
+ }
680
+ return setHeader(name, value);
681
+ };
682
+ }
683
+ next();
684
+ });
685
+ }
652
686
  },
653
687
  {
654
- code: 'OSRight',
655
- key: 'OS',
656
- location: DOM_KEY_LOCATION.RIGHT
657
- },
658
- {
659
- code: 'Tab',
660
- key: 'Tab'
661
- },
662
- {
663
- code: 'CapsLock',
664
- key: 'CapsLock'
665
- },
666
- {
667
- code: 'Backspace',
668
- key: 'Backspace'
669
- },
670
- {
671
- code: 'Enter',
672
- key: 'Enter'
673
- },
674
- // function
675
- {
676
- code: 'Escape',
677
- key: 'Escape'
678
- },
679
- // arrows
680
- {
681
- code: 'ArrowUp',
682
- key: 'ArrowUp'
683
- },
684
- {
685
- code: 'ArrowDown',
686
- key: 'ArrowDown'
687
- },
688
- {
689
- code: 'ArrowLeft',
690
- key: 'ArrowLeft'
691
- },
692
- {
693
- code: 'ArrowRight',
694
- key: 'ArrowRight'
695
- },
696
- // control pad
697
- {
698
- code: 'Home',
699
- key: 'Home'
700
- },
701
- {
702
- code: 'End',
703
- key: 'End'
688
+ name: "vitest:browser:tests",
689
+ enforce: "pre",
690
+ async config() {
691
+ const project = parentServer.vitest.getProjectByName(parentServer.config.name);
692
+ const { testFiles: allTestFiles } = await project.globTestFiles();
693
+ const browserTestFiles = allTestFiles.filter(
694
+ (file) => getFilePoolName(project, file) === "browser"
695
+ );
696
+ const setupFiles = toArray(project.config.setupFiles);
697
+ const define = {};
698
+ for (const env in project.config.env || {}) {
699
+ const stringValue = JSON.stringify(project.config.env[env]);
700
+ define[`import.meta.env.${env}`] = stringValue;
701
+ }
702
+ const entries = [
703
+ ...browserTestFiles,
704
+ ...setupFiles,
705
+ resolve(distDir, "index.js"),
706
+ resolve(distDir, "browser.js"),
707
+ resolve(distDir, "runners.js"),
708
+ resolve(distDir, "utils.js"),
709
+ ...project.config.snapshotSerializers || []
710
+ ];
711
+ const exclude = [
712
+ "vitest",
713
+ "vitest/utils",
714
+ "vitest/browser",
715
+ "vitest/runners",
716
+ "@vitest/browser",
717
+ "@vitest/browser/client",
718
+ "@vitest/utils",
719
+ "@vitest/utils/source-map",
720
+ "@vitest/runner",
721
+ "@vitest/spy",
722
+ "@vitest/utils/error",
723
+ "@vitest/snapshot",
724
+ "@vitest/expect",
725
+ "std-env",
726
+ "tinybench",
727
+ "tinyspy",
728
+ "tinyrainbow",
729
+ "pathe",
730
+ "msw",
731
+ "msw/browser"
732
+ ];
733
+ if (typeof project.config.diff === "string") {
734
+ entries.push(project.config.diff);
735
+ }
736
+ if (parentServer.vitest.coverageProvider) {
737
+ const coverage = parentServer.vitest.config.coverage;
738
+ const provider = coverage.provider;
739
+ if (provider === "v8") {
740
+ const path = tryResolve("@vitest/coverage-v8", [parentServer.config.root]);
741
+ if (path) {
742
+ entries.push(path);
743
+ exclude.push("@vitest/coverage-v8/browser");
744
+ }
745
+ } else if (provider === "istanbul") {
746
+ const path = tryResolve("@vitest/coverage-istanbul", [parentServer.config.root]);
747
+ if (path) {
748
+ entries.push(path);
749
+ exclude.push("@vitest/coverage-istanbul");
750
+ }
751
+ } else if (provider === "custom" && coverage.customProviderModule) {
752
+ entries.push(coverage.customProviderModule);
753
+ }
754
+ }
755
+ const include = [
756
+ "vitest > expect-type",
757
+ "vitest > @vitest/snapshot > magic-string",
758
+ "vitest > chai",
759
+ "vitest > chai > loupe",
760
+ "vitest > @vitest/utils > loupe",
761
+ "@vitest/browser > @testing-library/user-event",
762
+ "@vitest/browser > @testing-library/dom"
763
+ ];
764
+ const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root;
765
+ const svelte = isPackageExists("vitest-browser-svelte", fileRoot);
766
+ if (svelte) {
767
+ exclude.push("vitest-browser-svelte");
768
+ }
769
+ const vueTestUtils = isPackageExists("@vue/test-utils", fileRoot);
770
+ if (vueTestUtils) {
771
+ include.push("@vue/test-utils");
772
+ }
773
+ return {
774
+ define,
775
+ resolve: {
776
+ dedupe: ["vitest"]
777
+ },
778
+ optimizeDeps: {
779
+ entries,
780
+ exclude,
781
+ include
782
+ }
783
+ };
784
+ },
785
+ async resolveId(id) {
786
+ if (!/\?browserv=\w+$/.test(id)) {
787
+ return;
788
+ }
789
+ let useId = id.slice(0, id.lastIndexOf("?"));
790
+ if (useId.startsWith("/@fs/")) {
791
+ useId = useId.slice(5);
792
+ }
793
+ if (/^\w:/.test(useId)) {
794
+ useId = useId.replace(/\\/g, "/");
795
+ }
796
+ return useId;
797
+ }
704
798
  },
705
799
  {
706
- code: 'Delete',
707
- key: 'Delete'
800
+ name: "vitest:browser:resolve-virtual",
801
+ async resolveId(rawId) {
802
+ if (rawId === "/mockServiceWorker.js") {
803
+ return this.resolve("msw/mockServiceWorker.js", distRoot, {
804
+ skipSelf: true
805
+ });
806
+ }
807
+ }
708
808
  },
709
809
  {
710
- code: 'PageUp',
711
- key: 'PageUp'
810
+ name: "vitest:browser:assets",
811
+ configureServer(server) {
812
+ server.middlewares.use(
813
+ "/__vitest__",
814
+ sirv(resolve(distRoot, "client/__vitest__"))
815
+ );
816
+ },
817
+ resolveId(id) {
818
+ if (id.startsWith("/__vitest_browser__/")) {
819
+ return resolve(distRoot, "client", id.slice(1));
820
+ }
821
+ },
822
+ transform(code, id) {
823
+ if (id.includes(parentServer.vite.config.cacheDir) && id.includes("loupe.js")) {
824
+ const utilRequire = "nodeUtil = require_util();";
825
+ return code.replace(utilRequire, " ".repeat(utilRequire.length));
826
+ }
827
+ }
712
828
  },
829
+ BrowserContext(parentServer),
830
+ dynamicImportPlugin({
831
+ globalThisAccessor: '"__vitest_browser_runner__"',
832
+ filter(id) {
833
+ if (id.includes(distRoot)) {
834
+ return false;
835
+ }
836
+ return true;
837
+ }
838
+ }),
713
839
  {
714
- code: 'PageDown',
715
- key: 'PageDown'
840
+ name: "vitest:browser:config",
841
+ enforce: "post",
842
+ async config(viteConfig) {
843
+ // Enables using ignore hint for coverage providers with @preserve keyword
844
+ if (viteConfig.esbuild !== false) {
845
+ viteConfig.esbuild ||= {};
846
+ viteConfig.esbuild.legalComments = "inline";
847
+ }
848
+ const defaultPort = parentServer.vitest._browserLastPort++;
849
+ const api = resolveApiServerConfig(
850
+ viteConfig.test?.browser || {},
851
+ defaultPort
852
+ );
853
+ viteConfig.server = {
854
+ ...viteConfig.server,
855
+ port: defaultPort,
856
+ ...api,
857
+ middlewareMode: false,
858
+ open: false
859
+ };
860
+ viteConfig.server.fs ??= {};
861
+ viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
862
+ viteConfig.server.fs.allow.push(
863
+ ...resolveFsAllow(
864
+ parentServer.vitest.config.root,
865
+ parentServer.vitest.vite.config.configFile
866
+ ),
867
+ distRoot
868
+ );
869
+ return {
870
+ resolve: {
871
+ alias: viteConfig.test?.alias
872
+ }
873
+ };
874
+ }
716
875
  },
717
- // Special keys that are not part of a default US-layout but included for specific behavior
718
876
  {
719
- code: 'Fn',
720
- key: 'Fn'
877
+ name: "vitest:browser:in-source-tests",
878
+ transform(code, id) {
879
+ const project = parentServer.vitest.getProjectByName(parentServer.config.name);
880
+ if (!project.isCachedTestFile(id) || !code.includes("import.meta.vitest")) {
881
+ return;
882
+ }
883
+ const s = new MagicString(code, { filename: cleanUrl(id) });
884
+ s.prepend(
885
+ `import.meta.vitest = __vitest_index__;
886
+ `
887
+ );
888
+ return {
889
+ code: s.toString(),
890
+ map: s.generateMap({ hires: true })
891
+ };
892
+ }
721
893
  },
722
894
  {
723
- code: 'Symbol',
724
- key: 'Symbol'
895
+ name: "vitest:browser:worker",
896
+ transform(code, id, _options) {
897
+ if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
898
+ const s = new MagicString(code);
899
+ s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
900
+ return {
901
+ code: s.toString(),
902
+ map: s.generateMap({ hires: "boundary" })
903
+ };
904
+ }
905
+ }
725
906
  },
726
907
  {
727
- code: 'AltRight',
728
- key: 'AltGraph'
729
- }
730
- ];
731
-
732
- /**
733
- * Parse key defintions per `keyboardMap`
734
- *
735
- * Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
736
- * Everything else will be interpreted as a typed character - e.g. `a`.
737
- * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
738
- * Keeping the key pressed can be written as `{key>}`.
739
- * When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
740
- * You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
741
- */ function parseKeyDef(keyboardMap, text) {
742
- const defs = [];
743
- do {
744
- const { type, descriptor, consumedLength, releasePrevious, releaseSelf = true, repeat } = readNextDescriptor(text);
745
- var _keyboardMap_find;
746
- const keyDef = (_keyboardMap_find = keyboardMap.find((def)=>{
747
- if (type === '[') {
748
- var _def_code;
749
- return ((_def_code = def.code) === null || _def_code === void 0 ? void 0 : _def_code.toLowerCase()) === descriptor.toLowerCase();
750
- } else if (type === '{') {
751
- var _def_key;
752
- return ((_def_key = def.key) === null || _def_key === void 0 ? void 0 : _def_key.toLowerCase()) === descriptor.toLowerCase();
753
- }
754
- return def.key === descriptor;
755
- })) !== null && _keyboardMap_find !== void 0 ? _keyboardMap_find : {
756
- key: 'Unknown',
757
- code: 'Unknown',
758
- [type === '[' ? 'code' : 'key']: descriptor
759
- };
760
- defs.push({
761
- keyDef,
762
- releasePrevious,
763
- releaseSelf,
764
- repeat
908
+ name: "vitest:browser:transform-tester-html",
909
+ enforce: "pre",
910
+ async transformIndexHtml(html, ctx) {
911
+ const projectBrowser = [...parentServer.children].find((server) => {
912
+ return ctx.filename === server.testerFilepath;
765
913
  });
766
- text = text.slice(consumedLength);
767
- }while (text)
768
- return defs;
769
- }
770
-
771
- const keyboard = async (context, text, state) => {
772
- if (context.provider instanceof PlaywrightBrowserProvider) {
773
- const frame = await context.frame();
774
- await frame.evaluate(focusIframe);
775
- } else if (context.provider instanceof WebdriverBrowserProvider) {
776
- await context.browser.execute(focusIframe);
777
- }
778
- const pressed = new Set(state.unreleased);
779
- await keyboardImplementation(
780
- pressed,
781
- context.provider,
782
- context.contextId,
783
- text,
784
- async () => {
785
- if (context.provider instanceof PlaywrightBrowserProvider) {
786
- const frame = await context.frame();
787
- await frame.evaluate(selectAll);
788
- } else if (context.provider instanceof WebdriverBrowserProvider) {
789
- await context.browser.execute(selectAll);
790
- } else {
791
- throw new TypeError(`Provider "${context.provider.name}" does not support selecting all text`);
792
- }
793
- },
794
- true
795
- );
796
- return {
797
- unreleased: Array.from(pressed)
798
- };
799
- };
800
- const keyboardCleanup = async (context, state) => {
801
- const { provider, contextId } = context;
802
- if (provider instanceof PlaywrightBrowserProvider) {
803
- const page = provider.getPage(contextId);
804
- for (const key of state.unreleased) {
805
- await page.keyboard.up(key);
806
- }
807
- } else if (provider instanceof WebdriverBrowserProvider) {
808
- const keyboard2 = provider.browser.action("key");
809
- for (const key of state.unreleased) {
810
- keyboard2.up(key);
811
- }
812
- await keyboard2.perform();
813
- } else {
814
- throw new TypeError(`Provider "${context.provider.name}" does not support keyboard api`);
815
- }
816
- };
817
- const VALID_KEYS = /* @__PURE__ */ new Set(["Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backquote", "`", "~", "Digit1", "1", "!", "Digit2", "2", "@", "Digit3", "3", "#", "Digit4", "4", "$", "Digit5", "5", "%", "Digit6", "6", "^", "Digit7", "7", "&", "Digit8", "8", "*", "Digit9", "9", "(", "Digit0", "0", ")", "Minus", "-", "_", "Equal", "=", "+", "Backslash", "\\", "|", "Backspace", "Tab", "KeyQ", "q", "Q", "KeyW", "w", "W", "KeyE", "e", "E", "KeyR", "r", "R", "KeyT", "t", "T", "KeyY", "y", "Y", "KeyU", "u", "U", "KeyI", "i", "I", "KeyO", "o", "O", "KeyP", "p", "P", "BracketLeft", "[", "{", "BracketRight", "]", "}", "CapsLock", "KeyA", "a", "A", "KeyS", "s", "S", "KeyD", "d", "D", "KeyF", "f", "F", "KeyG", "g", "G", "KeyH", "h", "H", "KeyJ", "j", "J", "KeyK", "k", "K", "KeyL", "l", "L", "Semicolon", ";", ":", "Quote", "'", '"', "Enter", "\n", "\r", "ShiftLeft", "Shift", "KeyZ", "z", "Z", "KeyX", "x", "X", "KeyC", "c", "C", "KeyV", "v", "V", "KeyB", "b", "B", "KeyN", "n", "N", "KeyM", "m", "M", "Comma", ",", "<", "Period", ".", ">", "Slash", "/", "?", "ShiftRight", "ControlLeft", "Control", "MetaLeft", "Meta", "AltLeft", "Alt", "Space", " ", "AltRight", "AltGraph", "MetaRight", "ContextMenu", "ControlRight", "PrintScreen", "ScrollLock", "Pause", "PageUp", "PageDown", "Insert", "Delete", "Home", "End", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", "NumLock", "NumpadDivide", "NumpadMultiply", "NumpadSubtract", "Numpad7", "Numpad8", "Numpad9", "Numpad4", "Numpad5", "Numpad6", "NumpadAdd", "Numpad1", "Numpad2", "Numpad3", "Numpad0", "NumpadDecimal", "NumpadEnter"]);
818
- async function keyboardImplementation(pressed, provider, contextId, text, selectAll2, skipRelease) {
819
- if (provider instanceof PlaywrightBrowserProvider) {
820
- const page = provider.getPage(contextId);
821
- const actions = parseKeyDef(defaultKeyMap, text);
822
- for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
823
- const key = keyDef.key;
824
- if (pressed.has(key)) {
825
- if (VALID_KEYS.has(key)) {
826
- await page.keyboard.up(key);
827
- }
828
- pressed.delete(key);
829
- }
830
- if (!releasePrevious) {
831
- if (key === "selectall") {
832
- await selectAll2();
833
- continue;
914
+ if (!projectBrowser) {
915
+ return;
834
916
  }
835
- for (let i = 1; i <= repeat; i++) {
836
- if (VALID_KEYS.has(key)) {
837
- await page.keyboard.down(key);
838
- } else {
839
- await page.keyboard.insertText(key);
840
- }
917
+ if (!parentServer.testerScripts) {
918
+ const testerScripts = await parentServer.formatScripts(
919
+ parentServer.config.browser.testerScripts
920
+ );
921
+ parentServer.testerScripts = testerScripts;
841
922
  }
842
- if (releaseSelf) {
843
- if (VALID_KEYS.has(key)) {
844
- await page.keyboard.up(key);
923
+ const stateJs = typeof parentServer.stateJs === "string" ? parentServer.stateJs : await parentServer.stateJs;
924
+ const testerTags = [];
925
+ const isDefaultTemplate = resolve(distRoot, "client/tester/tester.html") === projectBrowser.testerFilepath;
926
+ if (!isDefaultTemplate) {
927
+ const manifestContent = parentServer.manifest instanceof Promise ? await parentServer.manifest : parentServer.manifest;
928
+ const testerEntry = manifestContent["tester/tester.html"];
929
+ testerTags.push({
930
+ tag: "script",
931
+ attrs: {
932
+ type: "module",
933
+ crossorigin: "",
934
+ src: `${parentServer.base}${testerEntry.file}`
935
+ },
936
+ injectTo: "head"
937
+ });
938
+ for (const importName of testerEntry.imports || []) {
939
+ const entryManifest = manifestContent[importName];
940
+ if (entryManifest) {
941
+ testerTags.push(
942
+ {
943
+ tag: "link",
944
+ attrs: {
945
+ href: `${parentServer.base}${entryManifest.file}`,
946
+ rel: "modulepreload",
947
+ crossorigin: ""
948
+ },
949
+ injectTo: "head"
950
+ }
951
+ );
952
+ }
845
953
  }
846
954
  } else {
847
- pressed.add(key);
848
- }
849
- }
850
- }
851
- if (!skipRelease && pressed.size) {
852
- for (const key of pressed) {
853
- if (VALID_KEYS.has(key)) {
854
- await page.keyboard.up(key);
955
+ testerTags.push({
956
+ tag: "style",
957
+ children: `
958
+ html {
959
+ padding: 0;
960
+ margin: 0;
961
+ }
962
+ body {
963
+ padding: 0;
964
+ margin: 0;
965
+ min-height: 100vh;
966
+ }`,
967
+ injectTo: "head"
968
+ });
855
969
  }
970
+ return [
971
+ {
972
+ tag: "script",
973
+ children: "{__VITEST_INJECTOR__}",
974
+ injectTo: "head-prepend"
975
+ },
976
+ {
977
+ tag: "script",
978
+ children: stateJs,
979
+ injectTo: "head-prepend"
980
+ },
981
+ {
982
+ tag: "script",
983
+ attrs: {
984
+ type: "module",
985
+ src: parentServer.errorCatcherUrl
986
+ },
987
+ injectTo: "head"
988
+ },
989
+ parentServer.locatorsUrl ? {
990
+ tag: "script",
991
+ attrs: {
992
+ type: "module",
993
+ src: parentServer.locatorsUrl
994
+ },
995
+ injectTo: "head"
996
+ } : null,
997
+ ...parentServer.testerScripts,
998
+ ...testerTags,
999
+ {
1000
+ tag: "script",
1001
+ attrs: {
1002
+ "type": "module",
1003
+ "data-vitest-append": ""
1004
+ },
1005
+ children: "{__VITEST_APPEND__}",
1006
+ injectTo: "body"
1007
+ }
1008
+ ].filter((s) => s != null);
856
1009
  }
857
- }
858
- } else if (provider instanceof WebdriverBrowserProvider) {
859
- const { Key } = await import('webdriverio');
860
- const browser = provider.browser;
861
- const actions = parseKeyDef(defaultKeyMap, text);
862
- let keyboard2 = browser.action("key");
863
- for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
864
- let key = keyDef.key;
865
- const code = "location" in keyDef ? keyDef.key : keyDef.code;
866
- const special = Key[code];
867
- if (special) {
868
- key = special;
869
- }
870
- if (pressed.has(key)) {
871
- keyboard2.up(key);
872
- pressed.delete(key);
873
- }
874
- if (!releasePrevious) {
875
- if (key === "selectall") {
876
- await keyboard2.perform();
877
- keyboard2 = browser.action("key");
878
- await selectAll2();
879
- continue;
880
- }
881
- for (let i = 1; i <= repeat; i++) {
882
- keyboard2.down(key);
883
- }
884
- if (releaseSelf) {
885
- keyboard2.up(key);
886
- } else {
887
- pressed.add(key);
888
- }
1010
+ },
1011
+ {
1012
+ name: "vitest:browser:support-testing-library",
1013
+ config() {
1014
+ return {
1015
+ optimizeDeps: {
1016
+ esbuildOptions: {
1017
+ plugins: [
1018
+ {
1019
+ name: "test-utils-rewrite",
1020
+ setup(build) {
1021
+ build.onResolve({ filter: /^@vue\/test-utils$/ }, (args) => {
1022
+ const _require2 = getRequire();
1023
+ const resolved = _require2.resolve(args.path, {
1024
+ paths: [args.importer]
1025
+ });
1026
+ return { path: resolved };
1027
+ });
1028
+ }
1029
+ }
1030
+ ]
1031
+ }
1032
+ }
1033
+ };
889
1034
  }
890
1035
  }
891
- const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
892
- await keyboard2.perform(allRelease ? false : skipRelease);
893
- }
894
- return {
895
- pressed
896
- };
1036
+ ];
1037
+ };
1038
+ function tryResolve(path, paths) {
1039
+ try {
1040
+ const _require2 = getRequire();
1041
+ return _require2.resolve(path, { paths });
1042
+ } catch {
1043
+ return void 0;
1044
+ }
897
1045
  }
898
- function focusIframe() {
899
- if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
900
- window.focus();
1046
+ let _require;
1047
+ function getRequire() {
1048
+ if (!_require) {
1049
+ _require = createRequire(import.meta.url);
901
1050
  }
1051
+ return _require;
902
1052
  }
903
- function selectAll() {
904
- const element = document.activeElement;
905
- if (element && element.select) {
906
- element.select();
1053
+ function resolveCoverageFolder(vitest) {
1054
+ const options = vitest.config;
1055
+ const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
1056
+ if (typeof reporter === "string") {
1057
+ return reporter === "html";
1058
+ }
1059
+ return reporter[0] === "html";
1060
+ }) : void 0;
1061
+ if (!htmlReporter) {
1062
+ return void 0;
907
1063
  }
1064
+ const root = resolve(
1065
+ options.root || process.cwd(),
1066
+ options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
1067
+ );
1068
+ const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
1069
+ if (!subdir || typeof subdir !== "string") {
1070
+ return [root, `/${basename(root)}/`];
1071
+ }
1072
+ return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
1073
+ }
1074
+ const postfixRE = /[?#].*$/;
1075
+ function cleanUrl(url) {
1076
+ return url.replace(postfixRE, "");
908
1077
  }
909
1078
 
910
- const screenshot = async (context, name, options = {}) => {
911
- if (!context.testPath) {
912
- throw new Error(`Cannot take a screenshot without a test path`);
1079
+ class BrowserServerCDPHandler {
1080
+ constructor(session, tester) {
1081
+ this.session = session;
1082
+ this.tester = tester;
913
1083
  }
914
- const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(
915
- context.testPath,
916
- name,
917
- context.project.config
918
- );
919
- const savePath = normalize$1(path);
920
- await mkdir(dirname(path), { recursive: true });
921
- if (context.provider instanceof PlaywrightBrowserProvider) {
922
- if (options.element) {
923
- const { element: selector, ...config } = options;
924
- const element = context.iframe.locator(`${selector}`);
925
- const buffer2 = await element.screenshot({
926
- ...config,
927
- path: savePath
928
- });
929
- return returnResult(options, path, buffer2);
930
- }
931
- const buffer = await context.iframe.locator("body").screenshot({
932
- ...options,
933
- path: savePath
934
- });
935
- return returnResult(options, path, buffer);
1084
+ listenerIds = {};
1085
+ listeners = {};
1086
+ send(method, params) {
1087
+ return this.session.send(method, params);
936
1088
  }
937
- if (context.provider instanceof WebdriverBrowserProvider) {
938
- const page = context.provider.browser;
939
- if (!options.element) {
940
- const body = await page.$("body");
941
- const buffer2 = await body.saveScreenshot(savePath);
942
- return returnResult(options, path, buffer2);
1089
+ on(event, id, once = false) {
1090
+ if (!this.listenerIds[event]) {
1091
+ this.listenerIds[event] = [];
1092
+ }
1093
+ this.listenerIds[event].push(id);
1094
+ if (!this.listeners[event]) {
1095
+ this.listeners[event] = (payload) => {
1096
+ this.tester.cdpEvent(
1097
+ event,
1098
+ payload
1099
+ );
1100
+ if (once) {
1101
+ this.off(event, id);
1102
+ }
1103
+ };
1104
+ this.session.on(event, this.listeners[event]);
943
1105
  }
944
- const element = await page.$(`${options.element}`);
945
- const buffer = await element.saveScreenshot(savePath);
946
- return returnResult(options, path, buffer);
947
1106
  }
948
- throw new Error(
949
- `Provider "${context.provider.name}" does not support screenshots`
950
- );
951
- };
952
- function resolveScreenshotPath(testPath, name, config) {
953
- const dir = dirname(testPath);
954
- const base = basename(testPath);
955
- if (config.browser.screenshotDirectory) {
956
- return resolve(
957
- config.browser.screenshotDirectory,
958
- relative(config.root, dir),
959
- base,
960
- name
961
- );
1107
+ off(event, id) {
1108
+ if (!this.listenerIds[event]) {
1109
+ this.listenerIds[event] = [];
1110
+ }
1111
+ this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
1112
+ if (!this.listenerIds[event].length) {
1113
+ this.session.off(event, this.listeners[event]);
1114
+ delete this.listeners[event];
1115
+ }
962
1116
  }
963
- return resolve(dir, "__screenshots__", base, name);
964
- }
965
- function returnResult(options, path, buffer) {
966
- if (options.base64) {
967
- return { path, base64: buffer.toString("base64") };
1117
+ once(event, listener) {
1118
+ this.on(event, listener, true);
968
1119
  }
969
- return path;
970
1120
  }
971
1121
 
972
- const selectOptions = async (context, selector, userValues, options = {}) => {
1122
+ const clear = async (context, selector) => {
973
1123
  if (context.provider instanceof PlaywrightBrowserProvider) {
974
- const value = userValues;
975
1124
  const { iframe } = context;
976
- const selectElement = iframe.locator(selector);
977
- const values = await Promise.all(value.map(async (v) => {
978
- if (typeof v === "string") {
979
- return v;
980
- }
981
- const elementHandler = await iframe.locator(v.element).elementHandle();
982
- if (!elementHandler) {
983
- throw new Error(`Element not found: ${v.element}`);
984
- }
985
- return elementHandler;
986
- }));
987
- await selectElement.selectOption(values, options);
1125
+ const element = iframe.locator(selector);
1126
+ await element.clear();
988
1127
  } else if (context.provider instanceof WebdriverBrowserProvider) {
989
- const values = userValues;
990
- if (!values.length) {
991
- return;
992
- }
993
1128
  const browser = context.browser;
994
- if (values.length === 1 && "index" in values[0]) {
995
- const selectElement = browser.$(selector);
996
- await selectElement.selectByIndex(values[0].index);
997
- } else {
998
- throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
999
- }
1129
+ const element = await browser.$(selector);
1130
+ await element.clearValue();
1000
1131
  } else {
1001
- throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
1132
+ throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
1002
1133
  }
1003
1134
  };
1004
1135
 
1005
- const tab = async (context, options = {}) => {
1136
+ const click = async (context, selector, options = {}) => {
1006
1137
  const provider = context.provider;
1007
1138
  if (provider instanceof PlaywrightBrowserProvider) {
1008
- const page = context.page;
1009
- await page.keyboard.press(options.shift === true ? "Shift+Tab" : "Tab");
1010
- return;
1139
+ const tester = context.iframe;
1140
+ await tester.locator(selector).click(options);
1141
+ } else if (provider instanceof WebdriverBrowserProvider) {
1142
+ const browser = context.browser;
1143
+ await browser.$(selector).click(options);
1144
+ } else {
1145
+ throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
1011
1146
  }
1012
- if (provider instanceof WebdriverBrowserProvider) {
1013
- const { Key } = await import('webdriverio');
1147
+ };
1148
+ const dblClick = async (context, selector, options = {}) => {
1149
+ const provider = context.provider;
1150
+ if (provider instanceof PlaywrightBrowserProvider) {
1151
+ const tester = context.iframe;
1152
+ await tester.locator(selector).dblclick(options);
1153
+ } else if (provider instanceof WebdriverBrowserProvider) {
1014
1154
  const browser = context.browser;
1015
- await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab]);
1016
- return;
1155
+ await browser.$(selector).doubleClick();
1156
+ } else {
1157
+ throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
1158
+ }
1159
+ };
1160
+ const tripleClick = async (context, selector, options = {}) => {
1161
+ const provider = context.provider;
1162
+ if (provider instanceof PlaywrightBrowserProvider) {
1163
+ const tester = context.iframe;
1164
+ await tester.locator(selector).click({
1165
+ ...options,
1166
+ clickCount: 3
1167
+ });
1168
+ } else if (provider instanceof WebdriverBrowserProvider) {
1169
+ const browser = context.browser;
1170
+ await browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: await browser.$(selector) }).down().up().pause(50).down().up().pause(50).down().up().pause(50).perform();
1171
+ } else {
1172
+ throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
1017
1173
  }
1018
- throw new Error(`Provider "${provider.name}" doesn't support tab command`);
1019
1174
  };
1020
1175
 
1021
- const type = async (context, selector, text, options = {}) => {
1022
- const { skipClick = false, skipAutoClose = false } = options;
1023
- const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
1176
+ const dragAndDrop = async (context, source, target, options_) => {
1024
1177
  if (context.provider instanceof PlaywrightBrowserProvider) {
1025
- const { iframe } = context;
1026
- const element = iframe.locator(selector);
1027
- if (!skipClick) {
1028
- await element.focus();
1029
- }
1030
- await keyboardImplementation(
1031
- unreleased,
1032
- context.provider,
1033
- context.contextId,
1034
- text,
1035
- () => element.selectText(),
1036
- skipAutoClose
1178
+ const frame = await context.frame();
1179
+ await frame.dragAndDrop(
1180
+ source,
1181
+ target,
1182
+ options_
1037
1183
  );
1038
1184
  } else if (context.provider instanceof WebdriverBrowserProvider) {
1039
- const browser = context.browser;
1040
- const element = browser.$(selector);
1041
- if (!skipClick && !await element.isFocused()) {
1042
- await element.click();
1043
- }
1044
- await keyboardImplementation(
1045
- unreleased,
1046
- context.provider,
1047
- context.contextId,
1048
- text,
1049
- () => browser.execute(() => {
1050
- const element2 = document.activeElement;
1051
- if (element2) {
1052
- element2.select();
1053
- }
1054
- }),
1055
- skipAutoClose
1056
- );
1185
+ const $source = context.browser.$(source);
1186
+ const $target = context.browser.$(target);
1187
+ const options = options_ || {};
1188
+ const duration = options.duration ?? 10;
1189
+ await context.browser.action("pointer").move({ duration: 0, origin: $source, x: options.sourceX ?? 0, y: options.sourceY ?? 0 }).down({ button: 0 }).move({ duration: 0, origin: "pointer", x: 0, y: 0 }).pause(duration).move({ duration: 0, origin: $target, x: options.targetX ?? 0, y: options.targetY ?? 0 }).move({ duration: 0, origin: "pointer", x: 1, y: 0 }).move({ duration: 0, origin: "pointer", x: -1, y: 0 }).up({ button: 0 }).perform();
1057
1190
  } else {
1058
- throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
1191
+ throw new TypeError(`Provider "${context.provider.name}" does not support dragging elements`);
1059
1192
  }
1060
- return {
1061
- unreleased: Array.from(unreleased)
1062
- };
1063
1193
  };
1064
1194
 
1065
- const upload = async (context, selector, files) => {
1066
- const testPath = context.testPath;
1067
- if (!testPath) {
1068
- throw new Error(`Cannot upload files outside of a test`);
1069
- }
1070
- const testDir = dirname(testPath);
1195
+ const fill = async (context, selector, text, options = {}) => {
1071
1196
  if (context.provider instanceof PlaywrightBrowserProvider) {
1072
1197
  const { iframe } = context;
1073
- const playwrightFiles = files.map((file) => {
1074
- if (typeof file === "string") {
1075
- return resolve(testDir, file);
1076
- }
1077
- return {
1078
- name: file.name,
1079
- mimeType: file.mimeType,
1080
- buffer: Buffer.from(file.base64, "base64")
1081
- };
1082
- });
1083
- await iframe.locator(selector).setInputFiles(playwrightFiles);
1198
+ const element = iframe.locator(selector);
1199
+ await element.fill(text, options);
1084
1200
  } else if (context.provider instanceof WebdriverBrowserProvider) {
1085
- for (const file of files) {
1086
- if (typeof file !== "string") {
1087
- throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`);
1088
- }
1089
- }
1090
- const element = context.browser.$(selector);
1091
- for (const file of files) {
1092
- const filepath = resolve(testDir, file);
1093
- const remoteFilePath = await context.browser.uploadFile(filepath);
1094
- await element.addValue(remoteFilePath);
1095
- }
1201
+ const browser = context.browser;
1202
+ await browser.$(selector).setValue(text);
1096
1203
  } else {
1097
- throw new TypeError(`Provider "${context.provider.name}" does not support uploading files via userEvent.upload`);
1204
+ throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
1098
1205
  }
1099
1206
  };
1100
1207
 
1101
- var builtinCommands = {
1102
- readFile,
1103
- removeFile,
1104
- writeFile,
1105
- __vitest_fileInfo: _fileInfo,
1106
- __vitest_upload: upload,
1107
- __vitest_click: click,
1108
- __vitest_dblClick: dblClick,
1109
- __vitest_tripleClick: tripleClick,
1110
- __vitest_screenshot: screenshot,
1111
- __vitest_type: type,
1112
- __vitest_clear: clear,
1113
- __vitest_fill: fill,
1114
- __vitest_tab: tab,
1115
- __vitest_keyboard: keyboard,
1116
- __vitest_selectOptions: selectOptions,
1117
- __vitest_dragAndDrop: dragAndDrop,
1118
- __vitest_hover: hover,
1119
- __vitest_cleanup: keyboardCleanup
1208
+ const types = {
1209
+ 'application/andrew-inset': ['ez'],
1210
+ 'application/appinstaller': ['appinstaller'],
1211
+ 'application/applixware': ['aw'],
1212
+ 'application/appx': ['appx'],
1213
+ 'application/appxbundle': ['appxbundle'],
1214
+ 'application/atom+xml': ['atom'],
1215
+ 'application/atomcat+xml': ['atomcat'],
1216
+ 'application/atomdeleted+xml': ['atomdeleted'],
1217
+ 'application/atomsvc+xml': ['atomsvc'],
1218
+ 'application/atsc-dwd+xml': ['dwd'],
1219
+ 'application/atsc-held+xml': ['held'],
1220
+ 'application/atsc-rsat+xml': ['rsat'],
1221
+ 'application/automationml-aml+xml': ['aml'],
1222
+ 'application/automationml-amlx+zip': ['amlx'],
1223
+ 'application/bdoc': ['bdoc'],
1224
+ 'application/calendar+xml': ['xcs'],
1225
+ 'application/ccxml+xml': ['ccxml'],
1226
+ 'application/cdfx+xml': ['cdfx'],
1227
+ 'application/cdmi-capability': ['cdmia'],
1228
+ 'application/cdmi-container': ['cdmic'],
1229
+ 'application/cdmi-domain': ['cdmid'],
1230
+ 'application/cdmi-object': ['cdmio'],
1231
+ 'application/cdmi-queue': ['cdmiq'],
1232
+ 'application/cpl+xml': ['cpl'],
1233
+ 'application/cu-seeme': ['cu'],
1234
+ 'application/cwl': ['cwl'],
1235
+ 'application/dash+xml': ['mpd'],
1236
+ 'application/dash-patch+xml': ['mpp'],
1237
+ 'application/davmount+xml': ['davmount'],
1238
+ 'application/docbook+xml': ['dbk'],
1239
+ 'application/dssc+der': ['dssc'],
1240
+ 'application/dssc+xml': ['xdssc'],
1241
+ 'application/ecmascript': ['ecma'],
1242
+ 'application/emma+xml': ['emma'],
1243
+ 'application/emotionml+xml': ['emotionml'],
1244
+ 'application/epub+zip': ['epub'],
1245
+ 'application/exi': ['exi'],
1246
+ 'application/express': ['exp'],
1247
+ 'application/fdf': ['fdf'],
1248
+ 'application/fdt+xml': ['fdt'],
1249
+ 'application/font-tdpfr': ['pfr'],
1250
+ 'application/geo+json': ['geojson'],
1251
+ 'application/gml+xml': ['gml'],
1252
+ 'application/gpx+xml': ['gpx'],
1253
+ 'application/gxf': ['gxf'],
1254
+ 'application/gzip': ['gz'],
1255
+ 'application/hjson': ['hjson'],
1256
+ 'application/hyperstudio': ['stk'],
1257
+ 'application/inkml+xml': ['ink', 'inkml'],
1258
+ 'application/ipfix': ['ipfix'],
1259
+ 'application/its+xml': ['its'],
1260
+ 'application/java-archive': ['jar', 'war', 'ear'],
1261
+ 'application/java-serialized-object': ['ser'],
1262
+ 'application/java-vm': ['class'],
1263
+ 'application/javascript': ['*js'],
1264
+ 'application/json': ['json', 'map'],
1265
+ 'application/json5': ['json5'],
1266
+ 'application/jsonml+json': ['jsonml'],
1267
+ 'application/ld+json': ['jsonld'],
1268
+ 'application/lgr+xml': ['lgr'],
1269
+ 'application/lost+xml': ['lostxml'],
1270
+ 'application/mac-binhex40': ['hqx'],
1271
+ 'application/mac-compactpro': ['cpt'],
1272
+ 'application/mads+xml': ['mads'],
1273
+ 'application/manifest+json': ['webmanifest'],
1274
+ 'application/marc': ['mrc'],
1275
+ 'application/marcxml+xml': ['mrcx'],
1276
+ 'application/mathematica': ['ma', 'nb', 'mb'],
1277
+ 'application/mathml+xml': ['mathml'],
1278
+ 'application/mbox': ['mbox'],
1279
+ 'application/media-policy-dataset+xml': ['mpf'],
1280
+ 'application/mediaservercontrol+xml': ['mscml'],
1281
+ 'application/metalink+xml': ['metalink'],
1282
+ 'application/metalink4+xml': ['meta4'],
1283
+ 'application/mets+xml': ['mets'],
1284
+ 'application/mmt-aei+xml': ['maei'],
1285
+ 'application/mmt-usd+xml': ['musd'],
1286
+ 'application/mods+xml': ['mods'],
1287
+ 'application/mp21': ['m21', 'mp21'],
1288
+ 'application/mp4': ['*mp4', '*mpg4', 'mp4s', 'm4p'],
1289
+ 'application/msix': ['msix'],
1290
+ 'application/msixbundle': ['msixbundle'],
1291
+ 'application/msword': ['doc', 'dot'],
1292
+ 'application/mxf': ['mxf'],
1293
+ 'application/n-quads': ['nq'],
1294
+ 'application/n-triples': ['nt'],
1295
+ 'application/node': ['cjs'],
1296
+ 'application/octet-stream': [
1297
+ 'bin',
1298
+ 'dms',
1299
+ 'lrf',
1300
+ 'mar',
1301
+ 'so',
1302
+ 'dist',
1303
+ 'distz',
1304
+ 'pkg',
1305
+ 'bpk',
1306
+ 'dump',
1307
+ 'elc',
1308
+ 'deploy',
1309
+ 'exe',
1310
+ 'dll',
1311
+ 'deb',
1312
+ 'dmg',
1313
+ 'iso',
1314
+ 'img',
1315
+ 'msi',
1316
+ 'msp',
1317
+ 'msm',
1318
+ 'buffer',
1319
+ ],
1320
+ 'application/oda': ['oda'],
1321
+ 'application/oebps-package+xml': ['opf'],
1322
+ 'application/ogg': ['ogx'],
1323
+ 'application/omdoc+xml': ['omdoc'],
1324
+ 'application/onenote': ['onetoc', 'onetoc2', 'onetmp', 'onepkg'],
1325
+ 'application/oxps': ['oxps'],
1326
+ 'application/p2p-overlay+xml': ['relo'],
1327
+ 'application/patch-ops-error+xml': ['xer'],
1328
+ 'application/pdf': ['pdf'],
1329
+ 'application/pgp-encrypted': ['pgp'],
1330
+ 'application/pgp-keys': ['asc'],
1331
+ 'application/pgp-signature': ['sig', '*asc'],
1332
+ 'application/pics-rules': ['prf'],
1333
+ 'application/pkcs10': ['p10'],
1334
+ 'application/pkcs7-mime': ['p7m', 'p7c'],
1335
+ 'application/pkcs7-signature': ['p7s'],
1336
+ 'application/pkcs8': ['p8'],
1337
+ 'application/pkix-attr-cert': ['ac'],
1338
+ 'application/pkix-cert': ['cer'],
1339
+ 'application/pkix-crl': ['crl'],
1340
+ 'application/pkix-pkipath': ['pkipath'],
1341
+ 'application/pkixcmp': ['pki'],
1342
+ 'application/pls+xml': ['pls'],
1343
+ 'application/postscript': ['ai', 'eps', 'ps'],
1344
+ 'application/provenance+xml': ['provx'],
1345
+ 'application/pskc+xml': ['pskcxml'],
1346
+ 'application/raml+yaml': ['raml'],
1347
+ 'application/rdf+xml': ['rdf', 'owl'],
1348
+ 'application/reginfo+xml': ['rif'],
1349
+ 'application/relax-ng-compact-syntax': ['rnc'],
1350
+ 'application/resource-lists+xml': ['rl'],
1351
+ 'application/resource-lists-diff+xml': ['rld'],
1352
+ 'application/rls-services+xml': ['rs'],
1353
+ 'application/route-apd+xml': ['rapd'],
1354
+ 'application/route-s-tsid+xml': ['sls'],
1355
+ 'application/route-usd+xml': ['rusd'],
1356
+ 'application/rpki-ghostbusters': ['gbr'],
1357
+ 'application/rpki-manifest': ['mft'],
1358
+ 'application/rpki-roa': ['roa'],
1359
+ 'application/rsd+xml': ['rsd'],
1360
+ 'application/rss+xml': ['rss'],
1361
+ 'application/rtf': ['rtf'],
1362
+ 'application/sbml+xml': ['sbml'],
1363
+ 'application/scvp-cv-request': ['scq'],
1364
+ 'application/scvp-cv-response': ['scs'],
1365
+ 'application/scvp-vp-request': ['spq'],
1366
+ 'application/scvp-vp-response': ['spp'],
1367
+ 'application/sdp': ['sdp'],
1368
+ 'application/senml+xml': ['senmlx'],
1369
+ 'application/sensml+xml': ['sensmlx'],
1370
+ 'application/set-payment-initiation': ['setpay'],
1371
+ 'application/set-registration-initiation': ['setreg'],
1372
+ 'application/shf+xml': ['shf'],
1373
+ 'application/sieve': ['siv', 'sieve'],
1374
+ 'application/smil+xml': ['smi', 'smil'],
1375
+ 'application/sparql-query': ['rq'],
1376
+ 'application/sparql-results+xml': ['srx'],
1377
+ 'application/sql': ['sql'],
1378
+ 'application/srgs': ['gram'],
1379
+ 'application/srgs+xml': ['grxml'],
1380
+ 'application/sru+xml': ['sru'],
1381
+ 'application/ssdl+xml': ['ssdl'],
1382
+ 'application/ssml+xml': ['ssml'],
1383
+ 'application/swid+xml': ['swidtag'],
1384
+ 'application/tei+xml': ['tei', 'teicorpus'],
1385
+ 'application/thraud+xml': ['tfi'],
1386
+ 'application/timestamped-data': ['tsd'],
1387
+ 'application/toml': ['toml'],
1388
+ 'application/trig': ['trig'],
1389
+ 'application/ttml+xml': ['ttml'],
1390
+ 'application/ubjson': ['ubj'],
1391
+ 'application/urc-ressheet+xml': ['rsheet'],
1392
+ 'application/urc-targetdesc+xml': ['td'],
1393
+ 'application/voicexml+xml': ['vxml'],
1394
+ 'application/wasm': ['wasm'],
1395
+ 'application/watcherinfo+xml': ['wif'],
1396
+ 'application/widget': ['wgt'],
1397
+ 'application/winhlp': ['hlp'],
1398
+ 'application/wsdl+xml': ['wsdl'],
1399
+ 'application/wspolicy+xml': ['wspolicy'],
1400
+ 'application/xaml+xml': ['xaml'],
1401
+ 'application/xcap-att+xml': ['xav'],
1402
+ 'application/xcap-caps+xml': ['xca'],
1403
+ 'application/xcap-diff+xml': ['xdf'],
1404
+ 'application/xcap-el+xml': ['xel'],
1405
+ 'application/xcap-ns+xml': ['xns'],
1406
+ 'application/xenc+xml': ['xenc'],
1407
+ 'application/xfdf': ['xfdf'],
1408
+ 'application/xhtml+xml': ['xhtml', 'xht'],
1409
+ 'application/xliff+xml': ['xlf'],
1410
+ 'application/xml': ['xml', 'xsl', 'xsd', 'rng'],
1411
+ 'application/xml-dtd': ['dtd'],
1412
+ 'application/xop+xml': ['xop'],
1413
+ 'application/xproc+xml': ['xpl'],
1414
+ 'application/xslt+xml': ['*xsl', 'xslt'],
1415
+ 'application/xspf+xml': ['xspf'],
1416
+ 'application/xv+xml': ['mxml', 'xhvml', 'xvml', 'xvm'],
1417
+ 'application/yang': ['yang'],
1418
+ 'application/yin+xml': ['yin'],
1419
+ 'application/zip': ['zip'],
1420
+ 'audio/3gpp': ['*3gpp'],
1421
+ 'audio/aac': ['adts', 'aac'],
1422
+ 'audio/adpcm': ['adp'],
1423
+ 'audio/amr': ['amr'],
1424
+ 'audio/basic': ['au', 'snd'],
1425
+ 'audio/midi': ['mid', 'midi', 'kar', 'rmi'],
1426
+ 'audio/mobile-xmf': ['mxmf'],
1427
+ 'audio/mp3': ['*mp3'],
1428
+ 'audio/mp4': ['m4a', 'mp4a'],
1429
+ 'audio/mpeg': ['mpga', 'mp2', 'mp2a', 'mp3', 'm2a', 'm3a'],
1430
+ 'audio/ogg': ['oga', 'ogg', 'spx', 'opus'],
1431
+ 'audio/s3m': ['s3m'],
1432
+ 'audio/silk': ['sil'],
1433
+ 'audio/wav': ['wav'],
1434
+ 'audio/wave': ['*wav'],
1435
+ 'audio/webm': ['weba'],
1436
+ 'audio/xm': ['xm'],
1437
+ 'font/collection': ['ttc'],
1438
+ 'font/otf': ['otf'],
1439
+ 'font/ttf': ['ttf'],
1440
+ 'font/woff': ['woff'],
1441
+ 'font/woff2': ['woff2'],
1442
+ 'image/aces': ['exr'],
1443
+ 'image/apng': ['apng'],
1444
+ 'image/avci': ['avci'],
1445
+ 'image/avcs': ['avcs'],
1446
+ 'image/avif': ['avif'],
1447
+ 'image/bmp': ['bmp', 'dib'],
1448
+ 'image/cgm': ['cgm'],
1449
+ 'image/dicom-rle': ['drle'],
1450
+ 'image/dpx': ['dpx'],
1451
+ 'image/emf': ['emf'],
1452
+ 'image/fits': ['fits'],
1453
+ 'image/g3fax': ['g3'],
1454
+ 'image/gif': ['gif'],
1455
+ 'image/heic': ['heic'],
1456
+ 'image/heic-sequence': ['heics'],
1457
+ 'image/heif': ['heif'],
1458
+ 'image/heif-sequence': ['heifs'],
1459
+ 'image/hej2k': ['hej2'],
1460
+ 'image/hsj2': ['hsj2'],
1461
+ 'image/ief': ['ief'],
1462
+ 'image/jls': ['jls'],
1463
+ 'image/jp2': ['jp2', 'jpg2'],
1464
+ 'image/jpeg': ['jpeg', 'jpg', 'jpe'],
1465
+ 'image/jph': ['jph'],
1466
+ 'image/jphc': ['jhc'],
1467
+ 'image/jpm': ['jpm', 'jpgm'],
1468
+ 'image/jpx': ['jpx', 'jpf'],
1469
+ 'image/jxl': ['jxl'],
1470
+ 'image/jxr': ['jxr'],
1471
+ 'image/jxra': ['jxra'],
1472
+ 'image/jxrs': ['jxrs'],
1473
+ 'image/jxs': ['jxs'],
1474
+ 'image/jxsc': ['jxsc'],
1475
+ 'image/jxsi': ['jxsi'],
1476
+ 'image/jxss': ['jxss'],
1477
+ 'image/ktx': ['ktx'],
1478
+ 'image/ktx2': ['ktx2'],
1479
+ 'image/png': ['png'],
1480
+ 'image/sgi': ['sgi'],
1481
+ 'image/svg+xml': ['svg', 'svgz'],
1482
+ 'image/t38': ['t38'],
1483
+ 'image/tiff': ['tif', 'tiff'],
1484
+ 'image/tiff-fx': ['tfx'],
1485
+ 'image/webp': ['webp'],
1486
+ 'image/wmf': ['wmf'],
1487
+ 'message/disposition-notification': ['disposition-notification'],
1488
+ 'message/global': ['u8msg'],
1489
+ 'message/global-delivery-status': ['u8dsn'],
1490
+ 'message/global-disposition-notification': ['u8mdn'],
1491
+ 'message/global-headers': ['u8hdr'],
1492
+ 'message/rfc822': ['eml', 'mime'],
1493
+ 'model/3mf': ['3mf'],
1494
+ 'model/gltf+json': ['gltf'],
1495
+ 'model/gltf-binary': ['glb'],
1496
+ 'model/iges': ['igs', 'iges'],
1497
+ 'model/jt': ['jt'],
1498
+ 'model/mesh': ['msh', 'mesh', 'silo'],
1499
+ 'model/mtl': ['mtl'],
1500
+ 'model/obj': ['obj'],
1501
+ 'model/prc': ['prc'],
1502
+ 'model/step+xml': ['stpx'],
1503
+ 'model/step+zip': ['stpz'],
1504
+ 'model/step-xml+zip': ['stpxz'],
1505
+ 'model/stl': ['stl'],
1506
+ 'model/u3d': ['u3d'],
1507
+ 'model/vrml': ['wrl', 'vrml'],
1508
+ 'model/x3d+binary': ['*x3db', 'x3dbz'],
1509
+ 'model/x3d+fastinfoset': ['x3db'],
1510
+ 'model/x3d+vrml': ['*x3dv', 'x3dvz'],
1511
+ 'model/x3d+xml': ['x3d', 'x3dz'],
1512
+ 'model/x3d-vrml': ['x3dv'],
1513
+ 'text/cache-manifest': ['appcache', 'manifest'],
1514
+ 'text/calendar': ['ics', 'ifb'],
1515
+ 'text/coffeescript': ['coffee', 'litcoffee'],
1516
+ 'text/css': ['css'],
1517
+ 'text/csv': ['csv'],
1518
+ 'text/html': ['html', 'htm', 'shtml'],
1519
+ 'text/jade': ['jade'],
1520
+ 'text/javascript': ['js', 'mjs'],
1521
+ 'text/jsx': ['jsx'],
1522
+ 'text/less': ['less'],
1523
+ 'text/markdown': ['md', 'markdown'],
1524
+ 'text/mathml': ['mml'],
1525
+ 'text/mdx': ['mdx'],
1526
+ 'text/n3': ['n3'],
1527
+ 'text/plain': ['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'ini'],
1528
+ 'text/richtext': ['rtx'],
1529
+ 'text/rtf': ['*rtf'],
1530
+ 'text/sgml': ['sgml', 'sgm'],
1531
+ 'text/shex': ['shex'],
1532
+ 'text/slim': ['slim', 'slm'],
1533
+ 'text/spdx': ['spdx'],
1534
+ 'text/stylus': ['stylus', 'styl'],
1535
+ 'text/tab-separated-values': ['tsv'],
1536
+ 'text/troff': ['t', 'tr', 'roff', 'man', 'me', 'ms'],
1537
+ 'text/turtle': ['ttl'],
1538
+ 'text/uri-list': ['uri', 'uris', 'urls'],
1539
+ 'text/vcard': ['vcard'],
1540
+ 'text/vtt': ['vtt'],
1541
+ 'text/wgsl': ['wgsl'],
1542
+ 'text/xml': ['*xml'],
1543
+ 'text/yaml': ['yaml', 'yml'],
1544
+ 'video/3gpp': ['3gp', '3gpp'],
1545
+ 'video/3gpp2': ['3g2'],
1546
+ 'video/h261': ['h261'],
1547
+ 'video/h263': ['h263'],
1548
+ 'video/h264': ['h264'],
1549
+ 'video/iso.segment': ['m4s'],
1550
+ 'video/jpeg': ['jpgv'],
1551
+ 'video/jpm': ['*jpm', '*jpgm'],
1552
+ 'video/mj2': ['mj2', 'mjp2'],
1553
+ 'video/mp2t': ['ts', 'm2t', 'm2ts', 'mts'],
1554
+ 'video/mp4': ['mp4', 'mp4v', 'mpg4'],
1555
+ 'video/mpeg': ['mpeg', 'mpg', 'mpe', 'm1v', 'm2v'],
1556
+ 'video/ogg': ['ogv'],
1557
+ 'video/quicktime': ['qt', 'mov'],
1558
+ 'video/webm': ['webm'],
1120
1559
  };
1560
+ Object.freeze(types);
1121
1561
 
1122
- const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
1123
- const ID_CONTEXT = "@vitest/browser/context";
1124
- const __dirname = dirname(fileURLToPath(import.meta.url));
1125
- function BrowserContext(server) {
1126
- const project = server.project;
1127
- project.config.browser.commands ??= {};
1128
- for (const [name, command] of Object.entries(builtinCommands)) {
1129
- project.config.browser.commands[name] ??= command;
1130
- }
1131
- for (const command in project.config.browser.commands) {
1132
- if (!/^[a-z_$][\w$]*$/i.test(command)) {
1133
- throw new Error(
1134
- `Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`
1135
- );
1562
+ var __classPrivateFieldGet = (null && null.__classPrivateFieldGet) || function (receiver, state, kind, f) {
1563
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
1564
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
1565
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
1566
+ };
1567
+ var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
1568
+ class Mime {
1569
+ constructor(...args) {
1570
+ _Mime_extensionToType.set(this, new Map());
1571
+ _Mime_typeToExtension.set(this, new Map());
1572
+ _Mime_typeToExtensions.set(this, new Map());
1573
+ for (const arg of args) {
1574
+ this.define(arg);
1575
+ }
1136
1576
  }
1137
- }
1138
- return {
1139
- name: "vitest:browser:virtual-module:context",
1140
- enforce: "pre",
1141
- resolveId(id) {
1142
- if (id === ID_CONTEXT) {
1143
- return VIRTUAL_ID_CONTEXT;
1144
- }
1145
- },
1146
- load(id) {
1147
- if (id === VIRTUAL_ID_CONTEXT) {
1148
- return generateContextFile.call(this, server);
1149
- }
1577
+ define(typeMap, force = false) {
1578
+ for (let [type, extensions] of Object.entries(typeMap)) {
1579
+ type = type.toLowerCase();
1580
+ extensions = extensions.map((ext) => ext.toLowerCase());
1581
+ if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) {
1582
+ __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, new Set());
1583
+ }
1584
+ const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
1585
+ let first = true;
1586
+ for (let extension of extensions) {
1587
+ const starred = extension.startsWith('*');
1588
+ extension = starred ? extension.slice(1) : extension;
1589
+ allExtensions?.add(extension);
1590
+ if (first) {
1591
+ __classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
1592
+ }
1593
+ first = false;
1594
+ if (starred)
1595
+ continue;
1596
+ const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
1597
+ if (currentType && currentType != type && !force) {
1598
+ throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
1599
+ }
1600
+ __classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
1601
+ }
1602
+ }
1603
+ return this;
1604
+ }
1605
+ getType(path) {
1606
+ if (typeof path !== 'string')
1607
+ return null;
1608
+ const last = path.replace(/^.*[/\\]/, '').toLowerCase();
1609
+ const ext = last.replace(/^.*\./, '').toLowerCase();
1610
+ const hasPath = last.length < path.length;
1611
+ const hasDot = ext.length < last.length - 1;
1612
+ if (!hasDot && hasPath)
1613
+ return null;
1614
+ return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
1615
+ }
1616
+ getExtension(type) {
1617
+ if (typeof type !== 'string')
1618
+ return null;
1619
+ type = type?.split?.(';')[0];
1620
+ return ((type && __classPrivateFieldGet(this, _Mime_typeToExtension, "f").get(type.trim().toLowerCase())) ?? null);
1621
+ }
1622
+ getAllExtensions(type) {
1623
+ if (typeof type !== 'string')
1624
+ return null;
1625
+ return __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type.toLowerCase()) ?? null;
1626
+ }
1627
+ _freeze() {
1628
+ this.define = () => {
1629
+ throw new Error('define() not allowed for built-in Mime objects. See https://github.com/broofa/mime/blob/main/README.md#custom-mime-instances');
1630
+ };
1631
+ Object.freeze(this);
1632
+ for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
1633
+ Object.freeze(extensions);
1634
+ }
1635
+ return this;
1636
+ }
1637
+ _getTestState() {
1638
+ return {
1639
+ types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
1640
+ extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f"),
1641
+ };
1150
1642
  }
1151
- };
1152
1643
  }
1153
- async function generateContextFile(server) {
1154
- const commands = Object.keys(server.project.config.browser.commands ?? {});
1155
- const filepathCode = "__vitest_worker__.filepath || __vitest_worker__.current?.file?.filepath || undefined";
1156
- const provider = server.provider;
1157
- const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
1158
- return ` ["${command}"]: (...args) => rpc().triggerCommand(contextId, "${command}", filepath(), args),`;
1159
- }).join("\n");
1160
- const userEventNonProviderImport = await getUserEventImport(
1161
- provider,
1162
- this.resolve.bind(this)
1163
- );
1164
- const distContextPath = slash(`/@fs/${resolve(__dirname, "context.js")}`);
1165
- return `
1166
- import { page, createUserEvent, cdp } from '${distContextPath}'
1167
- ${userEventNonProviderImport}
1168
- const filepath = () => ${filepathCode}
1169
- const rpc = () => __vitest_worker__.rpc
1170
- const contextId = __vitest_browser_runner__.contextId
1644
+ _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
1171
1645
 
1172
- export const server = {
1173
- platform: ${JSON.stringify(process.platform)},
1174
- version: ${JSON.stringify(process.version)},
1175
- provider: ${JSON.stringify(provider.name)},
1176
- browser: ${JSON.stringify(server.project.config.browser.name)},
1177
- commands: {
1178
- ${commandsCode}
1179
- },
1180
- config: __vitest_browser_runner__.config,
1181
- }
1182
- export const commands = server.commands
1183
- export const userEvent = createUserEvent(_userEventSetup)
1184
- export { page, cdp }
1185
- `;
1186
- }
1187
- async function getUserEventImport(provider, resolve2) {
1188
- if (provider.name !== "preview") {
1189
- return "const _userEventSetup = undefined";
1190
- }
1191
- const resolved = await resolve2("@testing-library/user-event", __dirname);
1192
- if (!resolved) {
1193
- throw new Error(`Failed to resolve user-event package from ${__dirname}`);
1194
- }
1195
- return `import { userEvent as __vitest_user_event__ } from '${slash(`/@fs/${resolved.id}`)}'
1196
- const _userEventSetup = __vitest_user_event__
1197
- `;
1198
- }
1646
+ var mime = new Mime(types)._freeze();
1199
1647
 
1200
- function replacer(code, values) {
1201
- return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
1202
- }
1203
- const builtinProviders = ["webdriverio", "playwright", "preview"];
1204
- async function getBrowserProvider(options, project) {
1205
- if (options.provider == null || builtinProviders.includes(options.provider)) {
1206
- const providers = await import('./providers.js');
1207
- const provider = options.provider || "preview";
1208
- return providers[provider];
1209
- }
1210
- let customProviderModule;
1211
- try {
1212
- customProviderModule = await project.runner.executeId(
1213
- options.provider
1214
- );
1215
- } catch (error) {
1216
- throw new Error(
1217
- `Failed to load custom BrowserProvider from ${options.provider}`,
1218
- { cause: error }
1219
- );
1220
- }
1221
- if (customProviderModule.default == null) {
1648
+ function assertFileAccess(path, project) {
1649
+ if (!isFileServingAllowed(path, project.vite) && !isFileServingAllowed(path, project.vitest.server)) {
1222
1650
  throw new Error(
1223
- `Custom BrowserProvider loaded from ${options.provider} was not the default export`
1651
+ `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
1224
1652
  );
1225
1653
  }
1226
- return customProviderModule.default;
1227
1654
  }
1228
-
1229
- async function resolveOrchestrator(server, url, res) {
1230
- const project = server.project;
1231
- let contextId = url.searchParams.get("contextId");
1232
- if (!contextId) {
1233
- const contexts = [...server.state.orchestrators.keys()];
1234
- contextId = contexts[contexts.length - 1] ?? "none";
1235
- }
1236
- const files = server.state.getContext(contextId)?.files ?? [];
1237
- const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1238
- const injector = replacer(injectorJs, {
1239
- __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1240
- __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1241
- __VITEST_VITE_CONFIG__: JSON.stringify({
1242
- root: server.vite.config.root
1243
- }),
1244
- __VITEST_FILES__: JSON.stringify(files),
1245
- __VITEST_TYPE__: '"orchestrator"',
1246
- __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1247
- __VITEST_TESTER_ID__: '"none"',
1248
- __VITEST_PROVIDED_CONTEXT__: "{}"
1249
- });
1250
- res.removeHeader("Content-Security-Policy");
1251
- if (!server.orchestratorScripts) {
1252
- server.orchestratorScripts = (await server.formatScripts(
1253
- project.config.browser.orchestratorScripts
1254
- )).map((script) => {
1255
- let html = "<script ";
1256
- for (const attr in script.attrs || {}) {
1257
- html += `${attr}="${script.attrs[attr]}" `;
1258
- }
1259
- html += `>${script.children}<\/script>`;
1260
- return html;
1261
- }).join("\n");
1655
+ const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
1656
+ const filepath = resolve$1(dirname$1(testPath), path);
1657
+ assertFileAccess(filepath, project);
1658
+ if (typeof options === "object" && !options.encoding) {
1659
+ options.encoding = "utf-8";
1262
1660
  }
1263
- let baseHtml = typeof server.orchestratorHtml === "string" ? server.orchestratorHtml : await server.orchestratorHtml;
1264
- if (project.config.browser.ui) {
1265
- const manifestContent = server.manifest instanceof Promise ? await server.manifest : server.manifest;
1266
- const jsEntry = manifestContent["orchestrator.html"].file;
1267
- const base = server.vite.config.base || "/";
1268
- baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
1269
- "<!-- !LOAD_METADATA! -->",
1270
- [
1271
- "{__VITEST_INJECTOR__}",
1272
- "{__VITEST_ERROR_CATCHER__}",
1273
- "{__VITEST_SCRIPTS__}",
1274
- `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
1275
- ].join("\n")
1276
- );
1661
+ return promises.readFile(filepath, options);
1662
+ };
1663
+ const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
1664
+ const filepath = resolve$1(dirname$1(testPath), path);
1665
+ assertFileAccess(filepath, project);
1666
+ const dir = dirname$1(filepath);
1667
+ if (!fs.existsSync(dir)) {
1668
+ await promises.mkdir(dir, { recursive: true });
1277
1669
  }
1278
- return replacer(baseHtml, {
1279
- __VITEST_FAVICON__: server.faviconUrl,
1280
- __VITEST_TITLE__: "Vitest Browser Runner",
1281
- __VITEST_SCRIPTS__: server.orchestratorScripts,
1282
- __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
1283
- __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
1284
- __VITEST_CONTEXT_ID__: JSON.stringify(contextId)
1285
- });
1286
- }
1287
-
1288
- /// <reference types="../types/index.d.ts" />
1289
-
1290
- // (c) 2020-present Andrea Giammarchi
1291
-
1292
- const {parse: $parse, stringify: $stringify} = JSON;
1293
- const {keys} = Object;
1294
-
1295
- const Primitive = String; // it could be Number
1296
- const primitive = 'string'; // it could be 'number'
1297
-
1298
- const ignore = {};
1299
- const object = 'object';
1300
-
1301
- const noop = (_, value) => value;
1302
-
1303
- const primitives = value => (
1304
- value instanceof Primitive ? Primitive(value) : value
1305
- );
1306
-
1307
- const Primitives = (_, value) => (
1308
- typeof value === primitive ? new Primitive(value) : value
1309
- );
1670
+ await promises.writeFile(filepath, data, options);
1671
+ };
1672
+ const removeFile = async ({ project, testPath = process.cwd() }, path) => {
1673
+ const filepath = resolve$1(dirname$1(testPath), path);
1674
+ assertFileAccess(filepath, project);
1675
+ await promises.rm(filepath);
1676
+ };
1677
+ const _fileInfo = async ({ project, testPath = process.cwd() }, path, encoding) => {
1678
+ const filepath = resolve$1(dirname$1(testPath), path);
1679
+ assertFileAccess(filepath, project);
1680
+ const content = await promises.readFile(filepath, encoding || "base64");
1681
+ return {
1682
+ content,
1683
+ basename: basename$1(filepath),
1684
+ mime: mime.getType(filepath)
1685
+ };
1686
+ };
1310
1687
 
1311
- const revive = (input, parsed, output, $) => {
1312
- const lazy = [];
1313
- for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
1314
- const k = ke[y];
1315
- const value = output[k];
1316
- if (value instanceof Primitive) {
1317
- const tmp = input[value];
1318
- if (typeof tmp === object && !parsed.has(tmp)) {
1319
- parsed.add(tmp);
1320
- output[k] = ignore;
1321
- lazy.push({k, a: [input, parsed, tmp, $]});
1322
- }
1323
- else
1324
- output[k] = $.call(output, k, tmp);
1325
- }
1326
- else if (output[k] !== ignore)
1327
- output[k] = $.call(output, k, value);
1328
- }
1329
- for (let {length} = lazy, i = 0; i < length; i++) {
1330
- const {k, a} = lazy[i];
1331
- output[k] = $.call(output, k, revive.apply(null, a));
1688
+ const hover = async (context, selector, options = {}) => {
1689
+ if (context.provider instanceof PlaywrightBrowserProvider) {
1690
+ await context.iframe.locator(selector).hover(options);
1691
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
1692
+ const browser = context.browser;
1693
+ await browser.$(selector).moveTo(options);
1694
+ } else {
1695
+ throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
1332
1696
  }
1333
- return output;
1334
1697
  };
1335
1698
 
1336
- const set = (known, input, value) => {
1337
- const index = Primitive(input.push(value) - 1);
1338
- known.set(value, index);
1339
- return index;
1340
- };
1699
+ var clickableInputTypes;
1700
+ (function(clickableInputTypes) {
1701
+ clickableInputTypes["button"] = "button";
1702
+ clickableInputTypes["color"] = "color";
1703
+ clickableInputTypes["file"] = "file";
1704
+ clickableInputTypes["image"] = "image";
1705
+ clickableInputTypes["reset"] = "reset";
1706
+ clickableInputTypes["submit"] = "submit";
1707
+ clickableInputTypes["checkbox"] = "checkbox";
1708
+ clickableInputTypes["radio"] = "radio";
1709
+ })(clickableInputTypes || (clickableInputTypes = {}));
1341
1710
 
1342
- /**
1343
- * Converts a specialized flatted string into a JS value.
1344
- * @param {string} text
1345
- * @param {(this: any, key: string, value: any) => any} [reviver]
1346
- * @returns {any}
1347
- */
1348
- const parse = (text, reviver) => {
1349
- const input = $parse(text, Primitives).map(primitives);
1350
- const value = input[0];
1351
- const $ = reviver || noop;
1352
- const tmp = typeof value === object && value ?
1353
- revive(input, new Set, value, $) :
1354
- value;
1355
- return $.call({'': tmp}, '', tmp);
1356
- };
1711
+ var editableInputTypes;
1712
+ (function(editableInputTypes) {
1713
+ editableInputTypes["text"] = "text";
1714
+ editableInputTypes["date"] = "date";
1715
+ editableInputTypes["datetime-local"] = "datetime-local";
1716
+ editableInputTypes["email"] = "email";
1717
+ editableInputTypes["month"] = "month";
1718
+ editableInputTypes["number"] = "number";
1719
+ editableInputTypes["password"] = "password";
1720
+ editableInputTypes["search"] = "search";
1721
+ editableInputTypes["tel"] = "tel";
1722
+ editableInputTypes["time"] = "time";
1723
+ editableInputTypes["url"] = "url";
1724
+ editableInputTypes["week"] = "week";
1725
+ })(editableInputTypes || (editableInputTypes = {}));
1726
+
1727
+ var maxLengthSupportedTypes;
1728
+ (function(maxLengthSupportedTypes) {
1729
+ maxLengthSupportedTypes["email"] = "email";
1730
+ maxLengthSupportedTypes["password"] = "password";
1731
+ maxLengthSupportedTypes["search"] = "search";
1732
+ maxLengthSupportedTypes["telephone"] = "telephone";
1733
+ maxLengthSupportedTypes["text"] = "text";
1734
+ maxLengthSupportedTypes["url"] = "url";
1735
+ })(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
1357
1736
 
1737
+ var bracketDict;
1738
+ (function(bracketDict) {
1739
+ bracketDict["{"] = "}";
1740
+ bracketDict["["] = "]";
1741
+ })(bracketDict || (bracketDict = {}));
1358
1742
  /**
1359
- * Converts a JS value into a specialized flatted string.
1360
- * @param {any} value
1361
- * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
1362
- * @param {string | number | undefined} [space]
1363
- * @returns {string}
1364
- */
1365
- const stringify = (value, replacer, space) => {
1366
- const $ = replacer && typeof replacer === object ?
1367
- (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
1368
- (replacer || noop);
1369
- const known = new Map;
1370
- const input = [];
1371
- const output = [];
1372
- let i = +set(known, input, $.call({'': value}, '', value));
1373
- let firstRun = !i;
1374
- while (i < input.length) {
1375
- firstRun = true;
1376
- output[i] = $stringify(input[i++], replace, space);
1377
- }
1378
- return '[' + output.join(',') + ']';
1379
- function replace(key, value) {
1380
- if (firstRun) {
1381
- firstRun = !firstRun;
1382
- return value;
1743
+ * Read the next key definition from user input
1744
+ *
1745
+ * Describe key per `{descriptor}` or `[descriptor]`.
1746
+ * Everything else will be interpreted as a single character as descriptor - e.g. `a`.
1747
+ * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
1748
+ * A previously pressed key can be released per `{/descriptor}`.
1749
+ * Keeping the key pressed can be written as `{descriptor>}`.
1750
+ * When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`.
1751
+ * You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key.
1752
+ */ function readNextDescriptor(text, context) {
1753
+ let pos = 0;
1754
+ const startBracket = text[pos] in bracketDict ? text[pos] : '';
1755
+ pos += startBracket.length;
1756
+ const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text);
1757
+ const type = isEscapedChar ? '' : startBracket;
1758
+ return {
1759
+ type,
1760
+ ...type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type)
1761
+ };
1762
+ }
1763
+ function readPrintableChar(text, pos, context) {
1764
+ const descriptor = text[pos];
1765
+ assertDescriptor(descriptor, text, pos);
1766
+ pos += descriptor.length;
1767
+ return {
1768
+ consumedLength: pos,
1769
+ descriptor,
1770
+ releasePrevious: false,
1771
+ releaseSelf: true,
1772
+ repeat: 1
1773
+ };
1774
+ }
1775
+ function readTag(text, pos, startBracket, context) {
1776
+ var _text_slice_match, _text_slice_match1;
1777
+ const releasePreviousModifier = text[pos] === '/' ? '/' : '';
1778
+ pos += releasePreviousModifier.length;
1779
+ const escapedDescriptor = startBracket === '{' && text[pos] === '\\';
1780
+ pos += Number(escapedDescriptor);
1781
+ const descriptor = escapedDescriptor ? text[pos] : (_text_slice_match = text.slice(pos).match(startBracket === '{' ? /^\w+|^[^}>/]/ : /^\w+/)) === null || _text_slice_match === void 0 ? void 0 : _text_slice_match[0];
1782
+ assertDescriptor(descriptor, text, pos);
1783
+ pos += descriptor.length;
1784
+ var _text_slice_match_;
1785
+ const repeatModifier = (_text_slice_match_ = (_text_slice_match1 = text.slice(pos).match(/^>\d+/)) === null || _text_slice_match1 === void 0 ? void 0 : _text_slice_match1[0]) !== null && _text_slice_match_ !== void 0 ? _text_slice_match_ : '';
1786
+ pos += repeatModifier.length;
1787
+ const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
1788
+ pos += releaseSelfModifier.length;
1789
+ const expectedEndBracket = bracketDict[startBracket];
1790
+ const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
1791
+ if (!endBracket) {
1792
+ throw new Error(getErrorMessage([
1793
+ !repeatModifier && 'repeat modifier',
1794
+ !releaseSelfModifier && 'release modifier',
1795
+ `"${expectedEndBracket}"`
1796
+ ].filter(Boolean).join(' or '), text[pos], text));
1383
1797
  }
1384
- const after = $.call(this, key, value);
1385
- switch (typeof after) {
1386
- case object:
1387
- if (after === null) return after;
1388
- case primitive:
1389
- return known.get(after) || set(known, input, after);
1798
+ pos += endBracket.length;
1799
+ return {
1800
+ consumedLength: pos,
1801
+ descriptor,
1802
+ releasePrevious: !!releasePreviousModifier,
1803
+ repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
1804
+ releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier)
1805
+ };
1806
+ }
1807
+ function assertDescriptor(descriptor, text, pos, context) {
1808
+ if (!descriptor) {
1809
+ throw new Error(getErrorMessage('key descriptor', text[pos], text));
1390
1810
  }
1391
- return after;
1392
- }
1393
- };
1811
+ }
1812
+ function hasReleaseSelf(releaseSelfModifier, repeatModifier) {
1813
+ if (releaseSelfModifier) {
1814
+ return releaseSelfModifier === '/';
1815
+ }
1816
+ if (repeatModifier) {
1817
+ return false;
1818
+ }
1819
+ }
1820
+ function getErrorMessage(expected, found, text, context) {
1821
+ return `Expected ${expected} but found "${found !== null && found !== void 0 ? found : ''}" in "${text}"
1822
+ See ${`https://testing-library.com/docs/user-event/keyboard`}
1823
+ for more information about how userEvent parses your input.`;
1824
+ }
1394
1825
 
1395
- async function resolveTester(server, url, res, next) {
1396
- const csp = res.getHeader("Content-Security-Policy");
1397
- if (typeof csp === "string") {
1398
- res.setHeader(
1399
- "Content-Security-Policy",
1400
- csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *")
1401
- );
1402
- }
1403
- const { contextId, testFile } = server.resolveTesterUrl(url.pathname);
1404
- const project = server.project;
1405
- const state = server.state;
1406
- const { testFiles } = await project.globTestFiles();
1407
- const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
1408
- const iframeId = JSON.stringify(testFile);
1409
- const context = state.getContext(contextId);
1410
- const files = context?.files ?? [];
1411
- const method = context?.method ?? "run";
1412
- const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1413
- const injector = replacer(injectorJs, {
1414
- __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1415
- __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1416
- __VITEST_FILES__: JSON.stringify(files),
1417
- __VITEST_VITE_CONFIG__: JSON.stringify({
1418
- root: server.vite.config.root
1419
- }),
1420
- __VITEST_TYPE__: '"tester"',
1421
- __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1422
- __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
1423
- __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
1424
- });
1425
- const testerHtml = typeof server.testerHtml === "string" ? server.testerHtml : await server.testerHtml;
1426
- try {
1427
- const url2 = join("/@fs/", server.testerFilepath);
1428
- const indexhtml = await server.vite.transformIndexHtml(url2, testerHtml);
1429
- return replacer(indexhtml, {
1430
- __VITEST_FAVICON__: server.faviconUrl,
1431
- __VITEST_INJECTOR__: injector,
1432
- __VITEST_APPEND__: `
1433
- __vitest_browser_runner__.runningFiles = ${tests}
1434
- __vitest_browser_runner__.iframeId = ${iframeId}
1435
- __vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
1436
- document.querySelector('script[data-vitest-append]').remove()
1437
- `
1438
- });
1439
- } catch (err) {
1440
- context?.reject(err);
1441
- next(err);
1442
- }
1443
- }
1826
+ var ApiLevel;
1827
+ (function(ApiLevel) {
1828
+ ApiLevel[ApiLevel["Trigger"] = 2] = "Trigger";
1829
+ ApiLevel[ApiLevel["Call"] = 1] = "Call";
1830
+ })(ApiLevel || (ApiLevel = {}));
1444
1831
 
1445
- const versionRegexp = /(?:\?|&)v=\w{8}/;
1446
- var BrowserPlugin = (browserServer, base = "/") => {
1447
- const project = browserServer.project;
1448
- function isPackageExists(pkg, root) {
1449
- return browserServer.project.ctx.packageInstaller.isPackageExists?.(pkg, {
1450
- paths: [root]
1451
- });
1452
- }
1453
- return [
1832
+ var PointerEventsCheckLevel;
1833
+ (function(PointerEventsCheckLevel) {
1834
+ /**
1835
+ * Check pointer events on every user interaction that triggers a bunch of events.
1836
+ * E.g. once for releasing a mouse button even though this triggers `pointerup`, `mouseup`, `click`, etc...
1837
+ */ PointerEventsCheckLevel[PointerEventsCheckLevel["EachTrigger"] = 4] = "EachTrigger";
1838
+ /** Check each target once per call to pointer (related) API */ PointerEventsCheckLevel[PointerEventsCheckLevel["EachApiCall"] = 2] = "EachApiCall";
1839
+ /** Check each event target once */ PointerEventsCheckLevel[PointerEventsCheckLevel["EachTarget"] = 1] = "EachTarget";
1840
+ /** No pointer events check */ PointerEventsCheckLevel[PointerEventsCheckLevel["Never"] = 0] = "Never";
1841
+ })(PointerEventsCheckLevel || (PointerEventsCheckLevel = {}));
1842
+
1843
+ var DOM_KEY_LOCATION;
1844
+ (function(DOM_KEY_LOCATION) {
1845
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["STANDARD"] = 0] = "STANDARD";
1846
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["LEFT"] = 1] = "LEFT";
1847
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["RIGHT"] = 2] = "RIGHT";
1848
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["NUMPAD"] = 3] = "NUMPAD";
1849
+ })(DOM_KEY_LOCATION || (DOM_KEY_LOCATION = {}));
1850
+
1851
+ /**
1852
+ * Mapping for a default US-104-QWERTY keyboard
1853
+ */ const defaultKeyMap = [
1854
+ // alphanumeric keys
1855
+ ...'0123456789'.split('').map((c)=>({
1856
+ code: `Digit${c}`,
1857
+ key: c
1858
+ })),
1859
+ ...')!@#$%^&*('.split('').map((c, i)=>({
1860
+ code: `Digit${i}`,
1861
+ key: c,
1862
+ shiftKey: true
1863
+ })),
1864
+ ...'abcdefghijklmnopqrstuvwxyz'.split('').map((c)=>({
1865
+ code: `Key${c.toUpperCase()}`,
1866
+ key: c
1867
+ })),
1868
+ ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((c)=>({
1869
+ code: `Key${c}`,
1870
+ key: c,
1871
+ shiftKey: true
1872
+ })),
1873
+ // alphanumeric block - functional
1454
1874
  {
1455
- enforce: "pre",
1456
- name: "vitest:browser",
1457
- async configureServer(server) {
1458
- browserServer.setServer(server);
1459
- server.middlewares.use(function vitestHeaders(_req, res, next) {
1460
- const headers = server.config.server.headers;
1461
- if (headers) {
1462
- for (const name in headers) {
1463
- res.setHeader(name, headers[name]);
1464
- }
1465
- }
1466
- next();
1467
- });
1468
- server.middlewares.use(async function vitestBrowserMode(req, res, next) {
1469
- if (!req.url || !browserServer.provider) {
1470
- return next();
1471
- }
1472
- const url = new URL(req.url, "http://localhost");
1473
- if (!url.pathname.startsWith(browserServer.prefixTesterUrl) && url.pathname !== base) {
1474
- return next();
1475
- }
1476
- res.setHeader(
1477
- "Cache-Control",
1478
- "no-cache, max-age=0, must-revalidate"
1479
- );
1480
- res.setHeader("Content-Type", "text/html; charset=utf-8");
1481
- res.removeHeader("X-Frame-Options");
1482
- if (url.pathname === base) {
1483
- const html2 = await resolveOrchestrator(browserServer, url, res);
1484
- res.write(html2, "utf-8");
1485
- res.end();
1486
- return;
1487
- }
1488
- const html = await resolveTester(browserServer, url, res, next);
1489
- if (html) {
1490
- res.write(html, "utf-8");
1491
- res.end();
1492
- }
1493
- });
1494
- server.middlewares.use(
1495
- `${base}favicon.svg`,
1496
- (_, res) => {
1497
- const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
1498
- res.write(content, "utf-8");
1499
- res.end();
1500
- }
1501
- );
1502
- const coverageFolder = resolveCoverageFolder(project);
1503
- const coveragePath = coverageFolder ? coverageFolder[1] : void 0;
1504
- if (coveragePath && base === coveragePath) {
1505
- throw new Error(
1506
- `The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`
1507
- );
1508
- }
1509
- if (coverageFolder) {
1510
- server.middlewares.use(
1511
- coveragePath,
1512
- sirv(coverageFolder[0], {
1513
- single: true,
1514
- dev: true,
1515
- setHeaders: (res) => {
1516
- res.setHeader(
1517
- "Cache-Control",
1518
- "public,max-age=0,must-revalidate"
1519
- );
1520
- }
1521
- })
1522
- );
1523
- }
1524
- const screenshotFailures = project.config.browser.ui && project.config.browser.screenshotFailures;
1525
- if (screenshotFailures) {
1526
- server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
1527
- if (!req.url || !browserServer.provider) {
1528
- res.statusCode = 404;
1529
- res.end();
1530
- return;
1531
- }
1532
- const url = new URL(req.url, "http://localhost");
1533
- const file = url.searchParams.get("file");
1534
- if (!file) {
1535
- res.statusCode = 404;
1536
- res.end();
1537
- return;
1538
- }
1539
- let stat;
1540
- try {
1541
- stat = lstatSync(file);
1542
- } catch {
1543
- }
1544
- if (!stat?.isFile()) {
1545
- res.statusCode = 404;
1546
- res.end();
1547
- return;
1548
- }
1549
- const ext = extname(file);
1550
- const buffer = readFileSync(file);
1551
- res.setHeader(
1552
- "Cache-Control",
1553
- "public,max-age=0,must-revalidate"
1554
- );
1555
- res.setHeader("Content-Length", buffer.length);
1556
- res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
1557
- res.end(buffer);
1558
- });
1559
- }
1560
- server.middlewares.use((req, res, next) => {
1561
- if (req.url && versionRegexp.test(req.url) && !req.url.includes("chunk-")) {
1562
- res.setHeader("Cache-Control", "no-cache");
1563
- const setHeader = res.setHeader.bind(res);
1564
- res.setHeader = function(name, value) {
1565
- if (name === "Cache-Control") {
1566
- return res;
1567
- }
1568
- return setHeader(name, value);
1569
- };
1570
- }
1571
- next();
1572
- });
1573
- }
1875
+ code: 'Space',
1876
+ key: ' '
1574
1877
  },
1575
1878
  {
1576
- name: "vitest:browser:tests",
1577
- enforce: "pre",
1578
- async config() {
1579
- const { testFiles: allTestFiles } = await project.globTestFiles();
1580
- const browserTestFiles = allTestFiles.filter(
1581
- (file) => getFilePoolName(project, file) === "browser"
1582
- );
1583
- const setupFiles = toArray(project.config.setupFiles);
1584
- const define = {};
1585
- for (const env in project.config.env || {}) {
1586
- const stringValue = JSON.stringify(project.config.env[env]);
1587
- define[`import.meta.env.${env}`] = stringValue;
1588
- }
1589
- const entries = [
1590
- ...browserTestFiles,
1591
- ...setupFiles,
1592
- resolve(distDir, "index.js"),
1593
- resolve(distDir, "browser.js"),
1594
- resolve(distDir, "runners.js"),
1595
- resolve(distDir, "utils.js"),
1596
- ...project.config.snapshotSerializers || []
1597
- ];
1598
- const exclude = [
1599
- "vitest",
1600
- "vitest/utils",
1601
- "vitest/browser",
1602
- "vitest/runners",
1603
- "@vitest/browser",
1604
- "@vitest/browser/client",
1605
- "@vitest/utils",
1606
- "@vitest/utils/source-map",
1607
- "@vitest/runner",
1608
- "@vitest/spy",
1609
- "@vitest/utils/error",
1610
- "@vitest/snapshot",
1611
- "@vitest/expect",
1612
- "std-env",
1613
- "tinybench",
1614
- "tinyspy",
1615
- "tinyrainbow",
1616
- "pathe",
1617
- "msw",
1618
- "msw/browser"
1619
- ];
1620
- if (typeof project.config.diff === "string") {
1621
- entries.push(project.config.diff);
1622
- }
1623
- if (project.ctx.coverageProvider) {
1624
- const coverage = project.ctx.config.coverage;
1625
- const provider = coverage.provider;
1626
- if (provider === "v8") {
1627
- const path = tryResolve("@vitest/coverage-v8", [project.config.root]);
1628
- if (path) {
1629
- entries.push(path);
1630
- exclude.push("@vitest/coverage-v8/browser");
1631
- }
1632
- } else if (provider === "istanbul") {
1633
- const path = tryResolve("@vitest/coverage-istanbul", [project.config.root]);
1634
- if (path) {
1635
- entries.push(path);
1636
- exclude.push("@vitest/coverage-istanbul");
1637
- }
1638
- } else if (provider === "custom" && coverage.customProviderModule) {
1639
- entries.push(coverage.customProviderModule);
1640
- }
1641
- }
1642
- const include = [
1643
- "vitest > expect-type",
1644
- "vitest > @vitest/snapshot > magic-string",
1645
- "vitest > chai",
1646
- "vitest > chai > loupe",
1647
- "vitest > @vitest/utils > loupe",
1648
- "@vitest/browser > @testing-library/user-event",
1649
- "@vitest/browser > @testing-library/dom"
1650
- ];
1651
- const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root;
1652
- const svelte = isPackageExists("vitest-browser-svelte", fileRoot);
1653
- if (svelte) {
1654
- exclude.push("vitest-browser-svelte");
1655
- }
1656
- const vueTestUtils = isPackageExists("@vue/test-utils", fileRoot);
1657
- if (vueTestUtils) {
1658
- include.push("@vue/test-utils");
1659
- }
1660
- return {
1661
- define,
1662
- resolve: {
1663
- dedupe: ["vitest"]
1664
- },
1665
- optimizeDeps: {
1666
- entries,
1667
- exclude,
1668
- include
1669
- }
1670
- };
1671
- },
1672
- async resolveId(id) {
1673
- if (!/\?browserv=\w+$/.test(id)) {
1674
- return;
1675
- }
1676
- let useId = id.slice(0, id.lastIndexOf("?"));
1677
- if (useId.startsWith("/@fs/")) {
1678
- useId = useId.slice(5);
1679
- }
1680
- if (/^\w:/.test(useId)) {
1681
- useId = useId.replace(/\\/g, "/");
1682
- }
1683
- return useId;
1684
- }
1879
+ code: 'AltLeft',
1880
+ key: 'Alt',
1881
+ location: DOM_KEY_LOCATION.LEFT
1685
1882
  },
1686
1883
  {
1687
- name: "vitest:browser:resolve-virtual",
1688
- async resolveId(rawId) {
1689
- if (rawId === "/mockServiceWorker.js") {
1690
- return this.resolve("msw/mockServiceWorker.js", distRoot, {
1691
- skipSelf: true
1692
- });
1693
- }
1694
- }
1884
+ code: 'AltRight',
1885
+ key: 'Alt',
1886
+ location: DOM_KEY_LOCATION.RIGHT
1695
1887
  },
1696
1888
  {
1697
- name: "vitest:browser:assets",
1698
- configureServer(server) {
1699
- server.middlewares.use(
1700
- "/__vitest__",
1701
- sirv(resolve(distRoot, "client/__vitest__"))
1702
- );
1703
- },
1704
- resolveId(id) {
1705
- if (id.startsWith("/__vitest_browser__/")) {
1706
- return resolve(distRoot, "client", id.slice(1));
1707
- }
1708
- },
1709
- transform(code, id) {
1710
- if (id.includes(browserServer.vite.config.cacheDir) && id.includes("loupe.js")) {
1711
- const utilRequire = "nodeUtil = require_util();";
1712
- return code.replace(utilRequire, " ".repeat(utilRequire.length));
1713
- }
1714
- }
1889
+ code: 'ShiftLeft',
1890
+ key: 'Shift',
1891
+ location: DOM_KEY_LOCATION.LEFT
1715
1892
  },
1716
- BrowserContext(browserServer),
1717
- dynamicImportPlugin({
1718
- globalThisAccessor: '"__vitest_browser_runner__"',
1719
- filter(id) {
1720
- if (id.includes(distRoot)) {
1721
- return false;
1722
- }
1723
- return true;
1724
- }
1725
- }),
1726
1893
  {
1727
- name: "vitest:browser:config",
1728
- enforce: "post",
1729
- async config(viteConfig) {
1730
- // Enables using ignore hint for coverage providers with @preserve keyword
1731
- if (viteConfig.esbuild !== false) {
1732
- viteConfig.esbuild ||= {};
1733
- viteConfig.esbuild.legalComments = "inline";
1734
- }
1735
- const defaultPort = project.ctx._browserLastPort++;
1736
- const api = resolveApiServerConfig(
1737
- viteConfig.test?.browser || {},
1738
- defaultPort
1739
- );
1740
- viteConfig.server = {
1741
- ...viteConfig.server,
1742
- port: defaultPort,
1743
- ...api,
1744
- middlewareMode: false,
1745
- open: false
1746
- };
1747
- viteConfig.server.fs ??= {};
1748
- viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
1749
- viteConfig.server.fs.allow.push(
1750
- ...resolveFsAllow(
1751
- project.ctx.config.root,
1752
- project.ctx.server.config.configFile
1753
- ),
1754
- distRoot
1755
- );
1756
- return {
1757
- resolve: {
1758
- alias: viteConfig.test?.alias
1759
- }
1760
- };
1761
- }
1894
+ code: 'ShiftRight',
1895
+ key: 'Shift',
1896
+ location: DOM_KEY_LOCATION.RIGHT
1762
1897
  },
1763
1898
  {
1764
- name: "vitest:browser:in-source-tests",
1765
- transform(code, id) {
1766
- if (!project.isTestFile(id) || !code.includes("import.meta.vitest")) {
1767
- return;
1768
- }
1769
- const s = new MagicString(code, { filename: cleanUrl(id) });
1770
- s.prepend(
1771
- `import.meta.vitest = __vitest_index__;
1772
- `
1773
- );
1774
- return {
1775
- code: s.toString(),
1776
- map: s.generateMap({ hires: true })
1777
- };
1778
- }
1899
+ code: 'ControlLeft',
1900
+ key: 'Control',
1901
+ location: DOM_KEY_LOCATION.LEFT
1779
1902
  },
1780
1903
  {
1781
- name: "vitest:browser:worker",
1782
- transform(code, id, _options) {
1783
- if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
1784
- const s = new MagicString(code);
1785
- s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
1786
- return {
1787
- code: s.toString(),
1788
- map: s.generateMap({ hires: "boundary" })
1789
- };
1790
- }
1791
- }
1904
+ code: 'ControlRight',
1905
+ key: 'Control',
1906
+ location: DOM_KEY_LOCATION.RIGHT
1792
1907
  },
1793
1908
  {
1794
- name: "vitest:browser:transform-tester-html",
1795
- enforce: "pre",
1796
- async transformIndexHtml(html, ctx) {
1797
- if (ctx.filename !== browserServer.testerFilepath) {
1798
- return;
1799
- }
1800
- if (!browserServer.testerScripts) {
1801
- const testerScripts = await browserServer.formatScripts(
1802
- project.config.browser.testerScripts
1803
- );
1804
- browserServer.testerScripts = testerScripts;
1805
- }
1806
- const stateJs = typeof browserServer.stateJs === "string" ? browserServer.stateJs : await browserServer.stateJs;
1807
- const testerTags = [];
1808
- const isDefaultTemplate = resolve(distRoot, "client/tester/tester.html") === browserServer.testerFilepath;
1809
- if (!isDefaultTemplate) {
1810
- const manifestContent = browserServer.manifest instanceof Promise ? await browserServer.manifest : browserServer.manifest;
1811
- const testerEntry = manifestContent["tester/tester.html"];
1812
- testerTags.push({
1813
- tag: "script",
1814
- attrs: {
1815
- type: "module",
1816
- crossorigin: "",
1817
- src: `${browserServer.base}${testerEntry.file}`
1818
- },
1819
- injectTo: "head"
1820
- });
1821
- for (const importName of testerEntry.imports || []) {
1822
- const entryManifest = manifestContent[importName];
1823
- if (entryManifest) {
1824
- testerTags.push(
1825
- {
1826
- tag: "link",
1827
- attrs: {
1828
- href: `${browserServer.base}${entryManifest.file}`,
1829
- rel: "modulepreload",
1830
- crossorigin: ""
1831
- },
1832
- injectTo: "head"
1833
- }
1834
- );
1835
- }
1836
- }
1837
- } else {
1838
- testerTags.push({
1839
- tag: "style",
1840
- children: `
1841
- html {
1842
- padding: 0;
1843
- margin: 0;
1844
- }
1845
- body {
1846
- padding: 0;
1847
- margin: 0;
1848
- min-height: 100vh;
1849
- }`,
1850
- injectTo: "head"
1851
- });
1852
- }
1853
- return [
1854
- {
1855
- tag: "script",
1856
- children: "{__VITEST_INJECTOR__}",
1857
- injectTo: "head-prepend"
1858
- },
1859
- {
1860
- tag: "script",
1861
- children: stateJs,
1862
- injectTo: "head-prepend"
1863
- },
1864
- {
1865
- tag: "script",
1866
- attrs: {
1867
- type: "module",
1868
- src: browserServer.errorCatcherUrl
1869
- },
1870
- injectTo: "head"
1871
- },
1872
- browserServer.locatorsUrl ? {
1873
- tag: "script",
1874
- attrs: {
1875
- type: "module",
1876
- src: browserServer.locatorsUrl
1877
- },
1878
- injectTo: "head"
1879
- } : null,
1880
- ...browserServer.testerScripts,
1881
- ...testerTags,
1882
- {
1883
- tag: "script",
1884
- attrs: {
1885
- "type": "module",
1886
- "data-vitest-append": ""
1887
- },
1888
- children: "{__VITEST_APPEND__}",
1889
- injectTo: "body"
1890
- }
1891
- ].filter((s) => s != null);
1909
+ code: 'MetaLeft',
1910
+ key: 'Meta',
1911
+ location: DOM_KEY_LOCATION.LEFT
1912
+ },
1913
+ {
1914
+ code: 'MetaRight',
1915
+ key: 'Meta',
1916
+ location: DOM_KEY_LOCATION.RIGHT
1917
+ },
1918
+ {
1919
+ code: 'OSLeft',
1920
+ key: 'OS',
1921
+ location: DOM_KEY_LOCATION.LEFT
1922
+ },
1923
+ {
1924
+ code: 'OSRight',
1925
+ key: 'OS',
1926
+ location: DOM_KEY_LOCATION.RIGHT
1927
+ },
1928
+ {
1929
+ code: 'Tab',
1930
+ key: 'Tab'
1931
+ },
1932
+ {
1933
+ code: 'CapsLock',
1934
+ key: 'CapsLock'
1935
+ },
1936
+ {
1937
+ code: 'Backspace',
1938
+ key: 'Backspace'
1939
+ },
1940
+ {
1941
+ code: 'Enter',
1942
+ key: 'Enter'
1943
+ },
1944
+ // function
1945
+ {
1946
+ code: 'Escape',
1947
+ key: 'Escape'
1948
+ },
1949
+ // arrows
1950
+ {
1951
+ code: 'ArrowUp',
1952
+ key: 'ArrowUp'
1953
+ },
1954
+ {
1955
+ code: 'ArrowDown',
1956
+ key: 'ArrowDown'
1957
+ },
1958
+ {
1959
+ code: 'ArrowLeft',
1960
+ key: 'ArrowLeft'
1961
+ },
1962
+ {
1963
+ code: 'ArrowRight',
1964
+ key: 'ArrowRight'
1965
+ },
1966
+ // control pad
1967
+ {
1968
+ code: 'Home',
1969
+ key: 'Home'
1970
+ },
1971
+ {
1972
+ code: 'End',
1973
+ key: 'End'
1974
+ },
1975
+ {
1976
+ code: 'Delete',
1977
+ key: 'Delete'
1978
+ },
1979
+ {
1980
+ code: 'PageUp',
1981
+ key: 'PageUp'
1982
+ },
1983
+ {
1984
+ code: 'PageDown',
1985
+ key: 'PageDown'
1986
+ },
1987
+ // Special keys that are not part of a default US-layout but included for specific behavior
1988
+ {
1989
+ code: 'Fn',
1990
+ key: 'Fn'
1991
+ },
1992
+ {
1993
+ code: 'Symbol',
1994
+ key: 'Symbol'
1995
+ },
1996
+ {
1997
+ code: 'AltRight',
1998
+ key: 'AltGraph'
1999
+ }
2000
+ ];
2001
+
2002
+ /**
2003
+ * Parse key defintions per `keyboardMap`
2004
+ *
2005
+ * Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
2006
+ * Everything else will be interpreted as a typed character - e.g. `a`.
2007
+ * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
2008
+ * Keeping the key pressed can be written as `{key>}`.
2009
+ * When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
2010
+ * You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
2011
+ */ function parseKeyDef(keyboardMap, text) {
2012
+ const defs = [];
2013
+ do {
2014
+ const { type, descriptor, consumedLength, releasePrevious, releaseSelf = true, repeat } = readNextDescriptor(text);
2015
+ var _keyboardMap_find;
2016
+ const keyDef = (_keyboardMap_find = keyboardMap.find((def)=>{
2017
+ if (type === '[') {
2018
+ var _def_code;
2019
+ return ((_def_code = def.code) === null || _def_code === void 0 ? void 0 : _def_code.toLowerCase()) === descriptor.toLowerCase();
2020
+ } else if (type === '{') {
2021
+ var _def_key;
2022
+ return ((_def_key = def.key) === null || _def_key === void 0 ? void 0 : _def_key.toLowerCase()) === descriptor.toLowerCase();
2023
+ }
2024
+ return def.key === descriptor;
2025
+ })) !== null && _keyboardMap_find !== void 0 ? _keyboardMap_find : {
2026
+ key: 'Unknown',
2027
+ code: 'Unknown',
2028
+ [type === '[' ? 'code' : 'key']: descriptor
2029
+ };
2030
+ defs.push({
2031
+ keyDef,
2032
+ releasePrevious,
2033
+ releaseSelf,
2034
+ repeat
2035
+ });
2036
+ text = text.slice(consumedLength);
2037
+ }while (text)
2038
+ return defs;
2039
+ }
2040
+
2041
+ const keyboard = async (context, text, state) => {
2042
+ if (context.provider instanceof PlaywrightBrowserProvider) {
2043
+ const frame = await context.frame();
2044
+ await frame.evaluate(focusIframe);
2045
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
2046
+ await context.browser.execute(focusIframe);
2047
+ }
2048
+ const pressed = new Set(state.unreleased);
2049
+ await keyboardImplementation(
2050
+ pressed,
2051
+ context.provider,
2052
+ context.sessionId,
2053
+ text,
2054
+ async () => {
2055
+ if (context.provider instanceof PlaywrightBrowserProvider) {
2056
+ const frame = await context.frame();
2057
+ await frame.evaluate(selectAll);
2058
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
2059
+ await context.browser.execute(selectAll);
2060
+ } else {
2061
+ throw new TypeError(`Provider "${context.provider.name}" does not support selecting all text`);
2062
+ }
2063
+ },
2064
+ true
2065
+ );
2066
+ return {
2067
+ unreleased: Array.from(pressed)
2068
+ };
2069
+ };
2070
+ const keyboardCleanup = async (context, state) => {
2071
+ const { provider, sessionId } = context;
2072
+ if (provider instanceof PlaywrightBrowserProvider) {
2073
+ const page = provider.getPage(sessionId);
2074
+ for (const key of state.unreleased) {
2075
+ await page.keyboard.up(key);
2076
+ }
2077
+ } else if (provider instanceof WebdriverBrowserProvider) {
2078
+ const keyboard2 = provider.browser.action("key");
2079
+ for (const key of state.unreleased) {
2080
+ keyboard2.up(key);
2081
+ }
2082
+ await keyboard2.perform();
2083
+ } else {
2084
+ throw new TypeError(`Provider "${context.provider.name}" does not support keyboard api`);
2085
+ }
2086
+ };
2087
+ const VALID_KEYS = /* @__PURE__ */ new Set(["Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "Backquote", "`", "~", "Digit1", "1", "!", "Digit2", "2", "@", "Digit3", "3", "#", "Digit4", "4", "$", "Digit5", "5", "%", "Digit6", "6", "^", "Digit7", "7", "&", "Digit8", "8", "*", "Digit9", "9", "(", "Digit0", "0", ")", "Minus", "-", "_", "Equal", "=", "+", "Backslash", "\\", "|", "Backspace", "Tab", "KeyQ", "q", "Q", "KeyW", "w", "W", "KeyE", "e", "E", "KeyR", "r", "R", "KeyT", "t", "T", "KeyY", "y", "Y", "KeyU", "u", "U", "KeyI", "i", "I", "KeyO", "o", "O", "KeyP", "p", "P", "BracketLeft", "[", "{", "BracketRight", "]", "}", "CapsLock", "KeyA", "a", "A", "KeyS", "s", "S", "KeyD", "d", "D", "KeyF", "f", "F", "KeyG", "g", "G", "KeyH", "h", "H", "KeyJ", "j", "J", "KeyK", "k", "K", "KeyL", "l", "L", "Semicolon", ";", ":", "Quote", "'", '"', "Enter", "\n", "\r", "ShiftLeft", "Shift", "KeyZ", "z", "Z", "KeyX", "x", "X", "KeyC", "c", "C", "KeyV", "v", "V", "KeyB", "b", "B", "KeyN", "n", "N", "KeyM", "m", "M", "Comma", ",", "<", "Period", ".", ">", "Slash", "/", "?", "ShiftRight", "ControlLeft", "Control", "MetaLeft", "Meta", "AltLeft", "Alt", "Space", " ", "AltRight", "AltGraph", "MetaRight", "ContextMenu", "ControlRight", "PrintScreen", "ScrollLock", "Pause", "PageUp", "PageDown", "Insert", "Delete", "Home", "End", "ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", "NumLock", "NumpadDivide", "NumpadMultiply", "NumpadSubtract", "Numpad7", "Numpad8", "Numpad9", "Numpad4", "Numpad5", "Numpad6", "NumpadAdd", "Numpad1", "Numpad2", "Numpad3", "Numpad0", "NumpadDecimal", "NumpadEnter"]);
2088
+ async function keyboardImplementation(pressed, provider, sessionId, text, selectAll2, skipRelease) {
2089
+ if (provider instanceof PlaywrightBrowserProvider) {
2090
+ const page = provider.getPage(sessionId);
2091
+ const actions = parseKeyDef(defaultKeyMap, text);
2092
+ for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
2093
+ const key = keyDef.key;
2094
+ if (pressed.has(key)) {
2095
+ if (VALID_KEYS.has(key)) {
2096
+ await page.keyboard.up(key);
2097
+ }
2098
+ pressed.delete(key);
2099
+ }
2100
+ if (!releasePrevious) {
2101
+ if (key === "selectall") {
2102
+ await selectAll2();
2103
+ continue;
2104
+ }
2105
+ for (let i = 1; i <= repeat; i++) {
2106
+ if (VALID_KEYS.has(key)) {
2107
+ await page.keyboard.down(key);
2108
+ } else {
2109
+ await page.keyboard.insertText(key);
2110
+ }
2111
+ }
2112
+ if (releaseSelf) {
2113
+ if (VALID_KEYS.has(key)) {
2114
+ await page.keyboard.up(key);
2115
+ }
2116
+ } else {
2117
+ pressed.add(key);
2118
+ }
2119
+ }
2120
+ }
2121
+ if (!skipRelease && pressed.size) {
2122
+ for (const key of pressed) {
2123
+ if (VALID_KEYS.has(key)) {
2124
+ await page.keyboard.up(key);
2125
+ }
2126
+ }
2127
+ }
2128
+ } else if (provider instanceof WebdriverBrowserProvider) {
2129
+ const { Key } = await import('webdriverio');
2130
+ const browser = provider.browser;
2131
+ const actions = parseKeyDef(defaultKeyMap, text);
2132
+ let keyboard2 = browser.action("key");
2133
+ for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
2134
+ let key = keyDef.key;
2135
+ const code = "location" in keyDef ? keyDef.key : keyDef.code;
2136
+ const special = Key[code];
2137
+ if (special) {
2138
+ key = special;
2139
+ }
2140
+ if (pressed.has(key)) {
2141
+ keyboard2.up(key);
2142
+ pressed.delete(key);
2143
+ }
2144
+ if (!releasePrevious) {
2145
+ if (key === "selectall") {
2146
+ await keyboard2.perform();
2147
+ keyboard2 = browser.action("key");
2148
+ await selectAll2();
2149
+ continue;
2150
+ }
2151
+ for (let i = 1; i <= repeat; i++) {
2152
+ keyboard2.down(key);
2153
+ }
2154
+ if (releaseSelf) {
2155
+ keyboard2.up(key);
2156
+ } else {
2157
+ pressed.add(key);
2158
+ }
2159
+ }
2160
+ }
2161
+ const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
2162
+ await keyboard2.perform(allRelease ? false : skipRelease);
2163
+ }
2164
+ return {
2165
+ pressed
2166
+ };
2167
+ }
2168
+ function focusIframe() {
2169
+ if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
2170
+ window.focus();
2171
+ }
2172
+ }
2173
+ function selectAll() {
2174
+ const element = document.activeElement;
2175
+ if (element && element.select) {
2176
+ element.select();
2177
+ }
2178
+ }
2179
+
2180
+ const screenshot = async (context, name, options = {}) => {
2181
+ if (!context.testPath) {
2182
+ throw new Error(`Cannot take a screenshot without a test path`);
2183
+ }
2184
+ const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(
2185
+ context.testPath,
2186
+ name,
2187
+ context.project.config
2188
+ );
2189
+ const savePath = normalize$1(path);
2190
+ await mkdir(dirname(path), { recursive: true });
2191
+ if (context.provider instanceof PlaywrightBrowserProvider) {
2192
+ if (options.element) {
2193
+ const { element: selector, ...config } = options;
2194
+ const element = context.iframe.locator(`${selector}`);
2195
+ const buffer2 = await element.screenshot({
2196
+ ...config,
2197
+ path: savePath
2198
+ });
2199
+ return returnResult(options, path, buffer2);
2200
+ }
2201
+ const buffer = await context.iframe.locator("body").screenshot({
2202
+ ...options,
2203
+ path: savePath
2204
+ });
2205
+ return returnResult(options, path, buffer);
2206
+ }
2207
+ if (context.provider instanceof WebdriverBrowserProvider) {
2208
+ const page = context.provider.browser;
2209
+ if (!options.element) {
2210
+ const body = await page.$("body");
2211
+ const buffer2 = await body.saveScreenshot(savePath);
2212
+ return returnResult(options, path, buffer2);
2213
+ }
2214
+ const element = await page.$(`${options.element}`);
2215
+ const buffer = await element.saveScreenshot(savePath);
2216
+ return returnResult(options, path, buffer);
2217
+ }
2218
+ throw new Error(
2219
+ `Provider "${context.provider.name}" does not support screenshots`
2220
+ );
2221
+ };
2222
+ function resolveScreenshotPath(testPath, name, config) {
2223
+ const dir = dirname(testPath);
2224
+ const base = basename(testPath);
2225
+ if (config.browser.screenshotDirectory) {
2226
+ return resolve(
2227
+ config.browser.screenshotDirectory,
2228
+ relative(config.root, dir),
2229
+ base,
2230
+ name
2231
+ );
2232
+ }
2233
+ return resolve(dir, "__screenshots__", base, name);
2234
+ }
2235
+ function returnResult(options, path, buffer) {
2236
+ if (options.base64) {
2237
+ return { path, base64: buffer.toString("base64") };
2238
+ }
2239
+ return path;
2240
+ }
2241
+
2242
+ const selectOptions = async (context, selector, userValues, options = {}) => {
2243
+ if (context.provider instanceof PlaywrightBrowserProvider) {
2244
+ const value = userValues;
2245
+ const { iframe } = context;
2246
+ const selectElement = iframe.locator(selector);
2247
+ const values = await Promise.all(value.map(async (v) => {
2248
+ if (typeof v === "string") {
2249
+ return v;
2250
+ }
2251
+ const elementHandler = await iframe.locator(v.element).elementHandle();
2252
+ if (!elementHandler) {
2253
+ throw new Error(`Element not found: ${v.element}`);
2254
+ }
2255
+ return elementHandler;
2256
+ }));
2257
+ await selectElement.selectOption(values, options);
2258
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
2259
+ const values = userValues;
2260
+ if (!values.length) {
2261
+ return;
2262
+ }
2263
+ const browser = context.browser;
2264
+ if (values.length === 1 && "index" in values[0]) {
2265
+ const selectElement = browser.$(selector);
2266
+ await selectElement.selectByIndex(values[0].index);
2267
+ } else {
2268
+ throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
2269
+ }
2270
+ } else {
2271
+ throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
2272
+ }
2273
+ };
2274
+
2275
+ const tab = async (context, options = {}) => {
2276
+ const provider = context.provider;
2277
+ if (provider instanceof PlaywrightBrowserProvider) {
2278
+ const page = context.page;
2279
+ await page.keyboard.press(options.shift === true ? "Shift+Tab" : "Tab");
2280
+ return;
2281
+ }
2282
+ if (provider instanceof WebdriverBrowserProvider) {
2283
+ const { Key } = await import('webdriverio');
2284
+ const browser = context.browser;
2285
+ await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab]);
2286
+ return;
2287
+ }
2288
+ throw new Error(`Provider "${provider.name}" doesn't support tab command`);
2289
+ };
2290
+
2291
+ const type = async (context, selector, text, options = {}) => {
2292
+ const { skipClick = false, skipAutoClose = false } = options;
2293
+ const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
2294
+ if (context.provider instanceof PlaywrightBrowserProvider) {
2295
+ const { iframe } = context;
2296
+ const element = iframe.locator(selector);
2297
+ if (!skipClick) {
2298
+ await element.focus();
2299
+ }
2300
+ await keyboardImplementation(
2301
+ unreleased,
2302
+ context.provider,
2303
+ context.sessionId,
2304
+ text,
2305
+ () => element.selectText(),
2306
+ skipAutoClose
2307
+ );
2308
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
2309
+ const browser = context.browser;
2310
+ const element = browser.$(selector);
2311
+ if (!skipClick && !await element.isFocused()) {
2312
+ await element.click();
2313
+ }
2314
+ await keyboardImplementation(
2315
+ unreleased,
2316
+ context.provider,
2317
+ context.sessionId,
2318
+ text,
2319
+ () => browser.execute(() => {
2320
+ const element2 = document.activeElement;
2321
+ if (element2) {
2322
+ element2.select();
2323
+ }
2324
+ }),
2325
+ skipAutoClose
2326
+ );
2327
+ } else {
2328
+ throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
2329
+ }
2330
+ return {
2331
+ unreleased: Array.from(unreleased)
2332
+ };
2333
+ };
2334
+
2335
+ const upload = async (context, selector, files) => {
2336
+ const testPath = context.testPath;
2337
+ if (!testPath) {
2338
+ throw new Error(`Cannot upload files outside of a test`);
2339
+ }
2340
+ const testDir = dirname(testPath);
2341
+ if (context.provider instanceof PlaywrightBrowserProvider) {
2342
+ const { iframe } = context;
2343
+ const playwrightFiles = files.map((file) => {
2344
+ if (typeof file === "string") {
2345
+ return resolve(testDir, file);
1892
2346
  }
1893
- },
1894
- {
1895
- name: "vitest:browser:support-testing-library",
1896
- config() {
1897
- return {
1898
- optimizeDeps: {
1899
- esbuildOptions: {
1900
- plugins: [
1901
- {
1902
- name: "test-utils-rewrite",
1903
- setup(build) {
1904
- build.onResolve({ filter: /^@vue\/test-utils$/ }, (args) => {
1905
- const _require2 = getRequire();
1906
- const resolved = _require2.resolve(args.path, {
1907
- paths: [args.importer]
1908
- });
1909
- return { path: resolved };
1910
- });
1911
- }
1912
- }
1913
- ]
1914
- }
1915
- }
1916
- };
2347
+ return {
2348
+ name: file.name,
2349
+ mimeType: file.mimeType,
2350
+ buffer: Buffer.from(file.base64, "base64")
2351
+ };
2352
+ });
2353
+ await iframe.locator(selector).setInputFiles(playwrightFiles);
2354
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
2355
+ for (const file of files) {
2356
+ if (typeof file !== "string") {
2357
+ throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`);
1917
2358
  }
1918
2359
  }
1919
- ];
1920
- };
1921
- function tryResolve(path, paths) {
1922
- try {
1923
- const _require2 = getRequire();
1924
- return _require2.resolve(path, { paths });
1925
- } catch {
1926
- return void 0;
2360
+ const element = context.browser.$(selector);
2361
+ for (const file of files) {
2362
+ const filepath = resolve(testDir, file);
2363
+ const remoteFilePath = await context.browser.uploadFile(filepath);
2364
+ await element.addValue(remoteFilePath);
2365
+ }
2366
+ } else {
2367
+ throw new TypeError(`Provider "${context.provider.name}" does not support uploading files via userEvent.upload`);
1927
2368
  }
2369
+ };
2370
+
2371
+ var builtinCommands = {
2372
+ readFile,
2373
+ removeFile,
2374
+ writeFile,
2375
+ __vitest_fileInfo: _fileInfo,
2376
+ __vitest_upload: upload,
2377
+ __vitest_click: click,
2378
+ __vitest_dblClick: dblClick,
2379
+ __vitest_tripleClick: tripleClick,
2380
+ __vitest_screenshot: screenshot,
2381
+ __vitest_type: type,
2382
+ __vitest_clear: clear,
2383
+ __vitest_fill: fill,
2384
+ __vitest_tab: tab,
2385
+ __vitest_keyboard: keyboard,
2386
+ __vitest_selectOptions: selectOptions,
2387
+ __vitest_dragAndDrop: dragAndDrop,
2388
+ __vitest_hover: hover,
2389
+ __vitest_cleanup: keyboardCleanup
2390
+ };
2391
+
2392
+ class BrowserServerState {
2393
+ orchestrators = /* @__PURE__ */ new Map();
2394
+ testers = /* @__PURE__ */ new Map();
1928
2395
  }
1929
- let _require;
1930
- function getRequire() {
1931
- if (!_require) {
1932
- _require = createRequire(import.meta.url);
2396
+
2397
+ class ProjectBrowser {
2398
+ constructor(project, base) {
2399
+ this.project = project;
2400
+ this.base = base;
2401
+ this.vitest = project.vitest;
2402
+ this.config = project.config;
2403
+ const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
2404
+ const distRoot = resolve(pkgRoot, "dist");
2405
+ const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
2406
+ if (!existsSync(testerHtmlPath)) {
2407
+ throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
2408
+ }
2409
+ this.testerFilepath = testerHtmlPath;
2410
+ this.testerHtml = readFile$1(
2411
+ testerHtmlPath,
2412
+ "utf8"
2413
+ ).then((html) => this.testerHtml = html);
2414
+ }
2415
+ testerHtml;
2416
+ testerFilepath;
2417
+ locatorsUrl;
2418
+ provider;
2419
+ vitest;
2420
+ config;
2421
+ children = /* @__PURE__ */ new Set();
2422
+ parent;
2423
+ state = new BrowserServerState();
2424
+ get vite() {
2425
+ return this.parent.vite;
2426
+ }
2427
+ wrapSerializedConfig() {
2428
+ const config = wrapConfig(this.project.serializedConfig);
2429
+ config.env ??= {};
2430
+ config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
2431
+ return config;
2432
+ }
2433
+ async initBrowserProvider(project) {
2434
+ if (this.provider) {
2435
+ return;
2436
+ }
2437
+ const Provider = await getBrowserProvider(project.config.browser, project);
2438
+ this.provider = new Provider();
2439
+ const browser = project.config.browser.name;
2440
+ const name = project.name ? `[${project.name}] ` : "";
2441
+ if (!browser) {
2442
+ throw new Error(
2443
+ `${name}Browser name is required. Please, set \`test.browser.instances[].browser\` option manually.`
2444
+ );
2445
+ }
2446
+ const supportedBrowsers = this.provider.getSupportedBrowsers();
2447
+ if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
2448
+ throw new Error(
2449
+ `${name}Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`
2450
+ );
2451
+ }
2452
+ const providerOptions = project.config.browser.providerOptions;
2453
+ await this.provider.initialize(project, {
2454
+ browser,
2455
+ options: providerOptions
2456
+ });
2457
+ }
2458
+ parseErrorStacktrace(e, options = {}) {
2459
+ return this.parent.parseErrorStacktrace(e, options);
2460
+ }
2461
+ parseStacktrace(trace, options = {}) {
2462
+ return this.parent.parseStacktrace(trace, options);
2463
+ }
2464
+ async close() {
2465
+ await this.parent.vite.close();
1933
2466
  }
1934
- return _require;
1935
2467
  }
1936
- function resolveCoverageFolder(project) {
1937
- const options = project.ctx.config;
1938
- const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
1939
- if (typeof reporter === "string") {
1940
- return reporter === "html";
2468
+ function wrapConfig(config) {
2469
+ return {
2470
+ ...config,
2471
+ // workaround RegExp serialization
2472
+ testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : void 0
2473
+ };
2474
+ }
2475
+
2476
+ class ParentBrowserProject {
2477
+ constructor(project, base) {
2478
+ this.project = project;
2479
+ this.base = base;
2480
+ this.vitest = project.vitest;
2481
+ this.config = project.config;
2482
+ this.stackTraceOptions = {
2483
+ frameFilter: project.config.onStackTrace,
2484
+ getSourceMap: (id) => {
2485
+ const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
2486
+ return result?.map;
2487
+ },
2488
+ getFileName: (id) => {
2489
+ const mod = this.vite.moduleGraph.getModuleById(id);
2490
+ if (mod?.file) {
2491
+ return mod.file;
2492
+ }
2493
+ const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
2494
+ if (modUrl?.file) {
2495
+ return modUrl.file;
2496
+ }
2497
+ return id;
2498
+ }
2499
+ };
2500
+ for (const [name, command] of Object.entries(builtinCommands)) {
2501
+ this.commands[name] ??= command;
1941
2502
  }
1942
- return reporter[0] === "html";
1943
- }) : void 0;
1944
- if (!htmlReporter) {
1945
- return void 0;
2503
+ for (const command in project.config.browser.commands) {
2504
+ if (!/^[a-z_$][\w$]*$/i.test(command)) {
2505
+ throw new Error(
2506
+ `Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`
2507
+ );
2508
+ }
2509
+ this.commands[command] = project.config.browser.commands[command];
2510
+ }
2511
+ this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
2512
+ this.faviconUrl = `${base}__vitest__/favicon.svg`;
2513
+ this.manifest = (async () => {
2514
+ return JSON.parse(
2515
+ await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8")
2516
+ );
2517
+ })().then((manifest) => this.manifest = manifest);
2518
+ 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);
2519
+ this.injectorJs = readFile$1(
2520
+ resolve(distRoot, "client/esm-client-injector.js"),
2521
+ "utf8"
2522
+ ).then((js) => this.injectorJs = js);
2523
+ this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
2524
+ const builtinProviders = ["playwright", "webdriverio", "preview"];
2525
+ const providerName = project.config.browser.provider || "preview";
2526
+ if (builtinProviders.includes(providerName)) {
2527
+ this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
2528
+ }
2529
+ this.stateJs = readFile$1(
2530
+ resolve(distRoot, "state.js"),
2531
+ "utf-8"
2532
+ ).then((js) => this.stateJs = js);
2533
+ }
2534
+ orchestratorScripts;
2535
+ testerScripts;
2536
+ faviconUrl;
2537
+ prefixTesterUrl;
2538
+ manifest;
2539
+ vite;
2540
+ stackTraceOptions;
2541
+ orchestratorHtml;
2542
+ injectorJs;
2543
+ errorCatcherUrl;
2544
+ locatorsUrl;
2545
+ stateJs;
2546
+ commands = {};
2547
+ children = /* @__PURE__ */ new Set();
2548
+ vitest;
2549
+ config;
2550
+ setServer(vite) {
2551
+ this.vite = vite;
2552
+ }
2553
+ spawn(project) {
2554
+ if (!this.vite) {
2555
+ throw new Error(`Cannot spawn child server without a parent dev server.`);
2556
+ }
2557
+ const clone = new ProjectBrowser(
2558
+ project,
2559
+ "/"
2560
+ );
2561
+ clone.parent = this;
2562
+ this.children.add(clone);
2563
+ return clone;
2564
+ }
2565
+ parseErrorStacktrace(e, options = {}) {
2566
+ return parseErrorStacktrace(e, {
2567
+ ...this.stackTraceOptions,
2568
+ ...options
2569
+ });
2570
+ }
2571
+ parseStacktrace(trace, options = {}) {
2572
+ return parseStacktrace(trace, {
2573
+ ...this.stackTraceOptions,
2574
+ ...options
2575
+ });
2576
+ }
2577
+ cdps = /* @__PURE__ */ new Map();
2578
+ cdpSessionsPromises = /* @__PURE__ */ new Map();
2579
+ async ensureCDPHandler(sessionId, rpcId) {
2580
+ const cachedHandler = this.cdps.get(rpcId);
2581
+ if (cachedHandler) {
2582
+ return cachedHandler;
2583
+ }
2584
+ const browserSession = this.vitest._browserSessions.getSession(sessionId);
2585
+ if (!browserSession) {
2586
+ throw new Error(`Session "${sessionId}" not found.`);
2587
+ }
2588
+ const browser = browserSession.project.browser;
2589
+ const provider = browser.provider;
2590
+ if (!provider) {
2591
+ throw new Error(`Browser provider is not defined for the project "${browserSession.project.name}".`);
2592
+ }
2593
+ if (!provider.getCDPSession) {
2594
+ throw new Error(`CDP is not supported by the provider "${provider.name}".`);
2595
+ }
2596
+ const promise = this.cdpSessionsPromises.get(rpcId) ?? await (async () => {
2597
+ const promise2 = provider.getCDPSession(sessionId).finally(() => {
2598
+ this.cdpSessionsPromises.delete(rpcId);
2599
+ });
2600
+ this.cdpSessionsPromises.set(rpcId, promise2);
2601
+ return promise2;
2602
+ })();
2603
+ const session = await promise;
2604
+ const rpc = browser.state.testers.get(rpcId);
2605
+ if (!rpc) {
2606
+ throw new Error(`Tester RPC "${rpcId}" was not established.`);
2607
+ }
2608
+ const handler = new BrowserServerCDPHandler(session, rpc);
2609
+ this.cdps.set(
2610
+ rpcId,
2611
+ handler
2612
+ );
2613
+ return handler;
2614
+ }
2615
+ removeCDPHandler(sessionId) {
2616
+ this.cdps.delete(sessionId);
2617
+ }
2618
+ async formatScripts(scripts) {
2619
+ if (!scripts?.length) {
2620
+ return [];
2621
+ }
2622
+ const server = this.vite;
2623
+ const promises = scripts.map(
2624
+ async ({ content, src, async, id, type = "module" }, index) => {
2625
+ const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : void 0) || src;
2626
+ const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
2627
+ await server.moduleGraph.ensureEntryFromUrl(transformId);
2628
+ const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
2629
+ return {
2630
+ tag: "script",
2631
+ attrs: {
2632
+ type,
2633
+ ...async ? { async: "" } : {},
2634
+ ...srcLink ? {
2635
+ src: srcLink.startsWith("http") ? srcLink : slash(`/@fs/${srcLink}`)
2636
+ } : {}
2637
+ },
2638
+ injectTo: "head",
2639
+ children: contentProcessed || ""
2640
+ };
2641
+ }
2642
+ );
2643
+ return await Promise.all(promises);
1946
2644
  }
1947
- const root = resolve(
1948
- options.root || process.cwd(),
1949
- options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
1950
- );
1951
- const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
1952
- if (!subdir || typeof subdir !== "string") {
1953
- return [root, `/${basename(root)}/`];
2645
+ resolveTesterUrl(pathname) {
2646
+ const [sessionId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
2647
+ const decodedTestFile = decodeURIComponent(testFile);
2648
+ return { sessionId, testFile: decodedTestFile };
1954
2649
  }
1955
- return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
1956
- }
1957
- const postfixRE = /[?#].*$/;
1958
- function cleanUrl(url) {
1959
- return url.replace(postfixRE, "");
1960
2650
  }
1961
2651
 
1962
2652
  const DEFAULT_TIMEOUT = 6e4;
@@ -2086,10 +2776,9 @@ function nanoid(size = 21) {
2086
2776
 
2087
2777
  const debug$1 = createDebugger("vitest:browser:api");
2088
2778
  const BROWSER_API_PATH = "/__vitest_browser_api__";
2089
- function setupBrowserRpc(server) {
2090
- const project = server.project;
2091
- const vite = server.vite;
2092
- const ctx = project.ctx;
2779
+ function setupBrowserRpc(globalServer) {
2780
+ const vite = globalServer.vite;
2781
+ const vitest = globalServer.vitest;
2093
2782
  const wss = new WebSocketServer({ noServer: true });
2094
2783
  vite.httpServer?.on("upgrade", (request, socket, head) => {
2095
2784
  if (!request.url) {
@@ -2099,22 +2788,48 @@ function setupBrowserRpc(server) {
2099
2788
  if (pathname !== BROWSER_API_PATH) {
2100
2789
  return;
2101
2790
  }
2102
- const type = searchParams.get("type") ?? "tester";
2103
- const sessionId = searchParams.get("sessionId") ?? "0";
2791
+ const type = searchParams.get("type");
2792
+ const rpcId = searchParams.get("rpcId");
2793
+ const sessionId = searchParams.get("sessionId");
2794
+ const projectName = searchParams.get("projectName");
2795
+ if (type !== "tester" && type !== "orchestrator") {
2796
+ return error(
2797
+ new Error(`[vitest] Type query in ${request.url} is invalid. Type should be either "tester" or "orchestrator".`)
2798
+ );
2799
+ }
2800
+ if (!sessionId || !rpcId || projectName == null) {
2801
+ return error(
2802
+ new Error(`[vitest] Invalid URL ${request.url}. "projectName", "sessionId" and "rpcId" queries are required.`)
2803
+ );
2804
+ }
2805
+ if (type === "orchestrator") {
2806
+ const session = vitest._browserSessions.getSession(sessionId);
2807
+ session?.connected();
2808
+ }
2809
+ const project = vitest.getProjectByName(projectName);
2810
+ if (!project) {
2811
+ return error(
2812
+ new Error(`[vitest] Project "${projectName}" not found.`)
2813
+ );
2814
+ }
2104
2815
  wss.handleUpgrade(request, socket, head, (ws) => {
2105
2816
  wss.emit("connection", ws, request);
2106
- const rpc = setupClient(sessionId, ws);
2107
- const state = server.state;
2817
+ const rpc = setupClient(project, rpcId, ws);
2818
+ const state = project.browser.state;
2108
2819
  const clients = type === "tester" ? state.testers : state.orchestrators;
2109
- clients.set(sessionId, rpc);
2110
- debug$1?.("[%s] Browser API connected to %s", sessionId, type);
2820
+ clients.set(rpcId, rpc);
2821
+ debug$1?.("[%s] Browser API connected to %s", rpcId, type);
2111
2822
  ws.on("close", () => {
2112
- debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
2113
- clients.delete(sessionId);
2114
- server.state.removeCDPHandler(sessionId);
2823
+ debug$1?.("[%s] Browser API disconnected from %s", rpcId, type);
2824
+ clients.delete(rpcId);
2825
+ globalServer.removeCDPHandler(rpcId);
2115
2826
  });
2116
2827
  });
2117
2828
  });
2829
+ function error(err) {
2830
+ console.error(err);
2831
+ vitest.state.catchError(err, "RPC Error");
2832
+ }
2118
2833
  function checkFileAccess(path) {
2119
2834
  if (!isFileServingAllowed(path, vite)) {
2120
2835
  throw new Error(
@@ -2122,43 +2837,48 @@ function setupBrowserRpc(server) {
2122
2837
  );
2123
2838
  }
2124
2839
  }
2125
- function setupClient(sessionId, ws) {
2126
- const mockResolver = new ServerMockResolver(server.vite, {
2840
+ function setupClient(project, rpcId, ws) {
2841
+ const mockResolver = new ServerMockResolver(globalServer.vite, {
2127
2842
  moduleDirectories: project.config.server?.deps?.moduleDirectories
2128
2843
  });
2129
2844
  const rpc = createBirpc(
2130
2845
  {
2131
- async onUnhandledError(error, type) {
2132
- if (error && typeof error === "object") {
2133
- const _error = error;
2134
- _error.stacks = server.parseErrorStacktrace(_error);
2846
+ async onUnhandledError(error2, type) {
2847
+ if (error2 && typeof error2 === "object") {
2848
+ const _error = error2;
2849
+ _error.stacks = globalServer.parseErrorStacktrace(_error);
2135
2850
  }
2136
- ctx.state.catchError(error, type);
2851
+ vitest.state.catchError(error2, type);
2852
+ },
2853
+ async onQueued(file) {
2854
+ vitest.state.collectFiles(project, [file]);
2855
+ const testModule = vitest.state.getReportedEntity(file);
2856
+ await vitest.report("onTestModuleQueued", testModule);
2137
2857
  },
2138
2858
  async onCollected(files) {
2139
- ctx.state.collectFiles(project, files);
2140
- await ctx.report("onCollected", files);
2859
+ vitest.state.collectFiles(project, files);
2860
+ await vitest.report("onCollected", files);
2141
2861
  },
2142
2862
  async onTaskUpdate(packs) {
2143
- ctx.state.updateTasks(packs);
2144
- await ctx.report("onTaskUpdate", packs);
2863
+ vitest.state.updateTasks(packs);
2864
+ await vitest.report("onTaskUpdate", packs);
2145
2865
  },
2146
2866
  onAfterSuiteRun(meta) {
2147
- ctx.coverageProvider?.onAfterSuiteRun(meta);
2867
+ vitest.coverageProvider?.onAfterSuiteRun(meta);
2148
2868
  },
2149
2869
  sendLog(log) {
2150
- return ctx.report("onUserConsoleLog", log);
2870
+ return vitest.report("onUserConsoleLog", log);
2151
2871
  },
2152
2872
  resolveSnapshotPath(testPath) {
2153
- return ctx.snapshot.resolvePath(testPath, {
2154
- config: project.getSerializableConfig()
2873
+ return vitest.snapshot.resolvePath(testPath, {
2874
+ config: project.serializedConfig
2155
2875
  });
2156
2876
  },
2157
2877
  resolveSnapshotRawPath(testPath, rawPath) {
2158
- return ctx.snapshot.resolveRawPath(testPath, rawPath);
2878
+ return vitest.snapshot.resolveRawPath(testPath, rawPath);
2159
2879
  },
2160
2880
  snapshotSaved(snapshot) {
2161
- ctx.snapshot.add(snapshot);
2881
+ vitest.snapshot.add(snapshot);
2162
2882
  },
2163
2883
  async readSnapshotFile(snapshotPath) {
2164
2884
  checkFileAccess(snapshotPath);
@@ -2180,56 +2900,53 @@ function setupBrowserRpc(server) {
2180
2900
  return promises.unlink(id);
2181
2901
  },
2182
2902
  getBrowserFileSourceMap(id) {
2183
- const mod = server.vite.moduleGraph.getModuleById(id);
2903
+ const mod = globalServer.vite.moduleGraph.getModuleById(id);
2184
2904
  return mod?.transformResult?.map;
2185
2905
  },
2186
2906
  onCancel(reason) {
2187
- ctx.cancelCurrentRun(reason);
2907
+ vitest.cancelCurrentRun(reason);
2188
2908
  },
2189
2909
  async resolveId(id, importer) {
2190
2910
  return mockResolver.resolveId(id, importer);
2191
2911
  },
2192
2912
  debug(...args) {
2193
- ctx.logger.console.debug(...args);
2913
+ vitest.logger.console.debug(...args);
2194
2914
  },
2195
2915
  getCountOfFailedTests() {
2196
- return ctx.state.getCountOfFailedTests();
2916
+ return vitest.state.getCountOfFailedTests();
2197
2917
  },
2198
- async triggerCommand(contextId, command, testPath, payload) {
2199
- debug$1?.('[%s] Triggering command "%s"', contextId, command);
2200
- const provider = server.provider;
2918
+ async triggerCommand(sessionId, command, testPath, payload) {
2919
+ debug$1?.('[%s] Triggering command "%s"', sessionId, command);
2920
+ const provider = project.browser.provider;
2201
2921
  if (!provider) {
2202
2922
  throw new Error("Commands are only available for browser tests.");
2203
2923
  }
2204
- const commands = project.config.browser?.commands;
2924
+ const commands = globalServer.commands;
2205
2925
  if (!commands || !commands[command]) {
2206
2926
  throw new Error(`Unknown command "${command}".`);
2207
2927
  }
2208
- if (provider.beforeCommand) {
2209
- await provider.beforeCommand(command, payload);
2210
- }
2928
+ await provider.beforeCommand?.(command, payload);
2211
2929
  const context = Object.assign(
2212
2930
  {
2213
2931
  testPath,
2214
2932
  project,
2215
2933
  provider,
2216
- contextId
2934
+ contextId: sessionId,
2935
+ sessionId
2217
2936
  },
2218
- provider.getCommandsContext(contextId)
2937
+ provider.getCommandsContext(sessionId)
2219
2938
  );
2220
2939
  let result;
2221
2940
  try {
2222
2941
  result = await commands[command](context, ...payload);
2223
2942
  } finally {
2224
- if (provider.afterCommand) {
2225
- await provider.afterCommand(command, payload);
2226
- }
2943
+ await provider.afterCommand?.(command, payload);
2227
2944
  }
2228
2945
  return result;
2229
2946
  },
2230
- finishBrowserTests(contextId) {
2231
- debug$1?.("[%s] Finishing browser tests for context", contextId);
2232
- return server.state.getContext(contextId)?.resolve();
2947
+ finishBrowserTests(sessionId) {
2948
+ debug$1?.("[%s] Finishing browser tests for session", sessionId);
2949
+ return vitest._browserSessions.getSession(sessionId)?.resolve();
2233
2950
  },
2234
2951
  resolveMock(rawId, importer, options) {
2235
2952
  return mockResolver.resolveMock(rawId, importer, options);
@@ -2238,12 +2955,12 @@ function setupBrowserRpc(server) {
2238
2955
  return mockResolver.invalidate(ids);
2239
2956
  },
2240
2957
  // CDP
2241
- async sendCdpEvent(contextId, event, payload) {
2242
- const cdp = await server.ensureCDPHandler(contextId, sessionId);
2958
+ async sendCdpEvent(sessionId, event, payload) {
2959
+ const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
2243
2960
  return cdp.send(event, payload);
2244
2961
  },
2245
- async trackCdpEvent(contextId, type, event, listenerId) {
2246
- const cdp = await server.ensureCDPHandler(contextId, sessionId);
2962
+ async trackCdpEvent(sessionId, type, event, listenerId) {
2963
+ const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
2247
2964
  cdp[type](event, listenerId);
2248
2965
  }
2249
2966
  },
@@ -2258,7 +2975,7 @@ function setupBrowserRpc(server) {
2258
2975
  }
2259
2976
  }
2260
2977
  );
2261
- ctx.onCancel((reason) => rpc.onCancel(reason));
2978
+ vitest.onCancel((reason) => rpc.onCancel(reason));
2262
2979
  return rpc;
2263
2980
  }
2264
2981
  }
@@ -2285,275 +3002,15 @@ function stringifyReplace(key, value) {
2285
3002
  }
2286
3003
  }
2287
3004
 
2288
- class BrowserServerCDPHandler {
2289
- constructor(session, tester) {
2290
- this.session = session;
2291
- this.tester = tester;
2292
- }
2293
- listenerIds = {};
2294
- listeners = {};
2295
- send(method, params) {
2296
- return this.session.send(method, params);
2297
- }
2298
- on(event, id, once = false) {
2299
- if (!this.listenerIds[event]) {
2300
- this.listenerIds[event] = [];
2301
- }
2302
- this.listenerIds[event].push(id);
2303
- if (!this.listeners[event]) {
2304
- this.listeners[event] = (payload) => {
2305
- this.tester.cdpEvent(
2306
- event,
2307
- payload
2308
- );
2309
- if (once) {
2310
- this.off(event, id);
2311
- }
2312
- };
2313
- this.session.on(event, this.listeners[event]);
2314
- }
2315
- }
2316
- off(event, id) {
2317
- if (!this.listenerIds[event]) {
2318
- this.listenerIds[event] = [];
2319
- }
2320
- this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
2321
- if (!this.listenerIds[event].length) {
2322
- this.session.off(event, this.listeners[event]);
2323
- delete this.listeners[event];
2324
- }
2325
- }
2326
- once(event, listener) {
2327
- this.on(event, listener, true);
2328
- }
2329
- }
2330
-
2331
- class BrowserServerState {
2332
- orchestrators = /* @__PURE__ */ new Map();
2333
- testers = /* @__PURE__ */ new Map();
2334
- cdps = /* @__PURE__ */ new Map();
2335
- contexts = /* @__PURE__ */ new Map();
2336
- getContext(contextId) {
2337
- return this.contexts.get(contextId);
2338
- }
2339
- createAsyncContext(method, contextId, files) {
2340
- const defer = createDefer();
2341
- this.contexts.set(contextId, {
2342
- files,
2343
- method,
2344
- resolve: () => {
2345
- defer.resolve();
2346
- this.contexts.delete(contextId);
2347
- },
2348
- reject: defer.reject
2349
- });
2350
- return defer;
2351
- }
2352
- async removeCDPHandler(sessionId) {
2353
- this.cdps.delete(sessionId);
2354
- }
2355
- }
2356
-
2357
- class BrowserServer {
2358
- constructor(project, base) {
2359
- this.project = project;
2360
- this.base = base;
2361
- this.stackTraceOptions = {
2362
- frameFilter: project.config.onStackTrace,
2363
- getSourceMap: (id) => {
2364
- const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
2365
- return result?.map;
2366
- },
2367
- getFileName: (id) => {
2368
- const mod = this.vite.moduleGraph.getModuleById(id);
2369
- if (mod?.file) {
2370
- return mod.file;
2371
- }
2372
- const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
2373
- if (modUrl?.file) {
2374
- return modUrl.file;
2375
- }
2376
- return id;
2377
- }
2378
- };
2379
- this.state = new BrowserServerState();
2380
- const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
2381
- const distRoot = resolve(pkgRoot, "dist");
2382
- this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
2383
- this.faviconUrl = `${base}__vitest__/favicon.svg`;
2384
- this.manifest = (async () => {
2385
- return JSON.parse(
2386
- await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8")
2387
- );
2388
- })().then((manifest) => this.manifest = manifest);
2389
- const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
2390
- if (!existsSync(testerHtmlPath)) {
2391
- throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
2392
- }
2393
- this.testerFilepath = testerHtmlPath;
2394
- this.testerHtml = readFile$1(
2395
- testerHtmlPath,
2396
- "utf8"
2397
- ).then((html) => this.testerHtml = html);
2398
- 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);
2399
- this.injectorJs = readFile$1(
2400
- resolve(distRoot, "client/esm-client-injector.js"),
2401
- "utf8"
2402
- ).then((js) => this.injectorJs = js);
2403
- this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
2404
- const builtinProviders = ["playwright", "webdriverio", "preview"];
2405
- const providerName = project.config.browser.provider || "preview";
2406
- if (builtinProviders.includes(providerName)) {
2407
- this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
2408
- }
2409
- this.stateJs = readFile$1(
2410
- resolve(distRoot, "state.js"),
2411
- "utf-8"
2412
- ).then((js) => this.stateJs = js);
2413
- }
2414
- faviconUrl;
2415
- prefixTesterUrl;
2416
- orchestratorScripts;
2417
- testerScripts;
2418
- manifest;
2419
- testerHtml;
2420
- testerFilepath;
2421
- orchestratorHtml;
2422
- injectorJs;
2423
- errorCatcherUrl;
2424
- locatorsUrl;
2425
- stateJs;
2426
- state;
2427
- provider;
2428
- vite;
2429
- stackTraceOptions;
2430
- setServer(server) {
2431
- this.vite = server;
2432
- }
2433
- getSerializableConfig() {
2434
- const config = wrapConfig(this.project.getSerializableConfig());
2435
- config.env ??= {};
2436
- config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
2437
- return config;
2438
- }
2439
- resolveTesterUrl(pathname) {
2440
- const [contextId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
2441
- const decodedTestFile = decodeURIComponent(testFile);
2442
- return { contextId, testFile: decodedTestFile };
2443
- }
2444
- async formatScripts(scripts) {
2445
- if (!scripts?.length) {
2446
- return [];
2447
- }
2448
- const server = this.vite;
2449
- const promises = scripts.map(
2450
- async ({ content, src, async, id, type = "module" }, index) => {
2451
- const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : void 0) || src;
2452
- const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
2453
- await server.moduleGraph.ensureEntryFromUrl(transformId);
2454
- const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
2455
- return {
2456
- tag: "script",
2457
- attrs: {
2458
- type,
2459
- ...async ? { async: "" } : {},
2460
- ...srcLink ? {
2461
- src: srcLink.startsWith("http") ? srcLink : slash(`/@fs/${srcLink}`)
2462
- } : {}
2463
- },
2464
- injectTo: "head",
2465
- children: contentProcessed || ""
2466
- };
2467
- }
2468
- );
2469
- return await Promise.all(promises);
2470
- }
2471
- async initBrowserProvider() {
2472
- if (this.provider) {
2473
- return;
2474
- }
2475
- const Provider = await getBrowserProvider(this.project.config.browser, this.project);
2476
- this.provider = new Provider();
2477
- const browser = this.project.config.browser.name;
2478
- if (!browser) {
2479
- throw new Error(
2480
- `[${this.project.name}] Browser name is required. Please, set \`test.browser.name\` option manually.`
2481
- );
2482
- }
2483
- const supportedBrowsers = this.provider.getSupportedBrowsers();
2484
- if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
2485
- throw new Error(
2486
- `[${this.project.name}] Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`
2487
- );
2488
- }
2489
- const providerOptions = this.project.config.browser.providerOptions;
2490
- await this.provider.initialize(this.project, {
2491
- browser,
2492
- options: providerOptions
2493
- });
2494
- }
2495
- parseErrorStacktrace(e, options = {}) {
2496
- return parseErrorStacktrace(e, {
2497
- ...this.stackTraceOptions,
2498
- ...options
2499
- });
2500
- }
2501
- parseStacktrace(trace, options = {}) {
2502
- return parseStacktrace(trace, {
2503
- ...this.stackTraceOptions,
2504
- ...options
2505
- });
2506
- }
2507
- cdpSessionsPromises = /* @__PURE__ */ new Map();
2508
- async ensureCDPHandler(contextId, sessionId) {
2509
- const cachedHandler = this.state.cdps.get(sessionId);
2510
- if (cachedHandler) {
2511
- return cachedHandler;
2512
- }
2513
- const provider = this.provider;
2514
- if (!provider.getCDPSession) {
2515
- throw new Error(`CDP is not supported by the provider "${provider.name}".`);
2516
- }
2517
- const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
2518
- const promise2 = provider.getCDPSession(contextId).finally(() => {
2519
- this.cdpSessionsPromises.delete(sessionId);
2520
- });
2521
- this.cdpSessionsPromises.set(sessionId, promise2);
2522
- return promise2;
2523
- })();
2524
- const session = await promise;
2525
- const rpc = this.state.testers.get(sessionId);
2526
- if (!rpc) {
2527
- throw new Error(`Tester RPC "${sessionId}" was not established.`);
2528
- }
2529
- const handler = new BrowserServerCDPHandler(session, rpc);
2530
- this.state.cdps.set(
2531
- sessionId,
2532
- handler
2533
- );
2534
- return handler;
2535
- }
2536
- async close() {
2537
- await this.vite.close();
2538
- }
2539
- }
2540
- function wrapConfig(config) {
2541
- return {
2542
- ...config,
2543
- // workaround RegExp serialization
2544
- testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : void 0
2545
- };
2546
- }
2547
-
2548
3005
  const debug = createDebugger("vitest:browser:pool");
2549
- async function waitForTests(method, contextId, project, files) {
2550
- const context = project.browser.state.createAsyncContext(method, contextId, files);
3006
+ async function waitForTests(method, sessionId, project, files) {
3007
+ const context = project.vitest._browserSessions.createAsyncSession(method, sessionId, files, project);
2551
3008
  return await context;
2552
3009
  }
2553
- function createBrowserPool(ctx) {
3010
+ function createBrowserPool(vitest) {
2554
3011
  const providers = /* @__PURE__ */ new Set();
2555
3012
  const executeTests = async (method, project, files) => {
2556
- ctx.state.clearFiles(project, files);
3013
+ vitest.state.clearFiles(project, files);
2557
3014
  const browser = project.browser;
2558
3015
  const threadsCount = getThreadsCount(project);
2559
3016
  const provider = browser.provider;
@@ -2565,14 +3022,14 @@ function createBrowserPool(ctx) {
2565
3022
  `Can't find browser origin URL for project "${project.name}" when running tests for files "${files.join('", "')}"`
2566
3023
  );
2567
3024
  }
2568
- async function setBreakpoint(contextId, file) {
3025
+ async function setBreakpoint(sessionId, file) {
2569
3026
  if (!project.config.inspector.waitForDebugger) {
2570
3027
  return;
2571
3028
  }
2572
3029
  if (!provider.getCDPSession) {
2573
3030
  throw new Error("Unable to set breakpoint, CDP not supported");
2574
3031
  }
2575
- const session = await provider.getCDPSession(contextId);
3032
+ const session = await provider.getCDPSession(sessionId);
2576
3033
  await session.send("Debugger.enable", {});
2577
3034
  await session.send("Debugger.setBreakpointByUrl", {
2578
3035
  lineNumber: 0,
@@ -2596,13 +3053,13 @@ function createBrowserPool(ctx) {
2596
3053
  const promises = [];
2597
3054
  chunks.forEach((files2, index) => {
2598
3055
  if (orchestrators[index]) {
2599
- const [contextId, orchestrator] = orchestrators[index];
3056
+ const [sessionId, orchestrator] = orchestrators[index];
2600
3057
  debug?.(
2601
- "Reusing orchestrator (context %s) for files: %s",
2602
- contextId,
3058
+ "Reusing orchestrator (session %s) for files: %s",
3059
+ sessionId,
2603
3060
  [...files2.map((f) => relative(project.config.root, f))].join(", ")
2604
3061
  );
2605
- const promise = waitForTests(method, contextId, project, files2);
3062
+ const promise = waitForTests(method, sessionId, project, files2);
2606
3063
  const tester = orchestrator.createTesters(files2).catch((error) => {
2607
3064
  if (error instanceof Error && error.message.startsWith("[birpc] rpc is closed")) {
2608
3065
  return;
@@ -2611,16 +3068,16 @@ function createBrowserPool(ctx) {
2611
3068
  });
2612
3069
  promises.push(promise, tester);
2613
3070
  } else {
2614
- const contextId = crypto.randomUUID();
2615
- const waitPromise = waitForTests(method, contextId, project, files2);
3071
+ const sessionId = crypto.randomUUID();
3072
+ const waitPromise = waitForTests(method, sessionId, project, files2);
2616
3073
  debug?.(
2617
- "Opening a new context %s for files: %s",
2618
- contextId,
3074
+ "Opening a new session %s for files: %s",
3075
+ sessionId,
2619
3076
  [...files2.map((f) => relative(project.config.root, f))].join(", ")
2620
3077
  );
2621
3078
  const url = new URL("/", origin);
2622
- url.searchParams.set("contextId", contextId);
2623
- const page = provider.openPage(contextId, url.toString(), () => setBreakpoint(contextId, files2[0]));
3079
+ url.searchParams.set("sessionId", sessionId);
3080
+ const page = provider.openPage(sessionId, url.toString(), () => setBreakpoint(sessionId, files2[0]));
2624
3081
  promises.push(page, waitPromise);
2625
3082
  }
2626
3083
  });
@@ -2634,7 +3091,7 @@ function createBrowserPool(ctx) {
2634
3091
  groupedFiles.set(project, files);
2635
3092
  }
2636
3093
  let isCancelled = false;
2637
- ctx.onCancel(() => {
3094
+ vitest.onCancel(() => {
2638
3095
  isCancelled = true;
2639
3096
  });
2640
3097
  for (const [project, files] of groupedFiles.entries()) {
@@ -2654,14 +3111,14 @@ function createBrowserPool(ctx) {
2654
3111
  if (!config.fileParallelism) {
2655
3112
  return 1;
2656
3113
  }
2657
- return ctx.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
3114
+ return vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1);
2658
3115
  }
2659
3116
  return {
2660
3117
  name: "browser",
2661
3118
  async close() {
2662
3119
  await Promise.all([...providers].map((provider) => provider.close()));
2663
3120
  providers.clear();
2664
- ctx.resolvedProjects.forEach((project) => {
3121
+ vitest.resolvedProjects.forEach((project) => {
2665
3122
  project.browser?.state.orchestrators.forEach((orchestrator) => {
2666
3123
  orchestrator.$close();
2667
3124
  });
@@ -2676,19 +3133,19 @@ function escapePathToRegexp(path) {
2676
3133
  }
2677
3134
 
2678
3135
  async function createBrowserServer(project, configFile, prePlugins = [], postPlugins = []) {
2679
- if (project.ctx.version !== version) {
2680
- project.ctx.logger.warn(
3136
+ if (project.vitest.version !== version) {
3137
+ project.vitest.logger.warn(
2681
3138
  c.yellow(
2682
- `Loaded ${c.inverse(c.yellow(` vitest@${project.ctx.version} `))} and ${c.inverse(c.yellow(` @vitest/browser@${version} `))}.
3139
+ `Loaded ${c.inverse(c.yellow(` vitest@${project.vitest.version} `))} and ${c.inverse(c.yellow(` @vitest/browser@${version} `))}.
2683
3140
  Running mixed versions is not supported and may lead into bugs
2684
3141
  Update your dependencies and make sure the versions match.`
2685
3142
  )
2686
3143
  );
2687
3144
  }
2688
- const server = new BrowserServer(project, "/");
3145
+ const server = new ParentBrowserProject(project, "/");
2689
3146
  const configPath = typeof configFile === "string" ? configFile : false;
2690
3147
  const logLevel = process.env.VITEST_BROWSER_DEBUG ?? "info";
2691
- const logger = createViteLogger(project.logger, logLevel, {
3148
+ const logger = createViteLogger(project.vitest.logger, logLevel, {
2692
3149
  allowClearScreen: false
2693
3150
  });
2694
3151
  const vite = await createViteServer({