@vitest/browser 3.0.0-beta.2 → 3.0.0-beta.4

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