@vitest/browser 2.1.3 → 2.1.5

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
- import { createDebugger, isFileServingAllowed, getFilePoolName, resolveApiServerConfig, resolveFsAllow, distDir, createViteLogger, createViteServer } from 'vitest/node';
2
1
  import c from 'tinyrainbow';
3
- import fs, { existsSync, promises, readFileSync, lstatSync } from 'node:fs';
4
- import { WebSocketServer } from 'ws';
5
- import { ServerMockResolver, dynamicImportPlugin } from '@vitest/mocker/node';
6
- import { readFile as readFile$1, mkdir } from 'node:fs/promises';
7
- import { fileURLToPath } from 'node:url';
8
- import { createDefer, slash, toArray } from '@vitest/utils';
9
- import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
2
+ import { isFileServingAllowed, getFilePoolName, distDir, resolveApiServerConfig, resolveFsAllow, createDebugger, createViteLogger, createViteServer } from 'vitest/node';
3
+ import fs, { promises, readFileSync, lstatSync, existsSync } from 'node:fs';
10
4
  import { createRequire } from 'node:module';
11
- import sirv from 'sirv';
12
- import { defaultBrowserPort, coverageConfigDefaults } from 'vitest/config';
5
+ import { dynamicImportPlugin, ServerMockResolver } from '@vitest/mocker/node';
6
+ import { slash, toArray, createDefer } from '@vitest/utils';
13
7
  import MagicString from 'magic-string';
8
+ import sirv from 'sirv';
9
+ import { coverageConfigDefaults } from 'vitest/config';
10
+ import { fileURLToPath } from 'node:url';
14
11
  import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-Cv9wga63.js';
15
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';
16
14
  import crypto from 'node:crypto';
15
+ import { WebSocketServer } from 'ws';
16
+ import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
17
17
  import * as nodeos from 'node:os';
18
18
 
19
- var version = "2.1.3";
19
+ var version = "2.1.5";
20
20
 
21
21
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
22
22
  function normalizeWindowsPath(input = "") {
@@ -197,850 +197,367 @@ const basename = function(p, extension) {
197
197
  return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
198
198
  };
199
199
 
200
- const DEFAULT_TIMEOUT = 6e4;
201
- function defaultSerialize(i) {
202
- return i;
203
- }
204
- const defaultDeserialize = defaultSerialize;
205
- const { clearTimeout, setTimeout } = globalThis;
206
- const random = Math.random.bind(Math);
207
- function createBirpc(functions, options) {
208
- const {
209
- post,
210
- on,
211
- eventNames = [],
212
- serialize = defaultSerialize,
213
- deserialize = defaultDeserialize,
214
- resolver,
215
- timeout = DEFAULT_TIMEOUT
216
- } = options;
217
- const rpcPromiseMap = /* @__PURE__ */ new Map();
218
- let _promise;
219
- const rpc = new Proxy({}, {
220
- get(_, method) {
221
- if (method === "$functions")
222
- return functions;
223
- if (method === "then" && !eventNames.includes("then") && !("then" in functions))
224
- return void 0;
225
- const sendEvent = (...args) => {
226
- post(serialize({ m: method, a: args, t: "q" }));
227
- };
228
- if (eventNames.includes(method)) {
229
- sendEvent.asEvent = sendEvent;
230
- return sendEvent;
231
- }
232
- const sendCall = async (...args) => {
233
- await _promise;
234
- return new Promise((resolve, reject) => {
235
- const id = nanoid();
236
- let timeoutId;
237
- if (timeout >= 0) {
238
- timeoutId = setTimeout(() => {
239
- try {
240
- options.onTimeoutError?.(method, args);
241
- throw new Error(`[birpc] timeout on calling "${method}"`);
242
- } catch (e) {
243
- reject(e);
244
- }
245
- rpcPromiseMap.delete(id);
246
- }, timeout);
247
- if (typeof timeoutId === "object")
248
- timeoutId = timeoutId.unref?.();
249
- }
250
- rpcPromiseMap.set(id, { resolve, reject, timeoutId });
251
- post(serialize({ m: method, a: args, i: id, t: "q" }));
252
- });
253
- };
254
- sendCall.asEvent = sendEvent;
255
- return sendCall;
256
- }
257
- });
258
- _promise = on(async (data, ...extra) => {
259
- const msg = deserialize(data);
260
- if (msg.t === "q") {
261
- const { m: method, a: args } = msg;
262
- let result, error;
263
- const fn = resolver ? resolver(method, functions[method]) : functions[method];
264
- if (!fn) {
265
- error = new Error(`[birpc] function "${method}" not found`);
266
- } else {
267
- try {
268
- result = await fn.apply(rpc, args);
269
- } catch (e) {
270
- error = e;
271
- }
272
- }
273
- if (msg.i) {
274
- if (error && options.onError)
275
- options.onError(error, method, args);
276
- post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra);
277
- }
278
- } else {
279
- const { i: ack, r: result, e: error } = msg;
280
- const promise = rpcPromiseMap.get(ack);
281
- if (promise) {
282
- clearTimeout(promise.timeoutId);
283
- if (error)
284
- promise.reject(error);
285
- else
286
- promise.resolve(result);
287
- }
288
- rpcPromiseMap.delete(ack);
289
- }
290
- });
291
- return rpc;
292
- }
293
- const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
294
- function nanoid(size = 21) {
295
- let id = "";
296
- let i = size;
297
- while (i--)
298
- id += urlAlphabet[random() * 64 | 0];
299
- return id;
300
- }
301
-
302
- /// <reference types="../types/index.d.ts" />
303
-
304
- // (c) 2020-present Andrea Giammarchi
305
-
306
- const {parse: $parse, stringify: $stringify} = JSON;
307
- const {keys} = Object;
308
-
309
- const Primitive = String; // it could be Number
310
- const primitive = 'string'; // it could be 'number'
311
-
312
- const ignore = {};
313
- const object = 'object';
314
-
315
- const noop = (_, value) => value;
316
-
317
- const primitives = value => (
318
- value instanceof Primitive ? Primitive(value) : value
319
- );
200
+ const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
201
+ const distRoot = resolve(pkgRoot, "dist");
320
202
 
321
- const Primitives = (_, value) => (
322
- typeof value === primitive ? new Primitive(value) : value
323
- );
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
+ timeout: 1e3
209
+ });
210
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
211
+ const browser = context.browser;
212
+ const element = await browser.$(selector);
213
+ await element.clearValue();
214
+ } else {
215
+ throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
216
+ }
217
+ };
324
218
 
325
- const revive = (input, parsed, output, $) => {
326
- const lazy = [];
327
- for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
328
- const k = ke[y];
329
- const value = output[k];
330
- if (value instanceof Primitive) {
331
- const tmp = input[value];
332
- if (typeof tmp === object && !parsed.has(tmp)) {
333
- parsed.add(tmp);
334
- output[k] = ignore;
335
- lazy.push({k, a: [input, parsed, tmp, $]});
336
- }
337
- else
338
- output[k] = $.call(output, k, tmp);
339
- }
340
- else if (output[k] !== ignore)
341
- output[k] = $.call(output, k, value);
219
+ const click = async (context, selector, options = {}) => {
220
+ const provider = context.provider;
221
+ if (provider instanceof PlaywrightBrowserProvider) {
222
+ const tester = context.iframe;
223
+ await tester.locator(selector).click({
224
+ timeout: 1e3,
225
+ ...options
226
+ });
227
+ } else if (provider instanceof WebdriverBrowserProvider) {
228
+ const browser = context.browser;
229
+ await browser.$(selector).click(options);
230
+ } else {
231
+ throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
342
232
  }
343
- for (let {length} = lazy, i = 0; i < length; i++) {
344
- const {k, a} = lazy[i];
345
- output[k] = $.call(output, k, revive.apply(null, a));
233
+ };
234
+ const dblClick = async (context, selector, options = {}) => {
235
+ const provider = context.provider;
236
+ if (provider instanceof PlaywrightBrowserProvider) {
237
+ const tester = context.iframe;
238
+ await tester.locator(selector).dblclick(options);
239
+ } else if (provider instanceof WebdriverBrowserProvider) {
240
+ const browser = context.browser;
241
+ await browser.$(selector).doubleClick();
242
+ } else {
243
+ throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
244
+ }
245
+ };
246
+ const tripleClick = async (context, selector, options = {}) => {
247
+ const provider = context.provider;
248
+ if (provider instanceof PlaywrightBrowserProvider) {
249
+ const tester = context.iframe;
250
+ await tester.locator(selector).click({
251
+ timeout: 1e3,
252
+ ...options,
253
+ clickCount: 3
254
+ });
255
+ } else if (provider instanceof WebdriverBrowserProvider) {
256
+ const browser = context.browser;
257
+ 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();
258
+ } else {
259
+ throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
346
260
  }
347
- return output;
348
261
  };
349
262
 
350
- const set = (known, input, value) => {
351
- const index = Primitive(input.push(value) - 1);
352
- known.set(value, index);
353
- return index;
263
+ const dragAndDrop = async (context, source, target, options_) => {
264
+ if (context.provider instanceof PlaywrightBrowserProvider) {
265
+ const frame = await context.frame();
266
+ await frame.dragAndDrop(
267
+ source,
268
+ target,
269
+ {
270
+ timeout: 1e3,
271
+ ...options_
272
+ }
273
+ );
274
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
275
+ const $source = context.browser.$(source);
276
+ const $target = context.browser.$(target);
277
+ const options = options_ || {};
278
+ const duration = options.duration ?? 10;
279
+ 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();
280
+ } else {
281
+ throw new TypeError(`Provider "${context.provider.name}" does not support dragging elements`);
282
+ }
354
283
  };
355
284
 
356
- /**
357
- * Converts a specialized flatted string into a JS value.
358
- * @param {string} text
359
- * @param {(this: any, key: string, value: any) => any} [reviver]
360
- * @returns {any}
361
- */
362
- const parse = (text, reviver) => {
363
- const input = $parse(text, Primitives).map(primitives);
364
- const value = input[0];
365
- const $ = reviver || noop;
366
- const tmp = typeof value === object && value ?
367
- revive(input, new Set, value, $) :
368
- value;
369
- return $.call({'': tmp}, '', tmp);
285
+ const fill = async (context, selector, text, options = {}) => {
286
+ if (context.provider instanceof PlaywrightBrowserProvider) {
287
+ const { iframe } = context;
288
+ const element = iframe.locator(selector);
289
+ await element.fill(text, { timeout: 1e3, ...options });
290
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
291
+ const browser = context.browser;
292
+ await browser.$(selector).setValue(text);
293
+ } else {
294
+ throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
295
+ }
370
296
  };
371
297
 
372
- /**
373
- * Converts a JS value into a specialized flatted string.
374
- * @param {any} value
375
- * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
376
- * @param {string | number | undefined} [space]
377
- * @returns {string}
378
- */
379
- const stringify = (value, replacer, space) => {
380
- const $ = replacer && typeof replacer === object ?
381
- (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
382
- (replacer || noop);
383
- const known = new Map;
384
- const input = [];
385
- const output = [];
386
- let i = +set(known, input, $.call({'': value}, '', value));
387
- let firstRun = !i;
388
- while (i < input.length) {
389
- firstRun = true;
390
- output[i] = $stringify(input[i++], replace, space);
391
- }
392
- return '[' + output.join(',') + ']';
393
- function replace(key, value) {
394
- if (firstRun) {
395
- firstRun = !firstRun;
396
- return value;
298
+ 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"] };
299
+ Object.freeze(types);
300
+
301
+ var __classPrivateFieldGet = (null && null.__classPrivateFieldGet) || function (receiver, state, kind, f) {
302
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
303
+ 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");
304
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
305
+ };
306
+ var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
307
+ class Mime {
308
+ constructor(...args) {
309
+ _Mime_extensionToType.set(this, new Map());
310
+ _Mime_typeToExtension.set(this, new Map());
311
+ _Mime_typeToExtensions.set(this, new Map());
312
+ for (const arg of args) {
313
+ this.define(arg);
314
+ }
397
315
  }
398
- const after = $.call(this, key, value);
399
- switch (typeof after) {
400
- case object:
401
- if (after === null) return after;
402
- case primitive:
403
- return known.get(after) || set(known, input, after);
316
+ define(typeMap, force = false) {
317
+ for (let [type, extensions] of Object.entries(typeMap)) {
318
+ type = type.toLowerCase();
319
+ extensions = extensions.map((ext) => ext.toLowerCase());
320
+ if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) {
321
+ __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, new Set());
322
+ }
323
+ const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
324
+ let first = true;
325
+ for (let extension of extensions) {
326
+ const starred = extension.startsWith('*');
327
+ extension = starred ? extension.slice(1) : extension;
328
+ allExtensions?.add(extension);
329
+ if (first) {
330
+ __classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
331
+ }
332
+ first = false;
333
+ if (starred)
334
+ continue;
335
+ const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
336
+ if (currentType && currentType != type && !force) {
337
+ throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
338
+ }
339
+ __classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
340
+ }
341
+ }
342
+ return this;
404
343
  }
405
- return after;
406
- }
407
- };
408
-
409
- const debug$1 = createDebugger("vitest:browser:api");
410
- const BROWSER_API_PATH = "/__vitest_browser_api__";
411
- function setupBrowserRpc(server) {
412
- const project = server.project;
413
- const vite = server.vite;
414
- const ctx = project.ctx;
415
- const wss = new WebSocketServer({ noServer: true });
416
- vite.httpServer?.on("upgrade", (request, socket, head) => {
417
- if (!request.url) {
418
- return;
344
+ getType(path) {
345
+ if (typeof path !== 'string')
346
+ return null;
347
+ const last = path.replace(/^.*[/\\]/, '').toLowerCase();
348
+ const ext = last.replace(/^.*\./, '').toLowerCase();
349
+ const hasPath = last.length < path.length;
350
+ const hasDot = ext.length < last.length - 1;
351
+ if (!hasDot && hasPath)
352
+ return null;
353
+ return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
419
354
  }
420
- const { pathname, searchParams } = new URL(request.url, "http://localhost");
421
- if (pathname !== BROWSER_API_PATH) {
422
- return;
355
+ getExtension(type) {
356
+ if (typeof type !== 'string')
357
+ return null;
358
+ type = type?.split?.(';')[0];
359
+ return ((type && __classPrivateFieldGet(this, _Mime_typeToExtension, "f").get(type.trim().toLowerCase())) ?? null);
423
360
  }
424
- const type = searchParams.get("type") ?? "tester";
425
- const sessionId = searchParams.get("sessionId") ?? "0";
426
- wss.handleUpgrade(request, socket, head, (ws) => {
427
- wss.emit("connection", ws, request);
428
- const rpc = setupClient(sessionId, ws);
429
- const state = server.state;
430
- const clients = type === "tester" ? state.testers : state.orchestrators;
431
- clients.set(sessionId, rpc);
432
- debug$1?.("[%s] Browser API connected to %s", sessionId, type);
433
- ws.on("close", () => {
434
- debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
435
- clients.delete(sessionId);
436
- server.state.removeCDPHandler(sessionId);
437
- });
438
- });
439
- });
440
- function checkFileAccess(path) {
441
- if (!isFileServingAllowed(path, vite)) {
442
- throw new Error(
443
- `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
444
- );
361
+ getAllExtensions(type) {
362
+ if (typeof type !== 'string')
363
+ return null;
364
+ return __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type.toLowerCase()) ?? null;
445
365
  }
366
+ _freeze() {
367
+ this.define = () => {
368
+ throw new Error('define() not allowed for built-in Mime objects. See https://github.com/broofa/mime/blob/main/README.md#custom-mime-instances');
369
+ };
370
+ Object.freeze(this);
371
+ for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
372
+ Object.freeze(extensions);
373
+ }
374
+ return this;
375
+ }
376
+ _getTestState() {
377
+ return {
378
+ types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
379
+ extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f"),
380
+ };
381
+ }
382
+ }
383
+ _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
384
+
385
+ var mime = new Mime(types)._freeze();
386
+
387
+ function assertFileAccess(path, project) {
388
+ if (!isFileServingAllowed(path, project.server) && !isFileServingAllowed(path, project.ctx.server)) {
389
+ throw new Error(
390
+ `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
391
+ );
446
392
  }
447
- function setupClient(sessionId, ws) {
448
- const mockResolver = new ServerMockResolver(server.vite, {
449
- moduleDirectories: project.config.server?.deps?.moduleDirectories
393
+ }
394
+ const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
395
+ const filepath = resolve$1(dirname$1(testPath), path);
396
+ assertFileAccess(filepath, project);
397
+ if (typeof options === "object" && !options.encoding) {
398
+ options.encoding = "utf-8";
399
+ }
400
+ return promises.readFile(filepath, options);
401
+ };
402
+ const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
403
+ const filepath = resolve$1(dirname$1(testPath), path);
404
+ assertFileAccess(filepath, project);
405
+ const dir = dirname$1(filepath);
406
+ if (!fs.existsSync(dir)) {
407
+ await promises.mkdir(dir, { recursive: true });
408
+ }
409
+ await promises.writeFile(filepath, data, options);
410
+ };
411
+ const removeFile = async ({ project, testPath = process.cwd() }, path) => {
412
+ const filepath = resolve$1(dirname$1(testPath), path);
413
+ assertFileAccess(filepath, project);
414
+ await promises.rm(filepath);
415
+ };
416
+ const _fileInfo = async ({ project, testPath = process.cwd() }, path, encoding) => {
417
+ const filepath = resolve$1(dirname$1(testPath), path);
418
+ assertFileAccess(filepath, project);
419
+ const content = await promises.readFile(filepath, encoding || "base64");
420
+ return {
421
+ content,
422
+ basename: basename$1(filepath),
423
+ mime: mime.getType(filepath)
424
+ };
425
+ };
426
+
427
+ const hover = async (context, selector, options = {}) => {
428
+ if (context.provider instanceof PlaywrightBrowserProvider) {
429
+ await context.iframe.locator(selector).hover({
430
+ timeout: 1e3,
431
+ ...options
450
432
  });
451
- const rpc = createBirpc(
452
- {
453
- async onUnhandledError(error, type) {
454
- if (error && typeof error === "object") {
455
- const _error = error;
456
- _error.stacks = server.parseErrorStacktrace(_error);
457
- }
458
- ctx.state.catchError(error, type);
459
- },
460
- async onCollected(files) {
461
- ctx.state.collectFiles(project, files);
462
- await ctx.report("onCollected", files);
463
- },
464
- async onTaskUpdate(packs) {
465
- ctx.state.updateTasks(packs);
466
- await ctx.report("onTaskUpdate", packs);
467
- },
468
- onAfterSuiteRun(meta) {
469
- ctx.coverageProvider?.onAfterSuiteRun(meta);
470
- },
471
- sendLog(log) {
472
- return ctx.report("onUserConsoleLog", log);
473
- },
474
- resolveSnapshotPath(testPath) {
475
- return ctx.snapshot.resolvePath(testPath);
476
- },
477
- resolveSnapshotRawPath(testPath, rawPath) {
478
- return ctx.snapshot.resolveRawPath(testPath, rawPath);
479
- },
480
- snapshotSaved(snapshot) {
481
- ctx.snapshot.add(snapshot);
482
- },
483
- async readSnapshotFile(snapshotPath) {
484
- checkFileAccess(snapshotPath);
485
- if (!existsSync(snapshotPath)) {
486
- return null;
487
- }
488
- return promises.readFile(snapshotPath, "utf-8");
489
- },
490
- async saveSnapshotFile(id, content) {
491
- checkFileAccess(id);
492
- await promises.mkdir(dirname(id), { recursive: true });
493
- return promises.writeFile(id, content, "utf-8");
494
- },
495
- async removeSnapshotFile(id) {
496
- checkFileAccess(id);
497
- if (!existsSync(id)) {
498
- throw new Error(`Snapshot file "${id}" does not exist.`);
499
- }
500
- return promises.unlink(id);
501
- },
502
- getBrowserFileSourceMap(id) {
503
- const mod = server.vite.moduleGraph.getModuleById(id);
504
- return mod?.transformResult?.map;
505
- },
506
- onCancel(reason) {
507
- ctx.cancelCurrentRun(reason);
508
- },
509
- async resolveId(id, importer) {
510
- return mockResolver.resolveId(id, importer);
511
- },
512
- debug(...args) {
513
- ctx.logger.console.debug(...args);
514
- },
515
- getCountOfFailedTests() {
516
- return ctx.state.getCountOfFailedTests();
517
- },
518
- async triggerCommand(contextId, command, testPath, payload) {
519
- debug$1?.('[%s] Triggering command "%s"', contextId, command);
520
- const provider = server.provider;
521
- if (!provider) {
522
- throw new Error("Commands are only available for browser tests.");
523
- }
524
- const commands = project.config.browser?.commands;
525
- if (!commands || !commands[command]) {
526
- throw new Error(`Unknown command "${command}".`);
527
- }
528
- if (provider.beforeCommand) {
529
- await provider.beforeCommand(command, payload);
530
- }
531
- const context = Object.assign(
532
- {
533
- testPath,
534
- project,
535
- provider,
536
- contextId
537
- },
538
- provider.getCommandsContext(contextId)
539
- );
540
- let result;
541
- try {
542
- result = await commands[command](context, ...payload);
543
- } finally {
544
- if (provider.afterCommand) {
545
- await provider.afterCommand(command, payload);
546
- }
547
- }
548
- return result;
549
- },
550
- finishBrowserTests(contextId) {
551
- debug$1?.("[%s] Finishing browser tests for context", contextId);
552
- return server.state.getContext(contextId)?.resolve();
553
- },
554
- resolveMock(rawId, importer, options) {
555
- return mockResolver.resolveMock(rawId, importer, options);
556
- },
557
- invalidate(ids) {
558
- return mockResolver.invalidate(ids);
559
- },
560
- // CDP
561
- async sendCdpEvent(contextId, event, payload) {
562
- const cdp = await server.ensureCDPHandler(contextId, sessionId);
563
- return cdp.send(event, payload);
564
- },
565
- async trackCdpEvent(contextId, type, event, listenerId) {
566
- const cdp = await server.ensureCDPHandler(contextId, sessionId);
567
- cdp[type](event, listenerId);
568
- }
569
- },
570
- {
571
- post: (msg) => ws.send(msg),
572
- on: (fn) => ws.on("message", fn),
573
- eventNames: ["onCancel", "cdpEvent"],
574
- serialize: (data) => stringify(data, stringifyReplace),
575
- deserialize: parse,
576
- onTimeoutError(functionName) {
577
- throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
578
- }
579
- }
580
- );
581
- ctx.onCancel((reason) => rpc.onCancel(reason));
582
- return rpc;
433
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
434
+ const browser = context.browser;
435
+ await browser.$(selector).moveTo(options);
436
+ } else {
437
+ throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
583
438
  }
439
+ };
440
+
441
+ var clickableInputTypes;
442
+ (function(clickableInputTypes) {
443
+ clickableInputTypes["button"] = "button";
444
+ clickableInputTypes["color"] = "color";
445
+ clickableInputTypes["file"] = "file";
446
+ clickableInputTypes["image"] = "image";
447
+ clickableInputTypes["reset"] = "reset";
448
+ clickableInputTypes["submit"] = "submit";
449
+ clickableInputTypes["checkbox"] = "checkbox";
450
+ clickableInputTypes["radio"] = "radio";
451
+ })(clickableInputTypes || (clickableInputTypes = {}));
452
+
453
+ var editableInputTypes;
454
+ (function(editableInputTypes) {
455
+ editableInputTypes["text"] = "text";
456
+ editableInputTypes["date"] = "date";
457
+ editableInputTypes["datetime-local"] = "datetime-local";
458
+ editableInputTypes["email"] = "email";
459
+ editableInputTypes["month"] = "month";
460
+ editableInputTypes["number"] = "number";
461
+ editableInputTypes["password"] = "password";
462
+ editableInputTypes["search"] = "search";
463
+ editableInputTypes["tel"] = "tel";
464
+ editableInputTypes["time"] = "time";
465
+ editableInputTypes["url"] = "url";
466
+ editableInputTypes["week"] = "week";
467
+ })(editableInputTypes || (editableInputTypes = {}));
468
+
469
+ var maxLengthSupportedTypes;
470
+ (function(maxLengthSupportedTypes) {
471
+ maxLengthSupportedTypes["email"] = "email";
472
+ maxLengthSupportedTypes["password"] = "password";
473
+ maxLengthSupportedTypes["search"] = "search";
474
+ maxLengthSupportedTypes["telephone"] = "telephone";
475
+ maxLengthSupportedTypes["text"] = "text";
476
+ maxLengthSupportedTypes["url"] = "url";
477
+ })(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
478
+
479
+ var bracketDict;
480
+ (function(bracketDict) {
481
+ bracketDict["{"] = "}";
482
+ bracketDict["["] = "]";
483
+ })(bracketDict || (bracketDict = {}));
484
+ /**
485
+ * Read the next key definition from user input
486
+ *
487
+ * Describe key per `{descriptor}` or `[descriptor]`.
488
+ * Everything else will be interpreted as a single character as descriptor - e.g. `a`.
489
+ * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
490
+ * A previously pressed key can be released per `{/descriptor}`.
491
+ * Keeping the key pressed can be written as `{descriptor>}`.
492
+ * When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`.
493
+ * You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key.
494
+ */ function readNextDescriptor(text, context) {
495
+ let pos = 0;
496
+ const startBracket = text[pos] in bracketDict ? text[pos] : '';
497
+ pos += startBracket.length;
498
+ const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text);
499
+ const type = isEscapedChar ? '' : startBracket;
500
+ return {
501
+ type,
502
+ ...type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type)
503
+ };
584
504
  }
585
- function cloneByOwnProperties(value) {
586
- return Object.getOwnPropertyNames(value).reduce(
587
- (clone, prop) => ({
588
- ...clone,
589
- [prop]: value[prop]
590
- }),
591
- {}
592
- );
505
+ function readPrintableChar(text, pos, context) {
506
+ const descriptor = text[pos];
507
+ assertDescriptor(descriptor, text, pos);
508
+ pos += descriptor.length;
509
+ return {
510
+ consumedLength: pos,
511
+ descriptor,
512
+ releasePrevious: false,
513
+ releaseSelf: true,
514
+ repeat: 1
515
+ };
593
516
  }
594
- function stringifyReplace(key, value) {
595
- if (value instanceof Error) {
596
- const cloned = cloneByOwnProperties(value);
517
+ function readTag(text, pos, startBracket, context) {
518
+ var _text_slice_match, _text_slice_match1;
519
+ const releasePreviousModifier = text[pos] === '/' ? '/' : '';
520
+ pos += releasePreviousModifier.length;
521
+ const escapedDescriptor = startBracket === '{' && text[pos] === '\\';
522
+ pos += Number(escapedDescriptor);
523
+ 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];
524
+ assertDescriptor(descriptor, text, pos);
525
+ pos += descriptor.length;
526
+ var _text_slice_match_;
527
+ 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_ : '';
528
+ pos += repeatModifier.length;
529
+ const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
530
+ pos += releaseSelfModifier.length;
531
+ const expectedEndBracket = bracketDict[startBracket];
532
+ const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
533
+ if (!endBracket) {
534
+ throw new Error(getErrorMessage([
535
+ !repeatModifier && 'repeat modifier',
536
+ !releaseSelfModifier && 'release modifier',
537
+ `"${expectedEndBracket}"`
538
+ ].filter(Boolean).join(' or '), text[pos], text));
539
+ }
540
+ pos += endBracket.length;
597
541
  return {
598
- name: value.name,
599
- message: value.message,
600
- stack: value.stack,
601
- ...cloned
542
+ consumedLength: pos,
543
+ descriptor,
544
+ releasePrevious: !!releasePreviousModifier,
545
+ repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
546
+ releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier)
602
547
  };
603
- } else {
604
- return value;
605
- }
606
548
  }
607
-
608
- class BrowserServerState {
609
- orchestrators = /* @__PURE__ */ new Map();
610
- testers = /* @__PURE__ */ new Map();
611
- cdps = /* @__PURE__ */ new Map();
612
- contexts = /* @__PURE__ */ new Map();
613
- getContext(contextId) {
614
- return this.contexts.get(contextId);
615
- }
616
- createAsyncContext(method, contextId, files) {
617
- const defer = createDefer();
618
- this.contexts.set(contextId, {
619
- files,
620
- method,
621
- resolve: () => {
622
- defer.resolve();
623
- this.contexts.delete(contextId);
624
- },
625
- reject: defer.reject
626
- });
627
- return defer;
628
- }
629
- async removeCDPHandler(sessionId) {
630
- this.cdps.delete(sessionId);
631
- }
549
+ function assertDescriptor(descriptor, text, pos, context) {
550
+ if (!descriptor) {
551
+ throw new Error(getErrorMessage('key descriptor', text[pos], text));
552
+ }
632
553
  }
633
-
634
- function replacer(code, values) {
635
- return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? "");
636
- }
637
- const builtinProviders = ["webdriverio", "playwright", "preview"];
638
- async function getBrowserProvider(options, project) {
639
- if (options.provider == null || builtinProviders.includes(options.provider)) {
640
- const providers = await import('./providers.js');
641
- const provider = options.provider || "preview";
642
- return providers[provider];
643
- }
644
- let customProviderModule;
645
- try {
646
- customProviderModule = await project.runner.executeId(
647
- options.provider
648
- );
649
- } catch (error) {
650
- throw new Error(
651
- `Failed to load custom BrowserProvider from ${options.provider}`,
652
- { cause: error }
653
- );
654
- }
655
- if (customProviderModule.default == null) {
656
- throw new Error(
657
- `Custom BrowserProvider loaded from ${options.provider} was not the default export`
658
- );
659
- }
660
- return customProviderModule.default;
661
- }
662
-
663
- class BrowserServerCDPHandler {
664
- constructor(session, tester) {
665
- this.session = session;
666
- this.tester = tester;
667
- }
668
- listenerIds = {};
669
- listeners = {};
670
- send(method, params) {
671
- return this.session.send(method, params);
672
- }
673
- on(event, id, once = false) {
674
- if (!this.listenerIds[event]) {
675
- this.listenerIds[event] = [];
676
- }
677
- this.listenerIds[event].push(id);
678
- if (!this.listeners[event]) {
679
- this.listeners[event] = (payload) => {
680
- this.tester.cdpEvent(
681
- event,
682
- payload
683
- );
684
- if (once) {
685
- this.off(event, id);
686
- }
687
- };
688
- this.session.on(event, this.listeners[event]);
689
- }
690
- }
691
- off(event, id) {
692
- if (!this.listenerIds[event]) {
693
- this.listenerIds[event] = [];
694
- }
695
- this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
696
- if (!this.listenerIds[event].length) {
697
- this.session.off(event, this.listeners[event]);
698
- delete this.listeners[event];
699
- }
700
- }
701
- once(event, listener) {
702
- this.on(event, listener, true);
703
- }
704
- }
705
-
706
- class BrowserServer {
707
- constructor(project, base) {
708
- this.project = project;
709
- this.base = base;
710
- this.stackTraceOptions = {
711
- frameFilter: project.config.onStackTrace,
712
- getSourceMap: (id) => {
713
- const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
714
- return result?.map;
715
- },
716
- getFileName: (id) => {
717
- const mod = this.vite.moduleGraph.getModuleById(id);
718
- if (mod?.file) {
719
- return mod.file;
720
- }
721
- const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
722
- if (modUrl?.file) {
723
- return modUrl.file;
724
- }
725
- return id;
726
- }
727
- };
728
- this.state = new BrowserServerState();
729
- const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
730
- const distRoot = resolve(pkgRoot, "dist");
731
- this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
732
- this.faviconUrl = `${base}__vitest__/favicon.svg`;
733
- this.manifest = (async () => {
734
- return JSON.parse(
735
- await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8")
736
- );
737
- })().then((manifest) => this.manifest = manifest);
738
- this.testerHtml = readFile$1(
739
- resolve(distRoot, "client/tester/tester.html"),
740
- "utf8"
741
- ).then((html) => this.testerHtml = html);
742
- 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);
743
- this.injectorJs = readFile$1(
744
- resolve(distRoot, "client/esm-client-injector.js"),
745
- "utf8"
746
- ).then((js) => this.injectorJs = js);
747
- this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
748
- const builtinProviders = ["playwright", "webdriverio", "preview"];
749
- const providerName = project.config.browser.provider || "preview";
750
- if (builtinProviders.includes(providerName)) {
751
- this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
752
- }
753
- this.stateJs = readFile$1(
754
- resolve(distRoot, "state.js"),
755
- "utf-8"
756
- ).then((js) => this.stateJs = js);
757
- }
758
- faviconUrl;
759
- prefixTesterUrl;
760
- orchestratorScripts;
761
- testerScripts;
762
- manifest;
763
- testerHtml;
764
- orchestratorHtml;
765
- injectorJs;
766
- errorCatcherUrl;
767
- locatorsUrl;
768
- stateJs;
769
- state;
770
- provider;
771
- vite;
772
- stackTraceOptions;
773
- setServer(server) {
774
- this.vite = server;
775
- }
776
- getSerializableConfig() {
777
- const config = wrapConfig(this.project.getSerializableConfig());
778
- config.env ??= {};
779
- config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
780
- return config;
781
- }
782
- resolveTesterUrl(pathname) {
783
- const [contextId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
784
- const decodedTestFile = decodeURIComponent(testFile);
785
- return { contextId, testFile: decodedTestFile };
786
- }
787
- async formatScripts(scripts) {
788
- if (!scripts?.length) {
789
- return "";
790
- }
791
- const server = this.vite;
792
- const promises = scripts.map(
793
- async ({ content, src, async, id, type = "module" }, index) => {
794
- const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : void 0) || src;
795
- const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
796
- await server.moduleGraph.ensureEntryFromUrl(transformId);
797
- const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
798
- return `<script type="${type}"${async ? " async" : ""}${srcLink ? ` src="${slash(`/@fs/${srcLink}`)}"` : ""}>${contentProcessed || ""}<\/script>`;
799
- }
800
- );
801
- return (await Promise.all(promises)).join("\n");
802
- }
803
- async initBrowserProvider() {
804
- if (this.provider) {
805
- return;
806
- }
807
- const Provider = await getBrowserProvider(this.project.config.browser, this.project);
808
- this.provider = new Provider();
809
- const browser = this.project.config.browser.name;
810
- if (!browser) {
811
- throw new Error(
812
- `[${this.project.getName()}] Browser name is required. Please, set \`test.browser.name\` option manually.`
813
- );
814
- }
815
- const supportedBrowsers = this.provider.getSupportedBrowsers();
816
- if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
817
- throw new Error(
818
- `[${this.project.getName()}] Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`
819
- );
820
- }
821
- const providerOptions = this.project.config.browser.providerOptions;
822
- await this.provider.initialize(this.project, {
823
- browser,
824
- options: providerOptions
825
- });
826
- }
827
- parseErrorStacktrace(e, options = {}) {
828
- return parseErrorStacktrace(e, {
829
- ...this.stackTraceOptions,
830
- ...options
831
- });
832
- }
833
- parseStacktrace(trace, options = {}) {
834
- return parseStacktrace(trace, {
835
- ...this.stackTraceOptions,
836
- ...options
837
- });
838
- }
839
- cdpSessionsPromises = /* @__PURE__ */ new Map();
840
- async ensureCDPHandler(contextId, sessionId) {
841
- const cachedHandler = this.state.cdps.get(sessionId);
842
- if (cachedHandler) {
843
- return cachedHandler;
844
- }
845
- const provider = this.provider;
846
- if (!provider.getCDPSession) {
847
- throw new Error(`CDP is not supported by the provider "${provider.name}".`);
848
- }
849
- const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
850
- const promise2 = provider.getCDPSession(contextId).finally(() => {
851
- this.cdpSessionsPromises.delete(sessionId);
852
- });
853
- this.cdpSessionsPromises.set(sessionId, promise2);
854
- return promise2;
855
- })();
856
- const session = await promise;
857
- const rpc = this.state.testers.get(sessionId);
858
- if (!rpc) {
859
- throw new Error(`Tester RPC "${sessionId}" was not established.`);
860
- }
861
- const handler = new BrowserServerCDPHandler(session, rpc);
862
- this.state.cdps.set(
863
- sessionId,
864
- handler
865
- );
866
- return handler;
867
- }
868
- async close() {
869
- await this.vite.close();
870
- }
871
- }
872
- function wrapConfig(config) {
873
- return {
874
- ...config,
875
- // workaround RegExp serialization
876
- testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : void 0
877
- };
878
- }
879
-
880
- const click = async (context, selector, options = {}) => {
881
- const provider = context.provider;
882
- if (provider instanceof PlaywrightBrowserProvider) {
883
- const tester = context.iframe;
884
- await tester.locator(selector).click({
885
- timeout: 1e3,
886
- ...options
887
- });
888
- } else if (provider instanceof WebdriverBrowserProvider) {
889
- const browser = context.browser;
890
- await browser.$(selector).click(options);
891
- } else {
892
- throw new TypeError(`Provider "${provider.name}" doesn't support click command`);
893
- }
894
- };
895
- const dblClick = async (context, selector, options = {}) => {
896
- const provider = context.provider;
897
- if (provider instanceof PlaywrightBrowserProvider) {
898
- const tester = context.iframe;
899
- await tester.locator(selector).dblclick(options);
900
- } else if (provider instanceof WebdriverBrowserProvider) {
901
- const browser = context.browser;
902
- await browser.$(selector).doubleClick();
903
- } else {
904
- throw new TypeError(`Provider "${provider.name}" doesn't support dblClick command`);
905
- }
906
- };
907
- const tripleClick = async (context, selector, options = {}) => {
908
- const provider = context.provider;
909
- if (provider instanceof PlaywrightBrowserProvider) {
910
- const tester = context.iframe;
911
- await tester.locator(selector).click({
912
- timeout: 1e3,
913
- ...options,
914
- clickCount: 3
915
- });
916
- } else if (provider instanceof WebdriverBrowserProvider) {
917
- const browser = context.browser;
918
- 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();
919
- } else {
920
- throw new TypeError(`Provider "${provider.name}" doesn't support tripleClick command`);
921
- }
922
- };
923
-
924
- var clickableInputTypes;
925
- (function(clickableInputTypes) {
926
- clickableInputTypes["button"] = "button";
927
- clickableInputTypes["color"] = "color";
928
- clickableInputTypes["file"] = "file";
929
- clickableInputTypes["image"] = "image";
930
- clickableInputTypes["reset"] = "reset";
931
- clickableInputTypes["submit"] = "submit";
932
- clickableInputTypes["checkbox"] = "checkbox";
933
- clickableInputTypes["radio"] = "radio";
934
- })(clickableInputTypes || (clickableInputTypes = {}));
935
-
936
- var editableInputTypes;
937
- (function(editableInputTypes) {
938
- editableInputTypes["text"] = "text";
939
- editableInputTypes["date"] = "date";
940
- editableInputTypes["datetime-local"] = "datetime-local";
941
- editableInputTypes["email"] = "email";
942
- editableInputTypes["month"] = "month";
943
- editableInputTypes["number"] = "number";
944
- editableInputTypes["password"] = "password";
945
- editableInputTypes["search"] = "search";
946
- editableInputTypes["tel"] = "tel";
947
- editableInputTypes["time"] = "time";
948
- editableInputTypes["url"] = "url";
949
- editableInputTypes["week"] = "week";
950
- })(editableInputTypes || (editableInputTypes = {}));
951
-
952
- var maxLengthSupportedTypes;
953
- (function(maxLengthSupportedTypes) {
954
- maxLengthSupportedTypes["email"] = "email";
955
- maxLengthSupportedTypes["password"] = "password";
956
- maxLengthSupportedTypes["search"] = "search";
957
- maxLengthSupportedTypes["telephone"] = "telephone";
958
- maxLengthSupportedTypes["text"] = "text";
959
- maxLengthSupportedTypes["url"] = "url";
960
- })(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
961
-
962
- var bracketDict;
963
- (function(bracketDict) {
964
- bracketDict["{"] = "}";
965
- bracketDict["["] = "]";
966
- })(bracketDict || (bracketDict = {}));
967
- /**
968
- * Read the next key definition from user input
969
- *
970
- * Describe key per `{descriptor}` or `[descriptor]`.
971
- * Everything else will be interpreted as a single character as descriptor - e.g. `a`.
972
- * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
973
- * A previously pressed key can be released per `{/descriptor}`.
974
- * Keeping the key pressed can be written as `{descriptor>}`.
975
- * When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`.
976
- * You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key.
977
- */ function readNextDescriptor(text, context) {
978
- let pos = 0;
979
- const startBracket = text[pos] in bracketDict ? text[pos] : '';
980
- pos += startBracket.length;
981
- const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text);
982
- const type = isEscapedChar ? '' : startBracket;
983
- return {
984
- type,
985
- ...type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type)
986
- };
987
- }
988
- function readPrintableChar(text, pos, context) {
989
- const descriptor = text[pos];
990
- assertDescriptor(descriptor, text, pos);
991
- pos += descriptor.length;
992
- return {
993
- consumedLength: pos,
994
- descriptor,
995
- releasePrevious: false,
996
- releaseSelf: true,
997
- repeat: 1
998
- };
999
- }
1000
- function readTag(text, pos, startBracket, context) {
1001
- var _text_slice_match, _text_slice_match1;
1002
- const releasePreviousModifier = text[pos] === '/' ? '/' : '';
1003
- pos += releasePreviousModifier.length;
1004
- const escapedDescriptor = startBracket === '{' && text[pos] === '\\';
1005
- pos += Number(escapedDescriptor);
1006
- 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];
1007
- assertDescriptor(descriptor, text, pos);
1008
- pos += descriptor.length;
1009
- var _text_slice_match_;
1010
- 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_ : '';
1011
- pos += repeatModifier.length;
1012
- const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
1013
- pos += releaseSelfModifier.length;
1014
- const expectedEndBracket = bracketDict[startBracket];
1015
- const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
1016
- if (!endBracket) {
1017
- throw new Error(getErrorMessage([
1018
- !repeatModifier && 'repeat modifier',
1019
- !releaseSelfModifier && 'release modifier',
1020
- `"${expectedEndBracket}"`
1021
- ].filter(Boolean).join(' or '), text[pos], text));
1022
- }
1023
- pos += endBracket.length;
1024
- return {
1025
- consumedLength: pos,
1026
- descriptor,
1027
- releasePrevious: !!releasePreviousModifier,
1028
- repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
1029
- releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier)
1030
- };
1031
- }
1032
- function assertDescriptor(descriptor, text, pos, context) {
1033
- if (!descriptor) {
1034
- throw new Error(getErrorMessage('key descriptor', text[pos], text));
1035
- }
1036
- }
1037
- function hasReleaseSelf(releaseSelfModifier, repeatModifier) {
1038
- if (releaseSelfModifier) {
1039
- return releaseSelfModifier === '/';
1040
- }
1041
- if (repeatModifier) {
1042
- return false;
1043
- }
554
+ function hasReleaseSelf(releaseSelfModifier, repeatModifier) {
555
+ if (releaseSelfModifier) {
556
+ return releaseSelfModifier === '/';
557
+ }
558
+ if (repeatModifier) {
559
+ return false;
560
+ }
1044
561
  }
1045
562
  function getErrorMessage(expected, found, text, context) {
1046
563
  return `Expected ${expected} but found "${found !== null && found !== void 0 ? found : ''}" in "${text}"
@@ -1065,45 +582,6 @@ var PointerEventsCheckLevel;
1065
582
  /** No pointer events check */ PointerEventsCheckLevel[PointerEventsCheckLevel["Never"] = 0] = "Never";
1066
583
  })(PointerEventsCheckLevel || (PointerEventsCheckLevel = {}));
1067
584
 
1068
- /**
1069
- * Parse key defintions per `keyboardMap`
1070
- *
1071
- * Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
1072
- * Everything else will be interpreted as a typed character - e.g. `a`.
1073
- * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
1074
- * Keeping the key pressed can be written as `{key>}`.
1075
- * When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
1076
- * You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
1077
- */ function parseKeyDef(keyboardMap, text) {
1078
- const defs = [];
1079
- do {
1080
- const { type, descriptor, consumedLength, releasePrevious, releaseSelf = true, repeat } = readNextDescriptor(text);
1081
- var _keyboardMap_find;
1082
- const keyDef = (_keyboardMap_find = keyboardMap.find((def)=>{
1083
- if (type === '[') {
1084
- var _def_code;
1085
- return ((_def_code = def.code) === null || _def_code === void 0 ? void 0 : _def_code.toLowerCase()) === descriptor.toLowerCase();
1086
- } else if (type === '{') {
1087
- var _def_key;
1088
- return ((_def_key = def.key) === null || _def_key === void 0 ? void 0 : _def_key.toLowerCase()) === descriptor.toLowerCase();
1089
- }
1090
- return def.key === descriptor;
1091
- })) !== null && _keyboardMap_find !== void 0 ? _keyboardMap_find : {
1092
- key: 'Unknown',
1093
- code: 'Unknown',
1094
- [type === '[' ? 'code' : 'key']: descriptor
1095
- };
1096
- defs.push({
1097
- keyDef,
1098
- releasePrevious,
1099
- releaseSelf,
1100
- repeat
1101
- });
1102
- text = text.slice(consumedLength);
1103
- }while (text)
1104
- return defs;
1105
- }
1106
-
1107
585
  var DOM_KEY_LOCATION;
1108
586
  (function(DOM_KEY_LOCATION) {
1109
587
  DOM_KEY_LOCATION[DOM_KEY_LOCATION["STANDARD"] = 0] = "STANDARD";
@@ -1263,6 +741,45 @@ var DOM_KEY_LOCATION;
1263
741
  }
1264
742
  ];
1265
743
 
744
+ /**
745
+ * Parse key defintions per `keyboardMap`
746
+ *
747
+ * Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
748
+ * Everything else will be interpreted as a typed character - e.g. `a`.
749
+ * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
750
+ * Keeping the key pressed can be written as `{key>}`.
751
+ * When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
752
+ * You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
753
+ */ function parseKeyDef(keyboardMap, text) {
754
+ const defs = [];
755
+ do {
756
+ const { type, descriptor, consumedLength, releasePrevious, releaseSelf = true, repeat } = readNextDescriptor(text);
757
+ var _keyboardMap_find;
758
+ const keyDef = (_keyboardMap_find = keyboardMap.find((def)=>{
759
+ if (type === '[') {
760
+ var _def_code;
761
+ return ((_def_code = def.code) === null || _def_code === void 0 ? void 0 : _def_code.toLowerCase()) === descriptor.toLowerCase();
762
+ } else if (type === '{') {
763
+ var _def_key;
764
+ return ((_def_key = def.key) === null || _def_key === void 0 ? void 0 : _def_key.toLowerCase()) === descriptor.toLowerCase();
765
+ }
766
+ return def.key === descriptor;
767
+ })) !== null && _keyboardMap_find !== void 0 ? _keyboardMap_find : {
768
+ key: 'Unknown',
769
+ code: 'Unknown',
770
+ [type === '[' ? 'code' : 'key']: descriptor
771
+ };
772
+ defs.push({
773
+ keyDef,
774
+ releasePrevious,
775
+ releaseSelf,
776
+ repeat
777
+ });
778
+ text = text.slice(consumedLength);
779
+ }while (text)
780
+ return defs;
781
+ }
782
+
1266
783
  const keyboard = async (context, text, state) => {
1267
784
  if (context.provider instanceof PlaywrightBrowserProvider) {
1268
785
  const frame = await context.frame();
@@ -1292,6 +809,24 @@ const keyboard = async (context, text, state) => {
1292
809
  unreleased: Array.from(pressed)
1293
810
  };
1294
811
  };
812
+ const keyboardCleanup = async (context, state) => {
813
+ const { provider, contextId } = context;
814
+ if (provider instanceof PlaywrightBrowserProvider) {
815
+ const page = provider.getPage(contextId);
816
+ for (const key of state.unreleased) {
817
+ await page.keyboard.up(key);
818
+ }
819
+ } else if (provider instanceof WebdriverBrowserProvider) {
820
+ const keyboard2 = provider.browser.action("key");
821
+ for (const key of state.unreleased) {
822
+ keyboard2.up(key);
823
+ }
824
+ await keyboard2.perform();
825
+ } else {
826
+ throw new TypeError(`Provider "${context.provider.name}" does not support keyboard api`);
827
+ }
828
+ };
829
+ 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"]);
1295
830
  async function keyboardImplementation(pressed, provider, contextId, text, selectAll2, skipRelease) {
1296
831
  if (provider instanceof PlaywrightBrowserProvider) {
1297
832
  const page = provider.getPage(contextId);
@@ -1299,7 +834,9 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
1299
834
  for (const { releasePrevious, releaseSelf, repeat, keyDef } of actions) {
1300
835
  const key = keyDef.key;
1301
836
  if (pressed.has(key)) {
1302
- await page.keyboard.up(key);
837
+ if (VALID_KEYS.has(key)) {
838
+ await page.keyboard.up(key);
839
+ }
1303
840
  pressed.delete(key);
1304
841
  }
1305
842
  if (!releasePrevious) {
@@ -1308,10 +845,16 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
1308
845
  continue;
1309
846
  }
1310
847
  for (let i = 1; i <= repeat; i++) {
1311
- await page.keyboard.down(key);
848
+ if (VALID_KEYS.has(key)) {
849
+ await page.keyboard.down(key);
850
+ } else {
851
+ await page.keyboard.insertText(key);
852
+ }
1312
853
  }
1313
854
  if (releaseSelf) {
1314
- await page.keyboard.up(key);
855
+ if (VALID_KEYS.has(key)) {
856
+ await page.keyboard.up(key);
857
+ }
1315
858
  } else {
1316
859
  pressed.add(key);
1317
860
  }
@@ -1319,7 +862,9 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
1319
862
  }
1320
863
  if (!skipRelease && pressed.size) {
1321
864
  for (const key of pressed) {
1322
- await page.keyboard.up(key);
865
+ if (VALID_KEYS.has(key)) {
866
+ await page.keyboard.up(key);
867
+ }
1323
868
  }
1324
869
  }
1325
870
  } else if (provider instanceof WebdriverBrowserProvider) {
@@ -1355,1030 +900,1663 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
1355
900
  }
1356
901
  }
1357
902
  }
1358
- const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
1359
- await keyboard2.perform(allRelease ? false : skipRelease);
903
+ const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
904
+ await keyboard2.perform(allRelease ? false : skipRelease);
905
+ }
906
+ return {
907
+ pressed
908
+ };
909
+ }
910
+ function focusIframe() {
911
+ if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
912
+ window.focus();
913
+ }
914
+ }
915
+ function selectAll() {
916
+ const element = document.activeElement;
917
+ if (element && element.select) {
918
+ element.select();
919
+ }
920
+ }
921
+
922
+ const screenshot = async (context, name, options = {}) => {
923
+ if (!context.testPath) {
924
+ throw new Error(`Cannot take a screenshot without a test path`);
925
+ }
926
+ const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(
927
+ context.testPath,
928
+ name,
929
+ context.project.config
930
+ );
931
+ const savePath = normalize$1(path);
932
+ await mkdir(dirname(path), { recursive: true });
933
+ if (context.provider instanceof PlaywrightBrowserProvider) {
934
+ if (options.element) {
935
+ const { element: selector, ...config } = options;
936
+ const element = context.iframe.locator(`${selector}`);
937
+ const buffer2 = await element.screenshot({
938
+ timeout: 1e3,
939
+ ...config,
940
+ path: savePath
941
+ });
942
+ return returnResult(options, path, buffer2);
943
+ }
944
+ const buffer = await context.iframe.locator("body").screenshot({
945
+ ...options,
946
+ path: savePath
947
+ });
948
+ return returnResult(options, path, buffer);
949
+ }
950
+ if (context.provider instanceof WebdriverBrowserProvider) {
951
+ const page = context.provider.browser;
952
+ if (!options.element) {
953
+ const body = await page.$("body");
954
+ const buffer2 = await body.saveScreenshot(savePath);
955
+ return returnResult(options, path, buffer2);
956
+ }
957
+ const element = await page.$(`${options.element}`);
958
+ const buffer = await element.saveScreenshot(savePath);
959
+ return returnResult(options, path, buffer);
960
+ }
961
+ throw new Error(
962
+ `Provider "${context.provider.name}" does not support screenshots`
963
+ );
964
+ };
965
+ function resolveScreenshotPath(testPath, name, config) {
966
+ const dir = dirname(testPath);
967
+ const base = basename(testPath);
968
+ if (config.browser.screenshotDirectory) {
969
+ return resolve(
970
+ config.browser.screenshotDirectory,
971
+ relative(config.root, dir),
972
+ base,
973
+ name
974
+ );
975
+ }
976
+ return resolve(dir, "__screenshots__", base, name);
977
+ }
978
+ function returnResult(options, path, buffer) {
979
+ if (options.base64) {
980
+ return { path, base64: buffer.toString("base64") };
981
+ }
982
+ return path;
983
+ }
984
+
985
+ const selectOptions = async (context, selector, userValues, options = {}) => {
986
+ if (context.provider instanceof PlaywrightBrowserProvider) {
987
+ const value = userValues;
988
+ const { iframe } = context;
989
+ const selectElement = iframe.locator(selector);
990
+ const values = await Promise.all(value.map(async (v) => {
991
+ if (typeof v === "string") {
992
+ return v;
993
+ }
994
+ const elementHandler = await iframe.locator(v.element).elementHandle();
995
+ if (!elementHandler) {
996
+ throw new Error(`Element not found: ${v.element}`);
997
+ }
998
+ return elementHandler;
999
+ }));
1000
+ await selectElement.selectOption(values, {
1001
+ timeout: 1e3,
1002
+ ...options
1003
+ });
1004
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
1005
+ const values = userValues;
1006
+ if (!values.length) {
1007
+ return;
1008
+ }
1009
+ const browser = context.browser;
1010
+ if (values.length === 1 && "index" in values[0]) {
1011
+ const selectElement = browser.$(selector);
1012
+ await selectElement.selectByIndex(values[0].index);
1013
+ } else {
1014
+ throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
1015
+ }
1016
+ } else {
1017
+ throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
1018
+ }
1019
+ };
1020
+
1021
+ const tab = async (context, options = {}) => {
1022
+ const provider = context.provider;
1023
+ if (provider instanceof PlaywrightBrowserProvider) {
1024
+ const page = context.page;
1025
+ await page.keyboard.press(options.shift === true ? "Shift+Tab" : "Tab");
1026
+ return;
1027
+ }
1028
+ if (provider instanceof WebdriverBrowserProvider) {
1029
+ const { Key } = await import('webdriverio');
1030
+ const browser = context.browser;
1031
+ await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab]);
1032
+ return;
1033
+ }
1034
+ throw new Error(`Provider "${provider.name}" doesn't support tab command`);
1035
+ };
1036
+
1037
+ const type = async (context, selector, text, options = {}) => {
1038
+ const { skipClick = false, skipAutoClose = false } = options;
1039
+ const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
1040
+ if (context.provider instanceof PlaywrightBrowserProvider) {
1041
+ const { iframe } = context;
1042
+ const element = iframe.locator(selector);
1043
+ if (!skipClick) {
1044
+ await element.focus();
1045
+ }
1046
+ await keyboardImplementation(
1047
+ unreleased,
1048
+ context.provider,
1049
+ context.contextId,
1050
+ text,
1051
+ () => element.selectText(),
1052
+ skipAutoClose
1053
+ );
1054
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
1055
+ const browser = context.browser;
1056
+ const element = browser.$(selector);
1057
+ if (!skipClick && !await element.isFocused()) {
1058
+ await element.click();
1059
+ }
1060
+ await keyboardImplementation(
1061
+ unreleased,
1062
+ context.provider,
1063
+ context.contextId,
1064
+ text,
1065
+ () => browser.execute(() => {
1066
+ const element2 = document.activeElement;
1067
+ if (element2) {
1068
+ element2.select();
1069
+ }
1070
+ }),
1071
+ skipAutoClose
1072
+ );
1073
+ } else {
1074
+ throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
1075
+ }
1076
+ return {
1077
+ unreleased: Array.from(unreleased)
1078
+ };
1079
+ };
1080
+
1081
+ const upload = async (context, selector, files) => {
1082
+ const testPath = context.testPath;
1083
+ if (!testPath) {
1084
+ throw new Error(`Cannot upload files outside of a test`);
1085
+ }
1086
+ const testDir = dirname(testPath);
1087
+ if (context.provider instanceof PlaywrightBrowserProvider) {
1088
+ const { iframe } = context;
1089
+ const playwrightFiles = files.map((file) => {
1090
+ if (typeof file === "string") {
1091
+ return resolve(testDir, file);
1092
+ }
1093
+ return {
1094
+ name: file.name,
1095
+ mimeType: file.mimeType,
1096
+ buffer: Buffer.from(file.base64, "base64")
1097
+ };
1098
+ });
1099
+ await iframe.locator(selector).setInputFiles(playwrightFiles);
1100
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
1101
+ for (const file of files) {
1102
+ if (typeof file !== "string") {
1103
+ throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`);
1104
+ }
1105
+ }
1106
+ const element = context.browser.$(selector);
1107
+ for (const file of files) {
1108
+ const filepath = resolve(testDir, file);
1109
+ const remoteFilePath = await context.browser.uploadFile(filepath);
1110
+ await element.addValue(remoteFilePath);
1111
+ }
1112
+ } else {
1113
+ throw new TypeError(`Provider "${context.provider.name}" does not support uploading files via userEvent.upload`);
1114
+ }
1115
+ };
1116
+
1117
+ var builtinCommands = {
1118
+ readFile,
1119
+ removeFile,
1120
+ writeFile,
1121
+ __vitest_fileInfo: _fileInfo,
1122
+ __vitest_upload: upload,
1123
+ __vitest_click: click,
1124
+ __vitest_dblClick: dblClick,
1125
+ __vitest_tripleClick: tripleClick,
1126
+ __vitest_screenshot: screenshot,
1127
+ __vitest_type: type,
1128
+ __vitest_clear: clear,
1129
+ __vitest_fill: fill,
1130
+ __vitest_tab: tab,
1131
+ __vitest_keyboard: keyboard,
1132
+ __vitest_selectOptions: selectOptions,
1133
+ __vitest_dragAndDrop: dragAndDrop,
1134
+ __vitest_hover: hover,
1135
+ __vitest_cleanup: keyboardCleanup
1136
+ };
1137
+
1138
+ const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
1139
+ const ID_CONTEXT = "@vitest/browser/context";
1140
+ const __dirname = dirname(fileURLToPath(import.meta.url));
1141
+ function BrowserContext(server) {
1142
+ const project = server.project;
1143
+ project.config.browser.commands ??= {};
1144
+ for (const [name, command] of Object.entries(builtinCommands)) {
1145
+ project.config.browser.commands[name] ??= command;
1146
+ }
1147
+ for (const command in project.config.browser.commands) {
1148
+ if (!/^[a-z_$][\w$]*$/i.test(command)) {
1149
+ throw new Error(
1150
+ `Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`
1151
+ );
1152
+ }
1360
1153
  }
1361
1154
  return {
1362
- pressed
1155
+ name: "vitest:browser:virtual-module:context",
1156
+ enforce: "pre",
1157
+ resolveId(id) {
1158
+ if (id === ID_CONTEXT) {
1159
+ return VIRTUAL_ID_CONTEXT;
1160
+ }
1161
+ },
1162
+ load(id) {
1163
+ if (id === VIRTUAL_ID_CONTEXT) {
1164
+ return generateContextFile.call(this, server);
1165
+ }
1166
+ }
1363
1167
  };
1364
1168
  }
1365
- function focusIframe() {
1366
- if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
1367
- window.focus();
1368
- }
1169
+ async function generateContextFile(server) {
1170
+ const commands = Object.keys(server.project.config.browser.commands ?? {});
1171
+ const filepathCode = "__vitest_worker__.filepath || __vitest_worker__.current?.file?.filepath || undefined";
1172
+ const provider = server.provider;
1173
+ const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
1174
+ return ` ["${command}"]: (...args) => rpc().triggerCommand(contextId, "${command}", filepath(), args),`;
1175
+ }).join("\n");
1176
+ const userEventNonProviderImport = await getUserEventImport(
1177
+ provider,
1178
+ this.resolve.bind(this)
1179
+ );
1180
+ const distContextPath = slash(`/@fs/${resolve(__dirname, "context.js")}`);
1181
+ return `
1182
+ import { page, createUserEvent, cdp } from '${distContextPath}'
1183
+ ${userEventNonProviderImport}
1184
+ const filepath = () => ${filepathCode}
1185
+ const rpc = () => __vitest_worker__.rpc
1186
+ const contextId = __vitest_browser_runner__.contextId
1187
+
1188
+ export const server = {
1189
+ platform: ${JSON.stringify(process.platform)},
1190
+ version: ${JSON.stringify(process.version)},
1191
+ provider: ${JSON.stringify(provider.name)},
1192
+ browser: ${JSON.stringify(server.project.config.browser.name)},
1193
+ commands: {
1194
+ ${commandsCode}
1195
+ },
1196
+ config: __vitest_browser_runner__.config,
1369
1197
  }
1370
- function selectAll() {
1371
- const element = document.activeElement;
1372
- if (element && element.select) {
1373
- element.select();
1198
+ export const commands = server.commands
1199
+ export const userEvent = createUserEvent(_userEventSetup)
1200
+ export { page, cdp }
1201
+ `;
1202
+ }
1203
+ async function getUserEventImport(provider, resolve2) {
1204
+ if (provider.name !== "preview") {
1205
+ return "const _userEventSetup = undefined";
1206
+ }
1207
+ const resolved = await resolve2("@testing-library/user-event", __dirname);
1208
+ if (!resolved) {
1209
+ throw new Error(`Failed to resolve user-event package from ${__dirname}`);
1374
1210
  }
1211
+ return `import { userEvent as __vitest_user_event__ } from '${slash(`/@fs/${resolved.id}`)}'
1212
+ const _userEventSetup = __vitest_user_event__
1213
+ `;
1375
1214
  }
1376
1215
 
1377
- const type = async (context, selector, text, options = {}) => {
1378
- const { skipClick = false, skipAutoClose = false } = options;
1379
- const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
1380
- if (context.provider instanceof PlaywrightBrowserProvider) {
1381
- const { iframe } = context;
1382
- const element = iframe.locator(selector);
1383
- if (!skipClick) {
1384
- await element.focus();
1385
- }
1386
- await keyboardImplementation(
1387
- unreleased,
1388
- context.provider,
1389
- context.contextId,
1390
- text,
1391
- () => element.selectText(),
1392
- skipAutoClose
1216
+ function replacer(code, values) {
1217
+ return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
1218
+ }
1219
+ const builtinProviders = ["webdriverio", "playwright", "preview"];
1220
+ async function getBrowserProvider(options, project) {
1221
+ if (options.provider == null || builtinProviders.includes(options.provider)) {
1222
+ const providers = await import('./providers.js');
1223
+ const provider = options.provider || "preview";
1224
+ return providers[provider];
1225
+ }
1226
+ let customProviderModule;
1227
+ try {
1228
+ customProviderModule = await project.runner.executeId(
1229
+ options.provider
1393
1230
  );
1394
- } else if (context.provider instanceof WebdriverBrowserProvider) {
1395
- const browser = context.browser;
1396
- const element = browser.$(selector);
1397
- if (!skipClick && !await element.isFocused()) {
1398
- await element.click();
1399
- }
1400
- await keyboardImplementation(
1401
- unreleased,
1402
- context.provider,
1403
- context.contextId,
1404
- text,
1405
- () => browser.execute(() => {
1406
- const element2 = document.activeElement;
1407
- if (element2) {
1408
- element2.select();
1409
- }
1410
- }),
1411
- skipAutoClose
1231
+ } catch (error) {
1232
+ throw new Error(
1233
+ `Failed to load custom BrowserProvider from ${options.provider}`,
1234
+ { cause: error }
1412
1235
  );
1413
- } else {
1414
- throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
1415
1236
  }
1416
- return {
1417
- unreleased: Array.from(unreleased)
1418
- };
1419
- };
1420
-
1421
- const clear = async (context, selector) => {
1422
- if (context.provider instanceof PlaywrightBrowserProvider) {
1423
- const { iframe } = context;
1424
- const element = iframe.locator(selector);
1425
- await element.clear({
1426
- timeout: 1e3
1427
- });
1428
- } else if (context.provider instanceof WebdriverBrowserProvider) {
1429
- const browser = context.browser;
1430
- const element = await browser.$(selector);
1431
- await element.clearValue();
1432
- } else {
1433
- throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
1237
+ if (customProviderModule.default == null) {
1238
+ throw new Error(
1239
+ `Custom BrowserProvider loaded from ${options.provider} was not the default export`
1240
+ );
1434
1241
  }
1435
- };
1242
+ return customProviderModule.default;
1243
+ }
1436
1244
 
1437
- const fill = async (context, selector, text, options = {}) => {
1438
- if (context.provider instanceof PlaywrightBrowserProvider) {
1439
- const { iframe } = context;
1440
- const element = iframe.locator(selector);
1441
- await element.fill(text, { timeout: 1e3, ...options });
1442
- } else if (context.provider instanceof WebdriverBrowserProvider) {
1443
- const browser = context.browser;
1444
- await browser.$(selector).setValue(text);
1445
- } else {
1446
- throw new TypeError(`Provider "${context.provider.name}" does not support clearing elements`);
1245
+ async function resolveOrchestrator(server, url, res) {
1246
+ const project = server.project;
1247
+ let contextId = url.searchParams.get("contextId");
1248
+ if (!contextId) {
1249
+ const contexts = [...server.state.orchestrators.keys()];
1250
+ contextId = contexts[contexts.length - 1] ?? "none";
1447
1251
  }
1448
- };
1449
-
1450
- const selectOptions = async (context, selector, userValues, options = {}) => {
1451
- if (context.provider instanceof PlaywrightBrowserProvider) {
1452
- const value = userValues;
1453
- const { iframe } = context;
1454
- const selectElement = iframe.locator(selector);
1455
- const values = await Promise.all(value.map(async (v) => {
1456
- if (typeof v === "string") {
1457
- return v;
1458
- }
1459
- const elementHandler = await iframe.locator(v.element).elementHandle();
1460
- if (!elementHandler) {
1461
- throw new Error(`Element not found: ${v.element}`);
1252
+ const files = server.state.getContext(contextId)?.files ?? [];
1253
+ const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1254
+ const injector = replacer(injectorJs, {
1255
+ __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1256
+ __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1257
+ __VITEST_VITE_CONFIG__: JSON.stringify({
1258
+ root: server.vite.config.root
1259
+ }),
1260
+ __VITEST_FILES__: JSON.stringify(files),
1261
+ __VITEST_TYPE__: '"orchestrator"',
1262
+ __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1263
+ __VITEST_TESTER_ID__: '"none"',
1264
+ __VITEST_PROVIDED_CONTEXT__: "{}"
1265
+ });
1266
+ res.removeHeader("Content-Security-Policy");
1267
+ if (!server.orchestratorScripts) {
1268
+ server.orchestratorScripts = (await server.formatScripts(
1269
+ project.config.browser.orchestratorScripts
1270
+ )).map((script) => {
1271
+ let html = "<script ";
1272
+ for (const attr in script.attrs || {}) {
1273
+ html += `${attr}="${script.attrs[attr]}" `;
1462
1274
  }
1463
- return elementHandler;
1464
- }));
1465
- await selectElement.selectOption(values, {
1466
- timeout: 1e3,
1467
- ...options
1468
- });
1469
- } else if (context.provider instanceof WebdriverBrowserProvider) {
1470
- const values = userValues;
1471
- if (!values.length) {
1472
- return;
1473
- }
1474
- const browser = context.browser;
1475
- if (values.length === 1 && "index" in values[0]) {
1476
- const selectElement = browser.$(selector);
1477
- await selectElement.selectByIndex(values[0].index);
1478
- } else {
1479
- throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
1275
+ html += `>${script.children}<\/script>`;
1276
+ return html;
1277
+ }).join("\n");
1278
+ }
1279
+ let baseHtml = typeof server.orchestratorHtml === "string" ? server.orchestratorHtml : await server.orchestratorHtml;
1280
+ if (project.config.browser.ui) {
1281
+ const manifestContent = server.manifest instanceof Promise ? await server.manifest : server.manifest;
1282
+ const jsEntry = manifestContent["orchestrator.html"].file;
1283
+ const base = server.vite.config.base || "/";
1284
+ baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
1285
+ "<!-- !LOAD_METADATA! -->",
1286
+ [
1287
+ "{__VITEST_INJECTOR__}",
1288
+ "{__VITEST_ERROR_CATCHER__}",
1289
+ "{__VITEST_SCRIPTS__}",
1290
+ `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
1291
+ ].join("\n")
1292
+ );
1293
+ }
1294
+ return replacer(baseHtml, {
1295
+ __VITEST_FAVICON__: server.faviconUrl,
1296
+ __VITEST_TITLE__: "Vitest Browser Runner",
1297
+ __VITEST_SCRIPTS__: server.orchestratorScripts,
1298
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
1299
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
1300
+ __VITEST_CONTEXT_ID__: JSON.stringify(contextId)
1301
+ });
1302
+ }
1303
+
1304
+ /// <reference types="../types/index.d.ts" />
1305
+
1306
+ // (c) 2020-present Andrea Giammarchi
1307
+
1308
+ const {parse: $parse, stringify: $stringify} = JSON;
1309
+ const {keys} = Object;
1310
+
1311
+ const Primitive = String; // it could be Number
1312
+ const primitive = 'string'; // it could be 'number'
1313
+
1314
+ const ignore = {};
1315
+ const object = 'object';
1316
+
1317
+ const noop = (_, value) => value;
1318
+
1319
+ const primitives = value => (
1320
+ value instanceof Primitive ? Primitive(value) : value
1321
+ );
1322
+
1323
+ const Primitives = (_, value) => (
1324
+ typeof value === primitive ? new Primitive(value) : value
1325
+ );
1326
+
1327
+ const revive = (input, parsed, output, $) => {
1328
+ const lazy = [];
1329
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
1330
+ const k = ke[y];
1331
+ const value = output[k];
1332
+ if (value instanceof Primitive) {
1333
+ const tmp = input[value];
1334
+ if (typeof tmp === object && !parsed.has(tmp)) {
1335
+ parsed.add(tmp);
1336
+ output[k] = ignore;
1337
+ lazy.push({k, a: [input, parsed, tmp, $]});
1338
+ }
1339
+ else
1340
+ output[k] = $.call(output, k, tmp);
1480
1341
  }
1481
- } else {
1482
- throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
1342
+ else if (output[k] !== ignore)
1343
+ output[k] = $.call(output, k, value);
1344
+ }
1345
+ for (let {length} = lazy, i = 0; i < length; i++) {
1346
+ const {k, a} = lazy[i];
1347
+ output[k] = $.call(output, k, revive.apply(null, a));
1483
1348
  }
1349
+ return output;
1484
1350
  };
1485
1351
 
1486
- const tab = async (context, options = {}) => {
1487
- const provider = context.provider;
1488
- if (provider instanceof PlaywrightBrowserProvider) {
1489
- const page = context.page;
1490
- await page.keyboard.press(options.shift === true ? "Shift+Tab" : "Tab");
1491
- return;
1352
+ const set = (known, input, value) => {
1353
+ const index = Primitive(input.push(value) - 1);
1354
+ known.set(value, index);
1355
+ return index;
1356
+ };
1357
+
1358
+ /**
1359
+ * Converts a specialized flatted string into a JS value.
1360
+ * @param {string} text
1361
+ * @param {(this: any, key: string, value: any) => any} [reviver]
1362
+ * @returns {any}
1363
+ */
1364
+ const parse = (text, reviver) => {
1365
+ const input = $parse(text, Primitives).map(primitives);
1366
+ const value = input[0];
1367
+ const $ = reviver || noop;
1368
+ const tmp = typeof value === object && value ?
1369
+ revive(input, new Set, value, $) :
1370
+ value;
1371
+ return $.call({'': tmp}, '', tmp);
1372
+ };
1373
+
1374
+ /**
1375
+ * Converts a JS value into a specialized flatted string.
1376
+ * @param {any} value
1377
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
1378
+ * @param {string | number | undefined} [space]
1379
+ * @returns {string}
1380
+ */
1381
+ const stringify = (value, replacer, space) => {
1382
+ const $ = replacer && typeof replacer === object ?
1383
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
1384
+ (replacer || noop);
1385
+ const known = new Map;
1386
+ const input = [];
1387
+ const output = [];
1388
+ let i = +set(known, input, $.call({'': value}, '', value));
1389
+ let firstRun = !i;
1390
+ while (i < input.length) {
1391
+ firstRun = true;
1392
+ output[i] = $stringify(input[i++], replace, space);
1492
1393
  }
1493
- if (provider instanceof WebdriverBrowserProvider) {
1494
- const { Key } = await import('webdriverio');
1495
- const browser = context.browser;
1496
- await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab]);
1497
- return;
1394
+ return '[' + output.join(',') + ']';
1395
+ function replace(key, value) {
1396
+ if (firstRun) {
1397
+ firstRun = !firstRun;
1398
+ return value;
1399
+ }
1400
+ const after = $.call(this, key, value);
1401
+ switch (typeof after) {
1402
+ case object:
1403
+ if (after === null) return after;
1404
+ case primitive:
1405
+ return known.get(after) || set(known, input, after);
1406
+ }
1407
+ return after;
1498
1408
  }
1499
- throw new Error(`Provider "${provider.name}" doesn't support tab command`);
1500
1409
  };
1501
1410
 
1502
- const dragAndDrop = async (context, source, target, options_) => {
1503
- if (context.provider instanceof PlaywrightBrowserProvider) {
1504
- const frame = await context.frame();
1505
- await frame.dragAndDrop(
1506
- source,
1507
- target,
1508
- {
1509
- timeout: 1e3,
1510
- ...options_
1511
- }
1411
+ async function resolveTester(server, url, res, next) {
1412
+ const csp = res.getHeader("Content-Security-Policy");
1413
+ if (typeof csp === "string") {
1414
+ res.setHeader(
1415
+ "Content-Security-Policy",
1416
+ csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *")
1512
1417
  );
1513
- } else if (context.provider instanceof WebdriverBrowserProvider) {
1514
- const $source = context.browser.$(source);
1515
- const $target = context.browser.$(target);
1516
- const options = options_ || {};
1517
- const duration = options.duration ?? 10;
1518
- 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();
1519
- } else {
1520
- throw new TypeError(`Provider "${context.provider.name}" does not support dragging elements`);
1521
1418
  }
1522
- };
1523
-
1524
- const hover = async (context, selector, options = {}) => {
1525
- if (context.provider instanceof PlaywrightBrowserProvider) {
1526
- await context.iframe.locator(selector).hover({
1527
- timeout: 1e3,
1528
- ...options
1419
+ const { contextId, testFile } = server.resolveTesterUrl(url.pathname);
1420
+ const project = server.project;
1421
+ const state = server.state;
1422
+ const { testFiles } = await project.globTestFiles();
1423
+ const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
1424
+ const iframeId = JSON.stringify(testFile);
1425
+ const context = state.getContext(contextId);
1426
+ const files = context?.files ?? [];
1427
+ const method = context?.method ?? "run";
1428
+ const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1429
+ const injector = replacer(injectorJs, {
1430
+ __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1431
+ __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1432
+ __VITEST_FILES__: JSON.stringify(files),
1433
+ __VITEST_VITE_CONFIG__: JSON.stringify({
1434
+ root: server.vite.config.root
1435
+ }),
1436
+ __VITEST_TYPE__: '"tester"',
1437
+ __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1438
+ __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
1439
+ __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
1440
+ });
1441
+ const testerHtml = typeof server.testerHtml === "string" ? server.testerHtml : await server.testerHtml;
1442
+ try {
1443
+ const url2 = join("/@fs/", server.testerFilepath);
1444
+ const indexhtml = await server.vite.transformIndexHtml(url2, testerHtml);
1445
+ return replacer(indexhtml, {
1446
+ __VITEST_FAVICON__: server.faviconUrl,
1447
+ __VITEST_INJECTOR__: injector,
1448
+ __VITEST_APPEND__: `
1449
+ __vitest_browser_runner__.runningFiles = ${tests}
1450
+ __vitest_browser_runner__.iframeId = ${iframeId}
1451
+ __vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
1452
+ document.querySelector('script[data-vitest-append]').remove()
1453
+ `
1529
1454
  });
1530
- } else if (context.provider instanceof WebdriverBrowserProvider) {
1531
- const browser = context.browser;
1532
- await browser.$(selector).moveTo(options);
1533
- } else {
1534
- throw new TypeError(`Provider "${context.provider.name}" does not support hover`);
1455
+ } catch (err) {
1456
+ context?.reject(err);
1457
+ next(err);
1535
1458
  }
1536
- };
1459
+ }
1537
1460
 
1538
- const upload = async (context, selector, files) => {
1539
- const testPath = context.testPath;
1540
- if (!testPath) {
1541
- throw new Error(`Cannot upload files outside of a test`);
1542
- }
1543
- const testDir = dirname(testPath);
1544
- if (context.provider instanceof PlaywrightBrowserProvider) {
1545
- const { iframe } = context;
1546
- const playwrightFiles = files.map((file) => {
1547
- if (typeof file === "string") {
1548
- return resolve(testDir, file);
1549
- }
1550
- return {
1551
- name: file.name,
1552
- mimeType: file.mimeType,
1553
- buffer: Buffer.from(file.base64, "base64")
1554
- };
1461
+ const versionRegexp = /(?:\?|&)v=\w{8}/;
1462
+ var BrowserPlugin = (browserServer, base = "/") => {
1463
+ const project = browserServer.project;
1464
+ function isPackageExists(pkg, root) {
1465
+ return browserServer.project.ctx.packageInstaller.isPackageExists?.(pkg, {
1466
+ paths: [root]
1555
1467
  });
1556
- await iframe.locator(selector).setInputFiles(playwrightFiles);
1557
- } else if (context.provider instanceof WebdriverBrowserProvider) {
1558
- for (const file of files) {
1559
- if (typeof file !== "string") {
1560
- throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`);
1468
+ }
1469
+ return [
1470
+ {
1471
+ enforce: "pre",
1472
+ name: "vitest:browser",
1473
+ async configureServer(server) {
1474
+ browserServer.setServer(server);
1475
+ server.middlewares.use(function vitestHeaders(_req, res, next) {
1476
+ const headers = server.config.server.headers;
1477
+ if (headers) {
1478
+ for (const name in headers) {
1479
+ res.setHeader(name, headers[name]);
1480
+ }
1481
+ }
1482
+ next();
1483
+ });
1484
+ server.middlewares.use(async function vitestBrowserMode(req, res, next) {
1485
+ if (!req.url || !browserServer.provider) {
1486
+ return next();
1487
+ }
1488
+ const url = new URL(req.url, "http://localhost");
1489
+ if (!url.pathname.startsWith(browserServer.prefixTesterUrl) && url.pathname !== base) {
1490
+ return next();
1491
+ }
1492
+ res.setHeader(
1493
+ "Cache-Control",
1494
+ "no-cache, max-age=0, must-revalidate"
1495
+ );
1496
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1497
+ res.removeHeader("X-Frame-Options");
1498
+ if (url.pathname === base) {
1499
+ const html2 = await resolveOrchestrator(browserServer, url, res);
1500
+ res.write(html2, "utf-8");
1501
+ res.end();
1502
+ return;
1503
+ }
1504
+ const html = await resolveTester(browserServer, url, res, next);
1505
+ if (html) {
1506
+ res.write(html, "utf-8");
1507
+ res.end();
1508
+ }
1509
+ });
1510
+ server.middlewares.use(
1511
+ `${base}favicon.svg`,
1512
+ (_, res) => {
1513
+ const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
1514
+ res.write(content, "utf-8");
1515
+ res.end();
1516
+ }
1517
+ );
1518
+ const coverageFolder = resolveCoverageFolder(project);
1519
+ const coveragePath = coverageFolder ? coverageFolder[1] : void 0;
1520
+ if (coveragePath && base === coveragePath) {
1521
+ throw new Error(
1522
+ `The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`
1523
+ );
1524
+ }
1525
+ if (coverageFolder) {
1526
+ server.middlewares.use(
1527
+ coveragePath,
1528
+ sirv(coverageFolder[0], {
1529
+ single: true,
1530
+ dev: true,
1531
+ setHeaders: (res) => {
1532
+ res.setHeader(
1533
+ "Cache-Control",
1534
+ "public,max-age=0,must-revalidate"
1535
+ );
1536
+ }
1537
+ })
1538
+ );
1539
+ }
1540
+ const screenshotFailures = project.config.browser.ui && project.config.browser.screenshotFailures;
1541
+ if (screenshotFailures) {
1542
+ server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
1543
+ if (!req.url || !browserServer.provider) {
1544
+ res.statusCode = 404;
1545
+ res.end();
1546
+ return;
1547
+ }
1548
+ const url = new URL(req.url, "http://localhost");
1549
+ const file = url.searchParams.get("file");
1550
+ if (!file) {
1551
+ res.statusCode = 404;
1552
+ res.end();
1553
+ return;
1554
+ }
1555
+ let stat;
1556
+ try {
1557
+ stat = lstatSync(file);
1558
+ } catch {
1559
+ }
1560
+ if (!stat?.isFile()) {
1561
+ res.statusCode = 404;
1562
+ res.end();
1563
+ return;
1564
+ }
1565
+ const ext = extname(file);
1566
+ const buffer = readFileSync(file);
1567
+ res.setHeader(
1568
+ "Cache-Control",
1569
+ "public,max-age=0,must-revalidate"
1570
+ );
1571
+ res.setHeader("Content-Length", buffer.length);
1572
+ res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
1573
+ res.end(buffer);
1574
+ });
1575
+ }
1576
+ server.middlewares.use((req, res, next) => {
1577
+ if (req.url && versionRegexp.test(req.url) && !req.url.includes("chunk-")) {
1578
+ res.setHeader("Cache-Control", "no-cache");
1579
+ const setHeader = res.setHeader.bind(res);
1580
+ res.setHeader = function(name, value) {
1581
+ if (name === "Cache-Control") {
1582
+ return res;
1583
+ }
1584
+ return setHeader(name, value);
1585
+ };
1586
+ }
1587
+ next();
1588
+ });
1561
1589
  }
1562
- }
1563
- const element = context.browser.$(selector);
1564
- for (const file of files) {
1565
- const filepath = resolve(testDir, file);
1566
- const remoteFilePath = await context.browser.uploadFile(filepath);
1567
- await element.addValue(remoteFilePath);
1568
- }
1569
- } else {
1570
- throw new TypeError(`Provider "${context.provider.name}" does not support uploading files via userEvent.upload`);
1571
- }
1572
- };
1573
-
1574
- 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"] };
1575
- Object.freeze(types);
1576
-
1577
- var __classPrivateFieldGet = (null && null.__classPrivateFieldGet) || function (receiver, state, kind, f) {
1578
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
1579
- 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");
1580
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
1581
- };
1582
- var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
1583
- class Mime {
1584
- constructor(...args) {
1585
- _Mime_extensionToType.set(this, new Map());
1586
- _Mime_typeToExtension.set(this, new Map());
1587
- _Mime_typeToExtensions.set(this, new Map());
1588
- for (const arg of args) {
1589
- this.define(arg);
1590
+ },
1591
+ {
1592
+ name: "vitest:browser:tests",
1593
+ enforce: "pre",
1594
+ async config() {
1595
+ const { testFiles: allTestFiles } = await project.globTestFiles();
1596
+ const browserTestFiles = allTestFiles.filter(
1597
+ (file) => getFilePoolName(project, file) === "browser"
1598
+ );
1599
+ const setupFiles = toArray(project.config.setupFiles);
1600
+ const define = {};
1601
+ for (const env in project.config.env || {}) {
1602
+ const stringValue = JSON.stringify(project.config.env[env]);
1603
+ define[`import.meta.env.${env}`] = stringValue;
1590
1604
  }
1591
- }
1592
- define(typeMap, force = false) {
1593
- for (let [type, extensions] of Object.entries(typeMap)) {
1594
- type = type.toLowerCase();
1595
- extensions = extensions.map((ext) => ext.toLowerCase());
1596
- if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) {
1597
- __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, new Set());
1605
+ const entries = [
1606
+ ...browserTestFiles,
1607
+ ...setupFiles,
1608
+ resolve(distDir, "index.js"),
1609
+ resolve(distDir, "browser.js"),
1610
+ resolve(distDir, "runners.js"),
1611
+ resolve(distDir, "utils.js"),
1612
+ ...project.config.snapshotSerializers || []
1613
+ ];
1614
+ const exclude = [
1615
+ "vitest",
1616
+ "vitest/utils",
1617
+ "vitest/browser",
1618
+ "vitest/runners",
1619
+ "@vitest/browser",
1620
+ "@vitest/browser/client",
1621
+ "@vitest/utils",
1622
+ "@vitest/utils/source-map",
1623
+ "@vitest/runner",
1624
+ "@vitest/spy",
1625
+ "@vitest/utils/error",
1626
+ "@vitest/snapshot",
1627
+ "@vitest/expect",
1628
+ "std-env",
1629
+ "tinybench",
1630
+ "tinyspy",
1631
+ "tinyrainbow",
1632
+ "pathe",
1633
+ "msw",
1634
+ "msw/browser"
1635
+ ];
1636
+ if (project.config.diff) {
1637
+ entries.push(project.config.diff);
1638
+ }
1639
+ if (project.ctx.coverageProvider) {
1640
+ const coverage = project.ctx.config.coverage;
1641
+ const provider = coverage.provider;
1642
+ if (provider === "v8") {
1643
+ const path = tryResolve("@vitest/coverage-v8", [project.config.root]);
1644
+ if (path) {
1645
+ entries.push(path);
1646
+ exclude.push("@vitest/coverage-v8/browser");
1598
1647
  }
1599
- const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
1600
- let first = true;
1601
- for (let extension of extensions) {
1602
- const starred = extension.startsWith('*');
1603
- extension = starred ? extension.slice(1) : extension;
1604
- allExtensions?.add(extension);
1605
- if (first) {
1606
- __classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
1607
- }
1608
- first = false;
1609
- if (starred)
1610
- continue;
1611
- const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
1612
- if (currentType && currentType != type && !force) {
1613
- throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
1614
- }
1615
- __classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
1648
+ } else if (provider === "istanbul") {
1649
+ const path = tryResolve("@vitest/coverage-istanbul", [project.config.root]);
1650
+ if (path) {
1651
+ entries.push(path);
1652
+ exclude.push("@vitest/coverage-istanbul");
1616
1653
  }
1654
+ } else if (provider === "custom" && coverage.customProviderModule) {
1655
+ entries.push(coverage.customProviderModule);
1656
+ }
1617
1657
  }
1618
- return this;
1619
- }
1620
- getType(path) {
1621
- if (typeof path !== 'string')
1622
- return null;
1623
- const last = path.replace(/^.*[/\\]/, '').toLowerCase();
1624
- const ext = last.replace(/^.*\./, '').toLowerCase();
1625
- const hasPath = last.length < path.length;
1626
- const hasDot = ext.length < last.length - 1;
1627
- if (!hasDot && hasPath)
1628
- return null;
1629
- return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
1630
- }
1631
- getExtension(type) {
1632
- if (typeof type !== 'string')
1633
- return null;
1634
- type = type?.split?.(';')[0];
1635
- return ((type && __classPrivateFieldGet(this, _Mime_typeToExtension, "f").get(type.trim().toLowerCase())) ?? null);
1636
- }
1637
- getAllExtensions(type) {
1638
- if (typeof type !== 'string')
1639
- return null;
1640
- return __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type.toLowerCase()) ?? null;
1641
- }
1642
- _freeze() {
1643
- this.define = () => {
1644
- throw new Error('define() not allowed for built-in Mime objects. See https://github.com/broofa/mime/blob/main/README.md#custom-mime-instances');
1658
+ const include = [
1659
+ "vitest > expect-type",
1660
+ "vitest > @vitest/snapshot > magic-string",
1661
+ "vitest > chai",
1662
+ "vitest > chai > loupe",
1663
+ "vitest > @vitest/utils > loupe",
1664
+ "@vitest/browser > @testing-library/user-event",
1665
+ "@vitest/browser > @testing-library/dom"
1666
+ ];
1667
+ const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root;
1668
+ const svelte = isPackageExists("vitest-browser-svelte", fileRoot);
1669
+ if (svelte) {
1670
+ exclude.push("vitest-browser-svelte");
1671
+ }
1672
+ const vueTestUtils = isPackageExists("@vue/test-utils", fileRoot);
1673
+ if (vueTestUtils) {
1674
+ include.push("@vue/test-utils");
1675
+ }
1676
+ return {
1677
+ define,
1678
+ resolve: {
1679
+ dedupe: ["vitest"]
1680
+ },
1681
+ optimizeDeps: {
1682
+ entries,
1683
+ exclude,
1684
+ include
1685
+ }
1686
+ };
1687
+ },
1688
+ async resolveId(id) {
1689
+ if (!/\?browserv=\w+$/.test(id)) {
1690
+ return;
1691
+ }
1692
+ let useId = id.slice(0, id.lastIndexOf("?"));
1693
+ if (useId.startsWith("/@fs/")) {
1694
+ useId = useId.slice(5);
1695
+ }
1696
+ if (/^\w:/.test(useId)) {
1697
+ useId = useId.replace(/\\/g, "/");
1698
+ }
1699
+ return useId;
1700
+ }
1701
+ },
1702
+ {
1703
+ name: "vitest:browser:resolve-virtual",
1704
+ async resolveId(rawId) {
1705
+ if (rawId === "/mockServiceWorker.js") {
1706
+ return this.resolve("msw/mockServiceWorker.js", distRoot, {
1707
+ skipSelf: true
1708
+ });
1709
+ }
1710
+ }
1711
+ },
1712
+ {
1713
+ name: "vitest:browser:assets",
1714
+ configureServer(server) {
1715
+ server.middlewares.use(
1716
+ "/__vitest__",
1717
+ sirv(resolve(distRoot, "client/__vitest__"))
1718
+ );
1719
+ },
1720
+ resolveId(id) {
1721
+ if (id.startsWith("/__vitest_browser__/")) {
1722
+ return resolve(distRoot, "client", id.slice(1));
1723
+ }
1724
+ },
1725
+ transform(code, id) {
1726
+ if (id.includes(browserServer.vite.config.cacheDir) && id.includes("loupe.js")) {
1727
+ const utilRequire = "nodeUtil = require_util();";
1728
+ return code.replace(utilRequire, " ".repeat(utilRequire.length));
1729
+ }
1730
+ }
1731
+ },
1732
+ BrowserContext(browserServer),
1733
+ dynamicImportPlugin({
1734
+ globalThisAccessor: '"__vitest_browser_runner__"',
1735
+ filter(id) {
1736
+ if (id.includes(distRoot)) {
1737
+ return false;
1738
+ }
1739
+ return true;
1740
+ }
1741
+ }),
1742
+ {
1743
+ name: "vitest:browser:config",
1744
+ enforce: "post",
1745
+ async config(viteConfig) {
1746
+ // Enables using ignore hint for coverage providers with @preserve keyword
1747
+ if (viteConfig.esbuild !== false) {
1748
+ viteConfig.esbuild ||= {};
1749
+ viteConfig.esbuild.legalComments = "inline";
1750
+ }
1751
+ const defaultPort = project.ctx._browserLastPort++;
1752
+ const api = resolveApiServerConfig(
1753
+ viteConfig.test?.browser || {},
1754
+ defaultPort
1755
+ );
1756
+ viteConfig.server = {
1757
+ ...viteConfig.server,
1758
+ port: defaultPort,
1759
+ ...api,
1760
+ middlewareMode: false,
1761
+ open: false
1645
1762
  };
1646
- Object.freeze(this);
1647
- for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
1648
- Object.freeze(extensions);
1763
+ viteConfig.server.fs ??= {};
1764
+ viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
1765
+ viteConfig.server.fs.allow.push(
1766
+ ...resolveFsAllow(
1767
+ project.ctx.config.root,
1768
+ project.ctx.server.config.configFile
1769
+ ),
1770
+ distRoot
1771
+ );
1772
+ return {
1773
+ resolve: {
1774
+ alias: viteConfig.test?.alias
1775
+ }
1776
+ };
1777
+ }
1778
+ },
1779
+ {
1780
+ name: "vitest:browser:in-source-tests",
1781
+ transform(code, id) {
1782
+ if (!project.isTestFile(id) || !code.includes("import.meta.vitest")) {
1783
+ return;
1649
1784
  }
1650
- return this;
1651
- }
1652
- _getTestState() {
1785
+ const s = new MagicString(code, { filename: cleanUrl(id) });
1786
+ s.prepend(
1787
+ `import.meta.vitest = __vitest_index__;
1788
+ `
1789
+ );
1653
1790
  return {
1654
- types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
1655
- extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f"),
1791
+ code: s.toString(),
1792
+ map: s.generateMap({ hires: true })
1656
1793
  };
1657
- }
1658
- }
1659
- _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
1660
-
1661
- var mime = new Mime(types)._freeze();
1662
-
1663
- function assertFileAccess(path, project) {
1664
- if (!isFileServingAllowed(path, project.server) && !isFileServingAllowed(path, project.ctx.server)) {
1665
- throw new Error(
1666
- `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
1667
- );
1668
- }
1669
- }
1670
- const readFile = async ({ project, testPath = process.cwd() }, path, options = {}) => {
1671
- const filepath = resolve$1(dirname$1(testPath), path);
1672
- assertFileAccess(filepath, project);
1673
- if (typeof options === "object" && !options.encoding) {
1674
- options.encoding = "utf-8";
1675
- }
1676
- return promises.readFile(filepath, options);
1677
- };
1678
- const writeFile = async ({ project, testPath = process.cwd() }, path, data, options) => {
1679
- const filepath = resolve$1(dirname$1(testPath), path);
1680
- assertFileAccess(filepath, project);
1681
- const dir = dirname$1(filepath);
1682
- if (!fs.existsSync(dir)) {
1683
- await promises.mkdir(dir, { recursive: true });
1684
- }
1685
- await promises.writeFile(filepath, data, options);
1686
- };
1687
- const removeFile = async ({ project, testPath = process.cwd() }, path) => {
1688
- const filepath = resolve$1(dirname$1(testPath), path);
1689
- assertFileAccess(filepath, project);
1690
- await promises.rm(filepath);
1691
- };
1692
- const _fileInfo = async ({ project, testPath = process.cwd() }, path, encoding) => {
1693
- const filepath = resolve$1(dirname$1(testPath), path);
1694
- assertFileAccess(filepath, project);
1695
- const content = await promises.readFile(filepath, encoding || "base64");
1696
- return {
1697
- content,
1698
- basename: basename$1(filepath),
1699
- mime: mime.getType(filepath)
1700
- };
1701
- };
1702
-
1703
- const screenshot = async (context, name, options = {}) => {
1704
- if (!context.testPath) {
1705
- throw new Error(`Cannot take a screenshot without a test path`);
1706
- }
1707
- const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(
1708
- context.testPath,
1709
- name,
1710
- context.project.config
1711
- );
1712
- const savePath = normalize$1(path);
1713
- await mkdir(dirname(path), { recursive: true });
1714
- if (context.provider instanceof PlaywrightBrowserProvider) {
1715
- if (options.element) {
1716
- const { element: selector, ...config } = options;
1717
- const element = context.iframe.locator(`${selector}`);
1718
- const buffer2 = await element.screenshot({
1719
- timeout: 1e3,
1720
- ...config,
1721
- path: savePath
1722
- });
1723
- return returnResult(options, path, buffer2);
1724
- }
1725
- const buffer = await context.iframe.locator("body").screenshot({
1726
- ...options,
1727
- path: savePath
1728
- });
1729
- return returnResult(options, path, buffer);
1730
- }
1731
- if (context.provider instanceof WebdriverBrowserProvider) {
1732
- const page = context.provider.browser;
1733
- if (!options.element) {
1734
- const body = await page.$("body");
1735
- const buffer2 = await body.saveScreenshot(savePath);
1736
- return returnResult(options, path, buffer2);
1737
- }
1738
- const element = await page.$(`${options.element}`);
1739
- const buffer = await element.saveScreenshot(savePath);
1740
- return returnResult(options, path, buffer);
1741
- }
1742
- throw new Error(
1743
- `Provider "${context.provider.name}" does not support screenshots`
1744
- );
1745
- };
1746
- function resolveScreenshotPath(testPath, name, config) {
1747
- const dir = dirname(testPath);
1748
- const base = basename(testPath);
1749
- if (config.browser.screenshotDirectory) {
1750
- return resolve(
1751
- config.browser.screenshotDirectory,
1752
- relative(config.root, dir),
1753
- base,
1754
- name
1755
- );
1756
- }
1757
- return resolve(dir, "__screenshots__", base, name);
1758
- }
1759
- function returnResult(options, path, buffer) {
1760
- if (options.base64) {
1761
- return { path, base64: buffer.toString("base64") };
1762
- }
1763
- return path;
1764
- }
1765
-
1766
- var builtinCommands = {
1767
- readFile,
1768
- removeFile,
1769
- writeFile,
1770
- __vitest_fileInfo: _fileInfo,
1771
- __vitest_upload: upload,
1772
- __vitest_click: click,
1773
- __vitest_dblClick: dblClick,
1774
- __vitest_tripleClick: tripleClick,
1775
- __vitest_screenshot: screenshot,
1776
- __vitest_type: type,
1777
- __vitest_clear: clear,
1778
- __vitest_fill: fill,
1779
- __vitest_tab: tab,
1780
- __vitest_keyboard: keyboard,
1781
- __vitest_selectOptions: selectOptions,
1782
- __vitest_dragAndDrop: dragAndDrop,
1783
- __vitest_hover: hover
1784
- };
1785
-
1786
- const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
1787
- const ID_CONTEXT = "@vitest/browser/context";
1788
- const __dirname = dirname(fileURLToPath(import.meta.url));
1789
- function BrowserContext(server) {
1790
- const project = server.project;
1791
- project.config.browser.commands ??= {};
1792
- for (const [name, command] of Object.entries(builtinCommands)) {
1793
- project.config.browser.commands[name] ??= command;
1794
- }
1795
- for (const command in project.config.browser.commands) {
1796
- if (!/^[a-z_$][\w$]*$/i.test(command)) {
1797
- throw new Error(
1798
- `Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`
1799
- );
1800
- }
1801
- }
1802
- return {
1803
- name: "vitest:browser:virtual-module:context",
1804
- enforce: "pre",
1805
- resolveId(id) {
1806
- if (id === ID_CONTEXT) {
1807
- return VIRTUAL_ID_CONTEXT;
1808
1794
  }
1809
1795
  },
1810
- load(id) {
1811
- if (id === VIRTUAL_ID_CONTEXT) {
1812
- return generateContextFile.call(this, server);
1796
+ {
1797
+ name: "vitest:browser:worker",
1798
+ transform(code, id, _options) {
1799
+ if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
1800
+ const s = new MagicString(code);
1801
+ s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
1802
+ return {
1803
+ code: s.toString(),
1804
+ map: s.generateMap({ hires: "boundary" })
1805
+ };
1806
+ }
1813
1807
  }
1814
- }
1815
- };
1808
+ },
1809
+ {
1810
+ name: "vitest:browser:transform-tester-html",
1811
+ enforce: "pre",
1812
+ async transformIndexHtml(html, ctx) {
1813
+ if (ctx.filename !== browserServer.testerFilepath) {
1814
+ return;
1815
+ }
1816
+ if (!browserServer.testerScripts) {
1817
+ const testerScripts = await browserServer.formatScripts(
1818
+ project.config.browser.testerScripts
1819
+ );
1820
+ browserServer.testerScripts = testerScripts;
1821
+ }
1822
+ const stateJs = typeof browserServer.stateJs === "string" ? browserServer.stateJs : await browserServer.stateJs;
1823
+ const testerTags = [];
1824
+ const isDefaultTemplate = resolve(distRoot, "client/tester/tester.html") === browserServer.testerFilepath;
1825
+ if (!isDefaultTemplate) {
1826
+ const manifestContent = browserServer.manifest instanceof Promise ? await browserServer.manifest : browserServer.manifest;
1827
+ const testerEntry = manifestContent["tester/tester.html"];
1828
+ testerTags.push({
1829
+ tag: "script",
1830
+ attrs: {
1831
+ type: "module",
1832
+ crossorigin: "",
1833
+ src: `${browserServer.base}${testerEntry.file}`
1834
+ },
1835
+ injectTo: "head"
1836
+ });
1837
+ for (const importName of testerEntry.imports || []) {
1838
+ const entryManifest = manifestContent[importName];
1839
+ if (entryManifest) {
1840
+ testerTags.push(
1841
+ {
1842
+ tag: "link",
1843
+ attrs: {
1844
+ href: `${browserServer.base}${entryManifest.file}`,
1845
+ rel: "modulepreload",
1846
+ crossorigin: ""
1847
+ },
1848
+ injectTo: "head"
1849
+ }
1850
+ );
1851
+ }
1852
+ }
1853
+ } else {
1854
+ testerTags.push({
1855
+ tag: "style",
1856
+ children: `
1857
+ html {
1858
+ padding: 0;
1859
+ margin: 0;
1816
1860
  }
1817
- async function generateContextFile(server) {
1818
- const commands = Object.keys(server.project.config.browser.commands ?? {});
1819
- const filepathCode = "__vitest_worker__.filepath || __vitest_worker__.current?.file?.filepath || undefined";
1820
- const provider = server.provider;
1821
- const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
1822
- return ` ["${command}"]: (...args) => rpc().triggerCommand(contextId, "${command}", filepath(), args),`;
1823
- }).join("\n");
1824
- const userEventNonProviderImport = await getUserEventImport(
1825
- provider,
1826
- this.resolve.bind(this)
1827
- );
1828
- const distContextPath = slash(`/@fs/${resolve(__dirname, "context.js")}`);
1829
- return `
1830
- import { page, createUserEvent, cdp } from '${distContextPath}'
1831
- ${userEventNonProviderImport}
1832
- const filepath = () => ${filepathCode}
1833
- const rpc = () => __vitest_worker__.rpc
1834
- const contextId = __vitest_browser_runner__.contextId
1835
-
1836
- export const server = {
1837
- platform: ${JSON.stringify(process.platform)},
1838
- version: ${JSON.stringify(process.version)},
1839
- provider: ${JSON.stringify(provider.name)},
1840
- browser: ${JSON.stringify(server.project.config.browser.name)},
1841
- commands: {
1842
- ${commandsCode}
1843
- },
1844
- config: __vitest_browser_runner__.config,
1861
+ body {
1862
+ padding: 0;
1863
+ margin: 0;
1864
+ min-height: 100vh;
1865
+ }`,
1866
+ injectTo: "head"
1867
+ });
1868
+ }
1869
+ return [
1870
+ {
1871
+ tag: "script",
1872
+ children: "{__VITEST_INJECTOR__}",
1873
+ injectTo: "head-prepend"
1874
+ },
1875
+ {
1876
+ tag: "script",
1877
+ children: stateJs,
1878
+ injectTo: "head-prepend"
1879
+ },
1880
+ {
1881
+ tag: "script",
1882
+ attrs: {
1883
+ type: "module",
1884
+ src: browserServer.errorCatcherUrl
1885
+ },
1886
+ injectTo: "head"
1887
+ },
1888
+ browserServer.locatorsUrl ? {
1889
+ tag: "script",
1890
+ attrs: {
1891
+ type: "module",
1892
+ src: browserServer.locatorsUrl
1893
+ },
1894
+ injectTo: "head"
1895
+ } : null,
1896
+ ...browserServer.testerScripts,
1897
+ ...testerTags,
1898
+ {
1899
+ tag: "script",
1900
+ attrs: {
1901
+ "type": "module",
1902
+ "data-vitest-append": ""
1903
+ },
1904
+ children: "{__VITEST_APPEND__}",
1905
+ injectTo: "body"
1906
+ }
1907
+ ].filter((s) => s != null);
1908
+ }
1909
+ },
1910
+ {
1911
+ name: "vitest:browser:support-testing-library",
1912
+ config() {
1913
+ return {
1914
+ optimizeDeps: {
1915
+ esbuildOptions: {
1916
+ plugins: [
1917
+ {
1918
+ name: "test-utils-rewrite",
1919
+ setup(build) {
1920
+ build.onResolve({ filter: /^@vue\/test-utils$/ }, (args) => {
1921
+ const _require2 = getRequire();
1922
+ const resolved = _require2.resolve(args.path, {
1923
+ paths: [args.importer]
1924
+ });
1925
+ return { path: resolved };
1926
+ });
1927
+ }
1928
+ }
1929
+ ]
1930
+ }
1931
+ }
1932
+ };
1933
+ }
1934
+ }
1935
+ ];
1936
+ };
1937
+ function tryResolve(path, paths) {
1938
+ try {
1939
+ const _require2 = getRequire();
1940
+ return _require2.resolve(path, { paths });
1941
+ } catch {
1942
+ return void 0;
1943
+ }
1845
1944
  }
1846
- export const commands = server.commands
1847
- export const userEvent = createUserEvent(_userEventSetup)
1848
- export { page, cdp }
1849
- `;
1945
+ let _require;
1946
+ function getRequire() {
1947
+ if (!_require) {
1948
+ _require = createRequire(import.meta.url);
1949
+ }
1950
+ return _require;
1850
1951
  }
1851
- async function getUserEventImport(provider, resolve2) {
1852
- if (provider.name !== "preview") {
1853
- return "const _userEventSetup = undefined";
1952
+ function resolveCoverageFolder(project) {
1953
+ const options = project.ctx.config;
1954
+ const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
1955
+ if (typeof reporter === "string") {
1956
+ return reporter === "html";
1957
+ }
1958
+ return reporter[0] === "html";
1959
+ }) : void 0;
1960
+ if (!htmlReporter) {
1961
+ return void 0;
1854
1962
  }
1855
- const resolved = await resolve2("@testing-library/user-event", __dirname);
1856
- if (!resolved) {
1857
- throw new Error(`Failed to resolve user-event package from ${__dirname}`);
1963
+ const root = resolve(
1964
+ options.root || process.cwd(),
1965
+ options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
1966
+ );
1967
+ const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
1968
+ if (!subdir || typeof subdir !== "string") {
1969
+ return [root, `/${basename(root)}/`];
1858
1970
  }
1859
- return `import { userEvent as __vitest_user_event__ } from '${slash(
1860
- `/@fs/${resolved.id}`
1861
- )}'
1862
- const _userEventSetup = __vitest_user_event__.setup()
1863
- `;
1971
+ return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
1972
+ }
1973
+ const postfixRE = /[?#].*$/;
1974
+ function cleanUrl(url) {
1975
+ return url.replace(postfixRE, "");
1864
1976
  }
1865
1977
 
1866
- async function resolveOrchestrator(server, url, res) {
1867
- const project = server.project;
1868
- let contextId = url.searchParams.get("contextId");
1869
- if (!contextId) {
1870
- const contexts = [...server.state.orchestrators.keys()];
1871
- contextId = contexts[contexts.length - 1] ?? "none";
1872
- }
1873
- const files = server.state.getContext(contextId)?.files ?? [];
1874
- const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1875
- const injector = replacer(injectorJs, {
1876
- __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1877
- __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1878
- __VITEST_VITE_CONFIG__: JSON.stringify({
1879
- root: server.vite.config.root
1880
- }),
1881
- __VITEST_FILES__: JSON.stringify(files),
1882
- __VITEST_TYPE__: '"orchestrator"',
1883
- __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1884
- __VITEST_TESTER_ID__: '"none"',
1885
- __VITEST_PROVIDED_CONTEXT__: "{}"
1978
+ const DEFAULT_TIMEOUT = 6e4;
1979
+ function defaultSerialize(i) {
1980
+ return i;
1981
+ }
1982
+ const defaultDeserialize = defaultSerialize;
1983
+ const { clearTimeout, setTimeout } = globalThis;
1984
+ const random = Math.random.bind(Math);
1985
+ function createBirpc(functions, options) {
1986
+ const {
1987
+ post,
1988
+ on,
1989
+ off = () => {
1990
+ },
1991
+ eventNames = [],
1992
+ serialize = defaultSerialize,
1993
+ deserialize = defaultDeserialize,
1994
+ resolver,
1995
+ bind = "rpc",
1996
+ timeout = DEFAULT_TIMEOUT
1997
+ } = options;
1998
+ const rpcPromiseMap = /* @__PURE__ */ new Map();
1999
+ let _promise;
2000
+ let closed = false;
2001
+ const rpc = new Proxy({}, {
2002
+ get(_, method) {
2003
+ if (method === "$functions")
2004
+ return functions;
2005
+ if (method === "$close")
2006
+ return close;
2007
+ if (method === "then" && !eventNames.includes("then") && !("then" in functions))
2008
+ return void 0;
2009
+ const sendEvent = (...args) => {
2010
+ post(serialize({ m: method, a: args, t: "q" }));
2011
+ };
2012
+ if (eventNames.includes(method)) {
2013
+ sendEvent.asEvent = sendEvent;
2014
+ return sendEvent;
2015
+ }
2016
+ const sendCall = async (...args) => {
2017
+ if (closed)
2018
+ throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
2019
+ if (_promise) {
2020
+ try {
2021
+ await _promise;
2022
+ } finally {
2023
+ _promise = void 0;
2024
+ }
2025
+ }
2026
+ return new Promise((resolve, reject) => {
2027
+ const id = nanoid();
2028
+ let timeoutId;
2029
+ if (timeout >= 0) {
2030
+ timeoutId = setTimeout(() => {
2031
+ try {
2032
+ options.onTimeoutError?.(method, args);
2033
+ throw new Error(`[birpc] timeout on calling "${method}"`);
2034
+ } catch (e) {
2035
+ reject(e);
2036
+ }
2037
+ rpcPromiseMap.delete(id);
2038
+ }, timeout);
2039
+ if (typeof timeoutId === "object")
2040
+ timeoutId = timeoutId.unref?.();
2041
+ }
2042
+ rpcPromiseMap.set(id, { resolve, reject, timeoutId, method });
2043
+ post(serialize({ m: method, a: args, i: id, t: "q" }));
2044
+ });
2045
+ };
2046
+ sendCall.asEvent = sendEvent;
2047
+ return sendCall;
2048
+ }
1886
2049
  });
1887
- res.removeHeader("Content-Security-Policy");
1888
- if (!server.orchestratorScripts) {
1889
- server.orchestratorScripts = await server.formatScripts(
1890
- project.config.browser.orchestratorScripts
1891
- );
2050
+ function close() {
2051
+ closed = true;
2052
+ rpcPromiseMap.forEach(({ reject, method }) => {
2053
+ reject(new Error(`[birpc] rpc is closed, cannot call "${method}"`));
2054
+ });
2055
+ rpcPromiseMap.clear();
2056
+ off(onMessage);
1892
2057
  }
1893
- let baseHtml = typeof server.orchestratorHtml === "string" ? server.orchestratorHtml : await server.orchestratorHtml;
1894
- if (project.config.browser.ui) {
1895
- const manifestContent = server.manifest instanceof Promise ? await server.manifest : server.manifest;
1896
- const jsEntry = manifestContent["orchestrator.html"].file;
1897
- const base = server.vite.config.base || "/";
1898
- baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
1899
- "<!-- !LOAD_METADATA! -->",
1900
- [
1901
- "{__VITEST_INJECTOR__}",
1902
- "{__VITEST_ERROR_CATCHER__}",
1903
- "{__VITEST_SCRIPTS__}",
1904
- `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
1905
- ].join("\n")
1906
- );
2058
+ async function onMessage(data, ...extra) {
2059
+ const msg = deserialize(data);
2060
+ if (msg.t === "q") {
2061
+ const { m: method, a: args } = msg;
2062
+ let result, error;
2063
+ const fn = resolver ? resolver(method, functions[method]) : functions[method];
2064
+ if (!fn) {
2065
+ error = new Error(`[birpc] function "${method}" not found`);
2066
+ } else {
2067
+ try {
2068
+ result = await fn.apply(bind === "rpc" ? rpc : functions, args);
2069
+ } catch (e) {
2070
+ error = e;
2071
+ }
2072
+ }
2073
+ if (msg.i) {
2074
+ if (error && options.onError)
2075
+ options.onError(error, method, args);
2076
+ post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra);
2077
+ }
2078
+ } else {
2079
+ const { i: ack, r: result, e: error } = msg;
2080
+ const promise = rpcPromiseMap.get(ack);
2081
+ if (promise) {
2082
+ clearTimeout(promise.timeoutId);
2083
+ if (error)
2084
+ promise.reject(error);
2085
+ else
2086
+ promise.resolve(result);
2087
+ }
2088
+ rpcPromiseMap.delete(ack);
2089
+ }
1907
2090
  }
1908
- return replacer(baseHtml, {
1909
- __VITEST_FAVICON__: server.faviconUrl,
1910
- __VITEST_TITLE__: "Vitest Browser Runner",
1911
- __VITEST_SCRIPTS__: server.orchestratorScripts,
1912
- __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
1913
- __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
1914
- __VITEST_CONTEXT_ID__: JSON.stringify(contextId)
1915
- });
2091
+ _promise = on(onMessage);
2092
+ return rpc;
1916
2093
  }
1917
-
1918
- async function resolveTester(server, url, res) {
1919
- const csp = res.getHeader("Content-Security-Policy");
1920
- if (typeof csp === "string") {
1921
- res.setHeader(
1922
- "Content-Security-Policy",
1923
- csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *")
1924
- );
1925
- }
1926
- const { contextId, testFile } = server.resolveTesterUrl(url.pathname);
1927
- const project = server.project;
1928
- const state = server.state;
1929
- const { testFiles } = await project.globTestFiles();
1930
- const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
1931
- const iframeId = JSON.stringify(testFile);
1932
- const context = state.getContext(contextId);
1933
- const files = context?.files ?? [];
1934
- const method = context?.method ?? "run";
1935
- const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1936
- const injector = replacer(injectorJs, {
1937
- __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1938
- __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1939
- __VITEST_FILES__: JSON.stringify(files),
1940
- __VITEST_VITE_CONFIG__: JSON.stringify({
1941
- root: server.vite.config.root
1942
- }),
1943
- __VITEST_TYPE__: '"tester"',
1944
- __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1945
- __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
1946
- __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
1947
- });
1948
- if (!server.testerScripts) {
1949
- const testerScripts = await server.formatScripts(
1950
- project.config.browser.testerScripts
1951
- );
1952
- const clientScript = `<script type="module" src="${server.base}@vite/client"><\/script>`;
1953
- const stateJs = typeof server.stateJs === "string" ? server.stateJs : await server.stateJs;
1954
- const stateScript = `<script type="module">${stateJs}<\/script>`;
1955
- server.testerScripts = `${stateScript}${clientScript}${testerScripts}`;
1956
- }
1957
- const testerHtml = typeof server.testerHtml === "string" ? server.testerHtml : await server.testerHtml;
1958
- return replacer(testerHtml, {
1959
- __VITEST_FAVICON__: server.faviconUrl,
1960
- __VITEST_TITLE__: "Vitest Browser Tester",
1961
- __VITEST_SCRIPTS__: server.testerScripts,
1962
- __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
1963
- __VITEST_INTERNAL_SCRIPTS__: [
1964
- `<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
1965
- server.locatorsUrl ? `<script type="module" src="${server.locatorsUrl}"><\/script>` : ""
1966
- ].join("\n"),
1967
- __VITEST_APPEND__: `<script data-vitest-append type="module">
1968
- __vitest_browser_runner__.runningFiles = ${tests}
1969
- __vitest_browser_runner__.iframeId = ${iframeId}
1970
- __vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
1971
- document.querySelector('script[data-vitest-append]').remove()
1972
- <\/script>`
1973
- });
2094
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
2095
+ function nanoid(size = 21) {
2096
+ let id = "";
2097
+ let i = size;
2098
+ while (i--)
2099
+ id += urlAlphabet[random() * 64 | 0];
2100
+ return id;
1974
2101
  }
1975
2102
 
1976
- var BrowserPlugin = (browserServer, base = "/") => {
1977
- const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
1978
- const distRoot = resolve(pkgRoot, "dist");
1979
- const project = browserServer.project;
1980
- return [
1981
- {
1982
- enforce: "pre",
1983
- name: "vitest:browser",
1984
- async configureServer(server) {
1985
- browserServer.setServer(server);
1986
- server.middlewares.use(function vitestHeaders(_req, res, next) {
1987
- const headers = server.config.server.headers;
1988
- if (headers) {
1989
- for (const name in headers) {
1990
- res.setHeader(name, headers[name]);
1991
- }
1992
- }
1993
- next();
1994
- });
1995
- server.middlewares.use(async function vitestBrowserMode(req, res, next) {
1996
- if (!req.url || !browserServer.provider) {
1997
- return next();
1998
- }
1999
- const url = new URL(req.url, "http://localhost");
2000
- if (!url.pathname.startsWith(browserServer.prefixTesterUrl) && url.pathname !== base) {
2001
- return next();
2103
+ const debug$1 = createDebugger("vitest:browser:api");
2104
+ const BROWSER_API_PATH = "/__vitest_browser_api__";
2105
+ function setupBrowserRpc(server) {
2106
+ const project = server.project;
2107
+ const vite = server.vite;
2108
+ const ctx = project.ctx;
2109
+ const wss = new WebSocketServer({ noServer: true });
2110
+ vite.httpServer?.on("upgrade", (request, socket, head) => {
2111
+ if (!request.url) {
2112
+ return;
2113
+ }
2114
+ const { pathname, searchParams } = new URL(request.url, "http://localhost");
2115
+ if (pathname !== BROWSER_API_PATH) {
2116
+ return;
2117
+ }
2118
+ const type = searchParams.get("type") ?? "tester";
2119
+ const sessionId = searchParams.get("sessionId") ?? "0";
2120
+ wss.handleUpgrade(request, socket, head, (ws) => {
2121
+ wss.emit("connection", ws, request);
2122
+ const rpc = setupClient(sessionId, ws);
2123
+ const state = server.state;
2124
+ const clients = type === "tester" ? state.testers : state.orchestrators;
2125
+ clients.set(sessionId, rpc);
2126
+ debug$1?.("[%s] Browser API connected to %s", sessionId, type);
2127
+ ws.on("close", () => {
2128
+ debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
2129
+ clients.delete(sessionId);
2130
+ server.state.removeCDPHandler(sessionId);
2131
+ });
2132
+ });
2133
+ });
2134
+ function checkFileAccess(path) {
2135
+ if (!isFileServingAllowed(path, vite)) {
2136
+ throw new Error(
2137
+ `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
2138
+ );
2139
+ }
2140
+ }
2141
+ function setupClient(sessionId, ws) {
2142
+ const mockResolver = new ServerMockResolver(server.vite, {
2143
+ moduleDirectories: project.config.server?.deps?.moduleDirectories
2144
+ });
2145
+ const rpc = createBirpc(
2146
+ {
2147
+ async onUnhandledError(error, type) {
2148
+ if (error && typeof error === "object") {
2149
+ const _error = error;
2150
+ _error.stacks = server.parseErrorStacktrace(_error);
2002
2151
  }
2003
- res.setHeader(
2004
- "Cache-Control",
2005
- "no-cache, max-age=0, must-revalidate"
2006
- );
2007
- res.setHeader("Content-Type", "text/html; charset=utf-8");
2008
- res.removeHeader("X-Frame-Options");
2009
- if (url.pathname === base) {
2010
- const html2 = await resolveOrchestrator(browserServer, url, res);
2011
- res.write(html2, "utf-8");
2012
- res.end();
2013
- return;
2152
+ ctx.state.catchError(error, type);
2153
+ },
2154
+ async onCollected(files) {
2155
+ ctx.state.collectFiles(project, files);
2156
+ await ctx.report("onCollected", files);
2157
+ },
2158
+ async onTaskUpdate(packs) {
2159
+ ctx.state.updateTasks(packs);
2160
+ await ctx.report("onTaskUpdate", packs);
2161
+ },
2162
+ onAfterSuiteRun(meta) {
2163
+ ctx.coverageProvider?.onAfterSuiteRun(meta);
2164
+ },
2165
+ sendLog(log) {
2166
+ return ctx.report("onUserConsoleLog", log);
2167
+ },
2168
+ resolveSnapshotPath(testPath) {
2169
+ return ctx.snapshot.resolvePath(testPath);
2170
+ },
2171
+ resolveSnapshotRawPath(testPath, rawPath) {
2172
+ return ctx.snapshot.resolveRawPath(testPath, rawPath);
2173
+ },
2174
+ snapshotSaved(snapshot) {
2175
+ ctx.snapshot.add(snapshot);
2176
+ },
2177
+ async readSnapshotFile(snapshotPath) {
2178
+ checkFileAccess(snapshotPath);
2179
+ if (!existsSync(snapshotPath)) {
2180
+ return null;
2014
2181
  }
2015
- const html = await resolveTester(browserServer, url, res);
2016
- res.write(html, "utf-8");
2017
- res.end();
2018
- });
2019
- server.middlewares.use(
2020
- `${base}favicon.svg`,
2021
- (_, res) => {
2022
- const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
2023
- res.write(content, "utf-8");
2024
- res.end();
2182
+ return promises.readFile(snapshotPath, "utf-8");
2183
+ },
2184
+ async saveSnapshotFile(id, content) {
2185
+ checkFileAccess(id);
2186
+ await promises.mkdir(dirname(id), { recursive: true });
2187
+ return promises.writeFile(id, content, "utf-8");
2188
+ },
2189
+ async removeSnapshotFile(id) {
2190
+ checkFileAccess(id);
2191
+ if (!existsSync(id)) {
2192
+ throw new Error(`Snapshot file "${id}" does not exist.`);
2025
2193
  }
2026
- );
2027
- const coverageFolder = resolveCoverageFolder(project);
2028
- const coveragePath = coverageFolder ? coverageFolder[1] : void 0;
2029
- if (coveragePath && base === coveragePath) {
2030
- throw new Error(
2031
- `The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`
2032
- );
2033
- }
2034
- if (coverageFolder) {
2035
- server.middlewares.use(
2036
- coveragePath,
2037
- sirv(coverageFolder[0], {
2038
- single: true,
2039
- dev: true,
2040
- setHeaders: (res) => {
2041
- res.setHeader(
2042
- "Cache-Control",
2043
- "public,max-age=0,must-revalidate"
2044
- );
2045
- }
2046
- })
2047
- );
2048
- }
2049
- const screenshotFailures = project.config.browser.ui && project.config.browser.screenshotFailures;
2050
- if (screenshotFailures) {
2051
- server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
2052
- if (!req.url || !browserServer.provider) {
2053
- res.statusCode = 404;
2054
- res.end();
2055
- return;
2056
- }
2057
- const url = new URL(req.url, "http://localhost");
2058
- const file = url.searchParams.get("file");
2059
- if (!file) {
2060
- res.statusCode = 404;
2061
- res.end();
2062
- return;
2063
- }
2064
- let stat;
2065
- try {
2066
- stat = lstatSync(file);
2067
- } catch {
2068
- }
2069
- if (!stat?.isFile()) {
2070
- res.statusCode = 404;
2071
- res.end();
2072
- return;
2073
- }
2074
- const ext = extname(file);
2075
- const buffer = readFileSync(file);
2076
- res.setHeader(
2077
- "Cache-Control",
2078
- "public,max-age=0,must-revalidate"
2079
- );
2080
- res.setHeader("Content-Length", buffer.length);
2081
- res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
2082
- res.end(buffer);
2083
- });
2084
- }
2085
- }
2086
- },
2087
- {
2088
- name: "vitest:browser:tests",
2089
- enforce: "pre",
2090
- async config() {
2091
- const { testFiles: allTestFiles } = await project.globTestFiles();
2092
- const browserTestFiles = allTestFiles.filter(
2093
- (file) => getFilePoolName(project, file) === "browser"
2094
- );
2095
- const setupFiles = toArray(project.config.setupFiles);
2096
- const define = {};
2097
- for (const env in project.config.env || {}) {
2098
- const stringValue = JSON.stringify(project.config.env[env]);
2099
- define[`process.env.${env}`] = stringValue;
2100
- define[`import.meta.env.${env}`] = stringValue;
2101
- }
2102
- const entries = [
2103
- ...browserTestFiles,
2104
- ...setupFiles,
2105
- resolve(distDir, "index.js"),
2106
- resolve(distDir, "browser.js"),
2107
- resolve(distDir, "runners.js"),
2108
- resolve(distDir, "utils.js"),
2109
- ...project.config.snapshotSerializers || []
2110
- ];
2111
- const exclude = [
2112
- "vitest",
2113
- "vitest/utils",
2114
- "vitest/browser",
2115
- "vitest/runners",
2116
- "@vitest/browser",
2117
- "@vitest/browser/client",
2118
- "@vitest/utils",
2119
- "@vitest/utils/source-map",
2120
- "@vitest/runner",
2121
- "@vitest/spy",
2122
- "@vitest/utils/error",
2123
- "@vitest/snapshot",
2124
- "@vitest/expect",
2125
- "std-env",
2126
- "tinybench",
2127
- "tinyspy",
2128
- "tinyrainbow",
2129
- "pathe",
2130
- "msw",
2131
- "msw/browser"
2132
- ];
2133
- if (project.config.diff) {
2134
- entries.push(project.config.diff);
2135
- }
2136
- if (project.ctx.coverageProvider) {
2137
- const coverage = project.ctx.config.coverage;
2138
- const provider = coverage.provider;
2139
- if (provider === "v8") {
2140
- const path = tryResolve("@vitest/coverage-v8", [project.ctx.config.root]);
2141
- if (path) {
2142
- entries.push(path);
2143
- exclude.push("@vitest/coverage-v8/browser");
2144
- }
2145
- } else if (provider === "istanbul") {
2146
- const path = tryResolve("@vitest/coverage-istanbul", [project.ctx.config.root]);
2147
- if (path) {
2148
- entries.push(path);
2149
- exclude.push("@vitest/coverage-istanbul");
2150
- }
2151
- } else if (provider === "custom" && coverage.customProviderModule) {
2152
- entries.push(coverage.customProviderModule);
2194
+ return promises.unlink(id);
2195
+ },
2196
+ getBrowserFileSourceMap(id) {
2197
+ const mod = server.vite.moduleGraph.getModuleById(id);
2198
+ return mod?.transformResult?.map;
2199
+ },
2200
+ onCancel(reason) {
2201
+ ctx.cancelCurrentRun(reason);
2202
+ },
2203
+ async resolveId(id, importer) {
2204
+ return mockResolver.resolveId(id, importer);
2205
+ },
2206
+ debug(...args) {
2207
+ ctx.logger.console.debug(...args);
2208
+ },
2209
+ getCountOfFailedTests() {
2210
+ return ctx.state.getCountOfFailedTests();
2211
+ },
2212
+ async triggerCommand(contextId, command, testPath, payload) {
2213
+ debug$1?.('[%s] Triggering command "%s"', contextId, command);
2214
+ const provider = server.provider;
2215
+ if (!provider) {
2216
+ throw new Error("Commands are only available for browser tests.");
2153
2217
  }
2154
- }
2155
- const include = [
2156
- "vitest > @vitest/snapshot > magic-string",
2157
- "vitest > chai",
2158
- "vitest > chai > loupe",
2159
- "vitest > @vitest/utils > loupe",
2160
- "@vitest/browser > @testing-library/user-event",
2161
- "@vitest/browser > @testing-library/dom"
2162
- ];
2163
- const react = tryResolve("vitest-browser-react", [project.ctx.config.root]);
2164
- if (react) {
2165
- include.push(react);
2166
- }
2167
- const vue = tryResolve("vitest-browser-vue", [project.ctx.config.root]);
2168
- if (vue) {
2169
- include.push(vue);
2170
- }
2171
- const svelte = tryResolve("vitest-browser-svelte", [project.ctx.config.root]);
2172
- if (svelte) {
2173
- exclude.push(svelte);
2174
- }
2175
- return {
2176
- define,
2177
- resolve: {
2178
- dedupe: ["vitest"]
2179
- },
2180
- optimizeDeps: {
2181
- entries,
2182
- exclude,
2183
- include
2218
+ const commands = project.config.browser?.commands;
2219
+ if (!commands || !commands[command]) {
2220
+ throw new Error(`Unknown command "${command}".`);
2184
2221
  }
2185
- };
2186
- },
2187
- async resolveId(id) {
2188
- if (!/\?browserv=\w+$/.test(id)) {
2189
- return;
2190
- }
2191
- let useId = id.slice(0, id.lastIndexOf("?"));
2192
- if (useId.startsWith("/@fs/")) {
2193
- useId = useId.slice(5);
2194
- }
2195
- if (/^\w:/.test(useId)) {
2196
- useId = useId.replace(/\\/g, "/");
2197
- }
2198
- return useId;
2199
- }
2200
- },
2201
- {
2202
- name: "vitest:browser:resolve-virtual",
2203
- async resolveId(rawId) {
2204
- if (rawId === "/mockServiceWorker.js") {
2205
- return this.resolve("msw/mockServiceWorker.js", distRoot, {
2206
- skipSelf: true
2207
- });
2208
- }
2209
- }
2210
- },
2211
- {
2212
- name: "vitest:browser:assets",
2213
- configureServer(server) {
2214
- server.middlewares.use(
2215
- "/__vitest__",
2216
- sirv(resolve(distRoot, "client/__vitest__"))
2217
- );
2218
- },
2219
- resolveId(id) {
2220
- if (id.startsWith("/__vitest_browser__/")) {
2221
- return resolve(distRoot, "client", id.slice(1));
2222
+ if (provider.beforeCommand) {
2223
+ await provider.beforeCommand(command, payload);
2224
+ }
2225
+ const context = Object.assign(
2226
+ {
2227
+ testPath,
2228
+ project,
2229
+ provider,
2230
+ contextId
2231
+ },
2232
+ provider.getCommandsContext(contextId)
2233
+ );
2234
+ let result;
2235
+ try {
2236
+ result = await commands[command](context, ...payload);
2237
+ } finally {
2238
+ if (provider.afterCommand) {
2239
+ await provider.afterCommand(command, payload);
2240
+ }
2241
+ }
2242
+ return result;
2243
+ },
2244
+ finishBrowserTests(contextId) {
2245
+ debug$1?.("[%s] Finishing browser tests for context", contextId);
2246
+ return server.state.getContext(contextId)?.resolve();
2247
+ },
2248
+ resolveMock(rawId, importer, options) {
2249
+ return mockResolver.resolveMock(rawId, importer, options);
2250
+ },
2251
+ invalidate(ids) {
2252
+ return mockResolver.invalidate(ids);
2253
+ },
2254
+ // CDP
2255
+ async sendCdpEvent(contextId, event, payload) {
2256
+ const cdp = await server.ensureCDPHandler(contextId, sessionId);
2257
+ return cdp.send(event, payload);
2258
+ },
2259
+ async trackCdpEvent(contextId, type, event, listenerId) {
2260
+ const cdp = await server.ensureCDPHandler(contextId, sessionId);
2261
+ cdp[type](event, listenerId);
2222
2262
  }
2223
2263
  },
2224
- transform(code, id) {
2225
- if (id.includes(browserServer.vite.config.cacheDir) && id.includes("loupe.js")) {
2226
- const utilRequire = "nodeUtil = require_util();";
2227
- return code.replace(utilRequire, " ".repeat(utilRequire.length));
2264
+ {
2265
+ post: (msg) => ws.send(msg),
2266
+ on: (fn) => ws.on("message", fn),
2267
+ eventNames: ["onCancel", "cdpEvent"],
2268
+ serialize: (data) => stringify(data, stringifyReplace),
2269
+ deserialize: parse,
2270
+ onTimeoutError(functionName) {
2271
+ throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
2228
2272
  }
2229
2273
  }
2230
- },
2231
- BrowserContext(browserServer),
2232
- dynamicImportPlugin({
2233
- globalThisAccessor: '"__vitest_browser_runner__"'
2274
+ );
2275
+ ctx.onCancel((reason) => rpc.onCancel(reason));
2276
+ return rpc;
2277
+ }
2278
+ }
2279
+ function cloneByOwnProperties(value) {
2280
+ return Object.getOwnPropertyNames(value).reduce(
2281
+ (clone, prop) => ({
2282
+ ...clone,
2283
+ [prop]: value[prop]
2234
2284
  }),
2235
- {
2236
- name: "vitest:browser:config",
2237
- enforce: "post",
2238
- async config(viteConfig) {
2239
- // Enables using ignore hint for coverage providers with @preserve keyword
2240
- if (viteConfig.esbuild !== false) {
2241
- viteConfig.esbuild ||= {};
2242
- viteConfig.esbuild.legalComments = "inline";
2243
- }
2244
- const api = resolveApiServerConfig(
2245
- viteConfig.test?.browser || {},
2246
- defaultBrowserPort
2247
- ) || {
2248
- port: defaultBrowserPort
2249
- };
2250
- viteConfig.server = {
2251
- ...viteConfig.server,
2252
- ...api,
2253
- middlewareMode: false,
2254
- open: false
2255
- };
2256
- viteConfig.server.fs ??= {};
2257
- viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
2258
- viteConfig.server.fs.allow.push(
2259
- ...resolveFsAllow(
2260
- project.ctx.config.root,
2261
- project.ctx.server.config.configFile
2262
- ),
2263
- distRoot
2285
+ {}
2286
+ );
2287
+ }
2288
+ function stringifyReplace(key, value) {
2289
+ if (value instanceof Error) {
2290
+ const cloned = cloneByOwnProperties(value);
2291
+ return {
2292
+ name: value.name,
2293
+ message: value.message,
2294
+ stack: value.stack,
2295
+ ...cloned
2296
+ };
2297
+ } else {
2298
+ return value;
2299
+ }
2300
+ }
2301
+
2302
+ class BrowserServerCDPHandler {
2303
+ constructor(session, tester) {
2304
+ this.session = session;
2305
+ this.tester = tester;
2306
+ }
2307
+ listenerIds = {};
2308
+ listeners = {};
2309
+ send(method, params) {
2310
+ return this.session.send(method, params);
2311
+ }
2312
+ on(event, id, once = false) {
2313
+ if (!this.listenerIds[event]) {
2314
+ this.listenerIds[event] = [];
2315
+ }
2316
+ this.listenerIds[event].push(id);
2317
+ if (!this.listeners[event]) {
2318
+ this.listeners[event] = (payload) => {
2319
+ this.tester.cdpEvent(
2320
+ event,
2321
+ payload
2264
2322
  );
2265
- return {
2266
- resolve: {
2267
- alias: viteConfig.test?.alias
2268
- }
2269
- };
2270
- }
2271
- },
2272
- {
2273
- name: "vitest:browser:in-source-tests",
2274
- transform(code, id) {
2275
- if (!project.isTestFile(id) || !code.includes("import.meta.vitest")) {
2276
- return;
2323
+ if (once) {
2324
+ this.off(event, id);
2325
+ }
2326
+ };
2327
+ this.session.on(event, this.listeners[event]);
2328
+ }
2329
+ }
2330
+ off(event, id) {
2331
+ if (!this.listenerIds[event]) {
2332
+ this.listenerIds[event] = [];
2333
+ }
2334
+ this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
2335
+ if (!this.listenerIds[event].length) {
2336
+ this.session.off(event, this.listeners[event]);
2337
+ delete this.listeners[event];
2338
+ }
2339
+ }
2340
+ once(event, listener) {
2341
+ this.on(event, listener, true);
2342
+ }
2343
+ }
2344
+
2345
+ class BrowserServerState {
2346
+ orchestrators = /* @__PURE__ */ new Map();
2347
+ testers = /* @__PURE__ */ new Map();
2348
+ cdps = /* @__PURE__ */ new Map();
2349
+ contexts = /* @__PURE__ */ new Map();
2350
+ getContext(contextId) {
2351
+ return this.contexts.get(contextId);
2352
+ }
2353
+ createAsyncContext(method, contextId, files) {
2354
+ const defer = createDefer();
2355
+ this.contexts.set(contextId, {
2356
+ files,
2357
+ method,
2358
+ resolve: () => {
2359
+ defer.resolve();
2360
+ this.contexts.delete(contextId);
2361
+ },
2362
+ reject: defer.reject
2363
+ });
2364
+ return defer;
2365
+ }
2366
+ async removeCDPHandler(sessionId) {
2367
+ this.cdps.delete(sessionId);
2368
+ }
2369
+ }
2370
+
2371
+ class BrowserServer {
2372
+ constructor(project, base) {
2373
+ this.project = project;
2374
+ this.base = base;
2375
+ this.stackTraceOptions = {
2376
+ frameFilter: project.config.onStackTrace,
2377
+ getSourceMap: (id) => {
2378
+ const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
2379
+ return result?.map;
2380
+ },
2381
+ getFileName: (id) => {
2382
+ const mod = this.vite.moduleGraph.getModuleById(id);
2383
+ if (mod?.file) {
2384
+ return mod.file;
2277
2385
  }
2278
- const s = new MagicString(code, { filename: cleanUrl(id) });
2279
- s.prepend(
2280
- `import.meta.vitest = __vitest_index__;
2281
- `
2282
- );
2283
- return {
2284
- code: s.toString(),
2285
- map: s.generateMap({ hires: true })
2286
- };
2287
- }
2288
- },
2289
- {
2290
- name: "vitest:browser:worker",
2291
- transform(code, id, _options) {
2292
- if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
2293
- const s = new MagicString(code);
2294
- s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
2295
- return {
2296
- code: s.toString(),
2297
- map: s.generateMap({ hires: "boundary" })
2298
- };
2386
+ const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
2387
+ if (modUrl?.file) {
2388
+ return modUrl.file;
2299
2389
  }
2390
+ return id;
2300
2391
  }
2301
- },
2302
- // TODO: remove this when @testing-library/vue supports ESM
2303
- {
2304
- name: "vitest:browser:support-testing-library",
2305
- config() {
2392
+ };
2393
+ this.state = new BrowserServerState();
2394
+ const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
2395
+ const distRoot = resolve(pkgRoot, "dist");
2396
+ this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
2397
+ this.faviconUrl = `${base}__vitest__/favicon.svg`;
2398
+ this.manifest = (async () => {
2399
+ return JSON.parse(
2400
+ await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8")
2401
+ );
2402
+ })().then((manifest) => this.manifest = manifest);
2403
+ const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
2404
+ if (!existsSync(testerHtmlPath)) {
2405
+ throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
2406
+ }
2407
+ this.testerFilepath = testerHtmlPath;
2408
+ this.testerHtml = readFile$1(
2409
+ testerHtmlPath,
2410
+ "utf8"
2411
+ ).then((html) => this.testerHtml = html);
2412
+ 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);
2413
+ this.injectorJs = readFile$1(
2414
+ resolve(distRoot, "client/esm-client-injector.js"),
2415
+ "utf8"
2416
+ ).then((js) => this.injectorJs = js);
2417
+ this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
2418
+ const builtinProviders = ["playwright", "webdriverio", "preview"];
2419
+ const providerName = project.config.browser.provider || "preview";
2420
+ if (builtinProviders.includes(providerName)) {
2421
+ this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
2422
+ }
2423
+ this.stateJs = readFile$1(
2424
+ resolve(distRoot, "state.js"),
2425
+ "utf-8"
2426
+ ).then((js) => this.stateJs = js);
2427
+ }
2428
+ faviconUrl;
2429
+ prefixTesterUrl;
2430
+ orchestratorScripts;
2431
+ testerScripts;
2432
+ manifest;
2433
+ testerHtml;
2434
+ testerFilepath;
2435
+ orchestratorHtml;
2436
+ injectorJs;
2437
+ errorCatcherUrl;
2438
+ locatorsUrl;
2439
+ stateJs;
2440
+ state;
2441
+ provider;
2442
+ vite;
2443
+ stackTraceOptions;
2444
+ setServer(server) {
2445
+ this.vite = server;
2446
+ }
2447
+ getSerializableConfig() {
2448
+ const config = wrapConfig(this.project.getSerializableConfig());
2449
+ config.env ??= {};
2450
+ config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
2451
+ return config;
2452
+ }
2453
+ resolveTesterUrl(pathname) {
2454
+ const [contextId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
2455
+ const decodedTestFile = decodeURIComponent(testFile);
2456
+ return { contextId, testFile: decodedTestFile };
2457
+ }
2458
+ async formatScripts(scripts) {
2459
+ if (!scripts?.length) {
2460
+ return [];
2461
+ }
2462
+ const server = this.vite;
2463
+ const promises = scripts.map(
2464
+ async ({ content, src, async, id, type = "module" }, index) => {
2465
+ const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : void 0) || src;
2466
+ const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
2467
+ await server.moduleGraph.ensureEntryFromUrl(transformId);
2468
+ const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
2306
2469
  return {
2307
- define: {
2308
- // testing-library/preact
2309
- "process.env.PTL_SKIP_AUTO_CLEANUP": !!process.env.PTL_SKIP_AUTO_CLEANUP,
2310
- // testing-library/react
2311
- "process.env.RTL_SKIP_AUTO_CLEANUP": !!process.env.RTL_SKIP_AUTO_CLEANUP,
2312
- "process.env?.RTL_SKIP_AUTO_CLEANUP": !!process.env.RTL_SKIP_AUTO_CLEANUP,
2313
- // testing-library/svelte, testing-library/solid
2314
- "process.env.STL_SKIP_AUTO_CLEANUP": !!process.env.STL_SKIP_AUTO_CLEANUP,
2315
- // testing-library/vue
2316
- "process.env.VTL_SKIP_AUTO_CLEANUP": !!process.env.VTL_SKIP_AUTO_CLEANUP,
2317
- // dom.debug()
2318
- "process.env.DEBUG_PRINT_LIMIT": process.env.DEBUG_PRINT_LIMIT || 7e3
2470
+ tag: "script",
2471
+ attrs: {
2472
+ type,
2473
+ ...async ? { async: "" } : {},
2474
+ ...srcLink ? {
2475
+ src: srcLink.startsWith("http") ? srcLink : slash(`/@fs/${srcLink}`)
2476
+ } : {}
2319
2477
  },
2320
- optimizeDeps: {
2321
- esbuildOptions: {
2322
- plugins: [
2323
- {
2324
- name: "test-utils-rewrite",
2325
- setup(build) {
2326
- build.onResolve({ filter: /@vue\/test-utils/ }, (args) => {
2327
- const _require2 = getRequire();
2328
- const resolved = _require2.resolve(args.path, {
2329
- paths: [args.importer]
2330
- });
2331
- return { path: resolved };
2332
- });
2333
- }
2334
- }
2335
- ]
2336
- }
2337
- }
2478
+ injectTo: "head",
2479
+ children: contentProcessed || ""
2338
2480
  };
2339
2481
  }
2482
+ );
2483
+ return await Promise.all(promises);
2484
+ }
2485
+ async initBrowserProvider() {
2486
+ if (this.provider) {
2487
+ return;
2340
2488
  }
2341
- ];
2342
- };
2343
- function tryResolve(path, paths) {
2344
- try {
2345
- const _require2 = getRequire();
2346
- return _require2.resolve(path, { paths });
2347
- } catch {
2348
- return void 0;
2489
+ const Provider = await getBrowserProvider(this.project.config.browser, this.project);
2490
+ this.provider = new Provider();
2491
+ const browser = this.project.config.browser.name;
2492
+ if (!browser) {
2493
+ throw new Error(
2494
+ `[${this.project.getName()}] Browser name is required. Please, set \`test.browser.name\` option manually.`
2495
+ );
2496
+ }
2497
+ const supportedBrowsers = this.provider.getSupportedBrowsers();
2498
+ if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
2499
+ throw new Error(
2500
+ `[${this.project.getName()}] Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`
2501
+ );
2502
+ }
2503
+ const providerOptions = this.project.config.browser.providerOptions;
2504
+ await this.provider.initialize(this.project, {
2505
+ browser,
2506
+ options: providerOptions
2507
+ });
2349
2508
  }
2350
- }
2351
- let _require;
2352
- function getRequire() {
2353
- if (!_require) {
2354
- _require = createRequire(import.meta.url);
2509
+ parseErrorStacktrace(e, options = {}) {
2510
+ return parseErrorStacktrace(e, {
2511
+ ...this.stackTraceOptions,
2512
+ ...options
2513
+ });
2355
2514
  }
2356
- return _require;
2357
- }
2358
- function resolveCoverageFolder(project) {
2359
- const options = project.ctx.config;
2360
- const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
2361
- if (typeof reporter === "string") {
2362
- return reporter === "html";
2515
+ parseStacktrace(trace, options = {}) {
2516
+ return parseStacktrace(trace, {
2517
+ ...this.stackTraceOptions,
2518
+ ...options
2519
+ });
2520
+ }
2521
+ cdpSessionsPromises = /* @__PURE__ */ new Map();
2522
+ async ensureCDPHandler(contextId, sessionId) {
2523
+ const cachedHandler = this.state.cdps.get(sessionId);
2524
+ if (cachedHandler) {
2525
+ return cachedHandler;
2363
2526
  }
2364
- return reporter[0] === "html";
2365
- }) : void 0;
2366
- if (!htmlReporter) {
2367
- return void 0;
2527
+ const provider = this.provider;
2528
+ if (!provider.getCDPSession) {
2529
+ throw new Error(`CDP is not supported by the provider "${provider.name}".`);
2530
+ }
2531
+ const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
2532
+ const promise2 = provider.getCDPSession(contextId).finally(() => {
2533
+ this.cdpSessionsPromises.delete(sessionId);
2534
+ });
2535
+ this.cdpSessionsPromises.set(sessionId, promise2);
2536
+ return promise2;
2537
+ })();
2538
+ const session = await promise;
2539
+ const rpc = this.state.testers.get(sessionId);
2540
+ if (!rpc) {
2541
+ throw new Error(`Tester RPC "${sessionId}" was not established.`);
2542
+ }
2543
+ const handler = new BrowserServerCDPHandler(session, rpc);
2544
+ this.state.cdps.set(
2545
+ sessionId,
2546
+ handler
2547
+ );
2548
+ return handler;
2368
2549
  }
2369
- const root = resolve(
2370
- options.root || process.cwd(),
2371
- options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
2372
- );
2373
- const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
2374
- if (!subdir || typeof subdir !== "string") {
2375
- return [root, `/${basename(root)}/`];
2550
+ async close() {
2551
+ await this.vite.close();
2376
2552
  }
2377
- return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
2378
2553
  }
2379
- const postfixRE = /[?#].*$/;
2380
- function cleanUrl(url) {
2381
- return url.replace(postfixRE, "");
2554
+ function wrapConfig(config) {
2555
+ return {
2556
+ ...config,
2557
+ // workaround RegExp serialization
2558
+ testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : void 0
2559
+ };
2382
2560
  }
2383
2561
 
2384
2562
  const debug = createDebugger("vitest:browser:pool");
@@ -2439,8 +2617,13 @@ function createBrowserPool(ctx) {
2439
2617
  [...files2.map((f) => relative(project.config.root, f))].join(", ")
2440
2618
  );
2441
2619
  const promise = waitForTests(method, contextId, project, files2);
2442
- promises.push(promise);
2443
- orchestrator.createTesters(files2);
2620
+ const tester = orchestrator.createTesters(files2).catch((error) => {
2621
+ if (error instanceof Error && error.message.startsWith("[birpc] rpc is closed")) {
2622
+ return;
2623
+ }
2624
+ return Promise.reject(error);
2625
+ });
2626
+ promises.push(promise, tester);
2444
2627
  } else {
2445
2628
  const contextId = crypto.randomUUID();
2446
2629
  const waitPromise = waitForTests(method, contextId, project, files2);
@@ -2451,8 +2634,8 @@ function createBrowserPool(ctx) {
2451
2634
  );
2452
2635
  const url = new URL("/", origin);
2453
2636
  url.searchParams.set("contextId", contextId);
2454
- const page = provider.openPage(contextId, url.toString(), () => setBreakpoint(contextId, files2[0])).then(() => waitPromise);
2455
- promises.push(page);
2637
+ const page = provider.openPage(contextId, url.toString(), () => setBreakpoint(contextId, files2[0]));
2638
+ promises.push(page, waitPromise);
2456
2639
  }
2457
2640
  });
2458
2641
  await Promise.all(promises);
@@ -2472,6 +2655,7 @@ function createBrowserPool(ctx) {
2472
2655
  if (isCancelled) {
2473
2656
  break;
2474
2657
  }
2658
+ await project.initBrowserProvider();
2475
2659
  await executeTests(method, project, files);
2476
2660
  }
2477
2661
  };
@@ -2491,6 +2675,11 @@ function createBrowserPool(ctx) {
2491
2675
  async close() {
2492
2676
  await Promise.all([...providers].map((provider) => provider.close()));
2493
2677
  providers.clear();
2678
+ ctx.resolvedProjects.forEach((project) => {
2679
+ project.browser?.state.orchestrators.forEach((orchestrator) => {
2680
+ orchestrator.$close();
2681
+ });
2682
+ });
2494
2683
  },
2495
2684
  runTests: (files) => runWorkspaceTests("run", files),
2496
2685
  collectTests: (files) => runWorkspaceTests("collect", files)
@@ -2513,7 +2702,9 @@ Update your dependencies and make sure the versions match.`
2513
2702
  const server = new BrowserServer(project, "/");
2514
2703
  const configPath = typeof configFile === "string" ? configFile : false;
2515
2704
  const logLevel = process.env.VITEST_BROWSER_DEBUG ?? "info";
2516
- const logger = createViteLogger(logLevel);
2705
+ const logger = createViteLogger(project.logger, logLevel, {
2706
+ allowClearScreen: false
2707
+ });
2517
2708
  const vite = await createViteServer({
2518
2709
  ...project.options,
2519
2710
  // spread project config inlined in root workspace config
@@ -2556,4 +2747,4 @@ ${c.bold("[vitest]")} Vite unexpectedly reloaded a test. This may cause tests to
2556
2747
  return server;
2557
2748
  }
2558
2749
 
2559
- export { createBrowserPool, createBrowserServer };
2750
+ export { createBrowserPool, createBrowserServer, distRoot };