@vitest/browser 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,22 +1,22 @@
1
- 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.4";
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,23 @@ 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
+ };
1295
829
  async function keyboardImplementation(pressed, provider, contextId, text, selectAll2, skipRelease) {
1296
830
  if (provider instanceof PlaywrightBrowserProvider) {
1297
831
  const page = provider.getPage(contextId);
@@ -1355,1030 +889,1646 @@ async function keyboardImplementation(pressed, provider, contextId, text, select
1355
889
  }
1356
890
  }
1357
891
  }
1358
- const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
1359
- await keyboard2.perform(allRelease ? false : skipRelease);
892
+ const allRelease = keyboard2.toJSON().actions.every((action) => action.type === "keyUp");
893
+ await keyboard2.perform(allRelease ? false : skipRelease);
894
+ }
895
+ return {
896
+ pressed
897
+ };
898
+ }
899
+ function focusIframe() {
900
+ if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
901
+ window.focus();
902
+ }
903
+ }
904
+ function selectAll() {
905
+ const element = document.activeElement;
906
+ if (element && element.select) {
907
+ element.select();
908
+ }
909
+ }
910
+
911
+ const screenshot = async (context, name, options = {}) => {
912
+ if (!context.testPath) {
913
+ throw new Error(`Cannot take a screenshot without a test path`);
914
+ }
915
+ const path = options.path ? resolve(dirname(context.testPath), options.path) : resolveScreenshotPath(
916
+ context.testPath,
917
+ name,
918
+ context.project.config
919
+ );
920
+ const savePath = normalize$1(path);
921
+ await mkdir(dirname(path), { recursive: true });
922
+ if (context.provider instanceof PlaywrightBrowserProvider) {
923
+ if (options.element) {
924
+ const { element: selector, ...config } = options;
925
+ const element = context.iframe.locator(`${selector}`);
926
+ const buffer2 = await element.screenshot({
927
+ timeout: 1e3,
928
+ ...config,
929
+ path: savePath
930
+ });
931
+ return returnResult(options, path, buffer2);
932
+ }
933
+ const buffer = await context.iframe.locator("body").screenshot({
934
+ ...options,
935
+ path: savePath
936
+ });
937
+ return returnResult(options, path, buffer);
938
+ }
939
+ if (context.provider instanceof WebdriverBrowserProvider) {
940
+ const page = context.provider.browser;
941
+ if (!options.element) {
942
+ const body = await page.$("body");
943
+ const buffer2 = await body.saveScreenshot(savePath);
944
+ return returnResult(options, path, buffer2);
945
+ }
946
+ const element = await page.$(`${options.element}`);
947
+ const buffer = await element.saveScreenshot(savePath);
948
+ return returnResult(options, path, buffer);
949
+ }
950
+ throw new Error(
951
+ `Provider "${context.provider.name}" does not support screenshots`
952
+ );
953
+ };
954
+ function resolveScreenshotPath(testPath, name, config) {
955
+ const dir = dirname(testPath);
956
+ const base = basename(testPath);
957
+ if (config.browser.screenshotDirectory) {
958
+ return resolve(
959
+ config.browser.screenshotDirectory,
960
+ relative(config.root, dir),
961
+ base,
962
+ name
963
+ );
964
+ }
965
+ return resolve(dir, "__screenshots__", base, name);
966
+ }
967
+ function returnResult(options, path, buffer) {
968
+ if (options.base64) {
969
+ return { path, base64: buffer.toString("base64") };
970
+ }
971
+ return path;
972
+ }
973
+
974
+ const selectOptions = async (context, selector, userValues, options = {}) => {
975
+ if (context.provider instanceof PlaywrightBrowserProvider) {
976
+ const value = userValues;
977
+ const { iframe } = context;
978
+ const selectElement = iframe.locator(selector);
979
+ const values = await Promise.all(value.map(async (v) => {
980
+ if (typeof v === "string") {
981
+ return v;
982
+ }
983
+ const elementHandler = await iframe.locator(v.element).elementHandle();
984
+ if (!elementHandler) {
985
+ throw new Error(`Element not found: ${v.element}`);
986
+ }
987
+ return elementHandler;
988
+ }));
989
+ await selectElement.selectOption(values, {
990
+ timeout: 1e3,
991
+ ...options
992
+ });
993
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
994
+ const values = userValues;
995
+ if (!values.length) {
996
+ return;
997
+ }
998
+ const browser = context.browser;
999
+ if (values.length === 1 && "index" in values[0]) {
1000
+ const selectElement = browser.$(selector);
1001
+ await selectElement.selectByIndex(values[0].index);
1002
+ } else {
1003
+ throw new Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);
1004
+ }
1005
+ } else {
1006
+ throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
1007
+ }
1008
+ };
1009
+
1010
+ const tab = async (context, options = {}) => {
1011
+ const provider = context.provider;
1012
+ if (provider instanceof PlaywrightBrowserProvider) {
1013
+ const page = context.page;
1014
+ await page.keyboard.press(options.shift === true ? "Shift+Tab" : "Tab");
1015
+ return;
1016
+ }
1017
+ if (provider instanceof WebdriverBrowserProvider) {
1018
+ const { Key } = await import('webdriverio');
1019
+ const browser = context.browser;
1020
+ await browser.keys(options.shift === true ? [Key.Shift, Key.Tab] : [Key.Tab]);
1021
+ return;
1022
+ }
1023
+ throw new Error(`Provider "${provider.name}" doesn't support tab command`);
1024
+ };
1025
+
1026
+ const type = async (context, selector, text, options = {}) => {
1027
+ const { skipClick = false, skipAutoClose = false } = options;
1028
+ const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
1029
+ if (context.provider instanceof PlaywrightBrowserProvider) {
1030
+ const { iframe } = context;
1031
+ const element = iframe.locator(selector);
1032
+ if (!skipClick) {
1033
+ await element.focus();
1034
+ }
1035
+ await keyboardImplementation(
1036
+ unreleased,
1037
+ context.provider,
1038
+ context.contextId,
1039
+ text,
1040
+ () => element.selectText(),
1041
+ skipAutoClose
1042
+ );
1043
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
1044
+ const browser = context.browser;
1045
+ const element = browser.$(selector);
1046
+ if (!skipClick && !await element.isFocused()) {
1047
+ await element.click();
1048
+ }
1049
+ await keyboardImplementation(
1050
+ unreleased,
1051
+ context.provider,
1052
+ context.contextId,
1053
+ text,
1054
+ () => browser.execute(() => {
1055
+ const element2 = document.activeElement;
1056
+ if (element2) {
1057
+ element2.select();
1058
+ }
1059
+ }),
1060
+ skipAutoClose
1061
+ );
1062
+ } else {
1063
+ throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
1064
+ }
1065
+ return {
1066
+ unreleased: Array.from(unreleased)
1067
+ };
1068
+ };
1069
+
1070
+ const upload = async (context, selector, files) => {
1071
+ const testPath = context.testPath;
1072
+ if (!testPath) {
1073
+ throw new Error(`Cannot upload files outside of a test`);
1074
+ }
1075
+ const testDir = dirname(testPath);
1076
+ if (context.provider instanceof PlaywrightBrowserProvider) {
1077
+ const { iframe } = context;
1078
+ const playwrightFiles = files.map((file) => {
1079
+ if (typeof file === "string") {
1080
+ return resolve(testDir, file);
1081
+ }
1082
+ return {
1083
+ name: file.name,
1084
+ mimeType: file.mimeType,
1085
+ buffer: Buffer.from(file.base64, "base64")
1086
+ };
1087
+ });
1088
+ await iframe.locator(selector).setInputFiles(playwrightFiles);
1089
+ } else if (context.provider instanceof WebdriverBrowserProvider) {
1090
+ for (const file of files) {
1091
+ if (typeof file !== "string") {
1092
+ throw new TypeError(`The "${context.provider.name}" provider doesn't support uploading files objects. Provide a file path instead.`);
1093
+ }
1094
+ }
1095
+ const element = context.browser.$(selector);
1096
+ for (const file of files) {
1097
+ const filepath = resolve(testDir, file);
1098
+ const remoteFilePath = await context.browser.uploadFile(filepath);
1099
+ await element.addValue(remoteFilePath);
1100
+ }
1101
+ } else {
1102
+ throw new TypeError(`Provider "${context.provider.name}" does not support uploading files via userEvent.upload`);
1103
+ }
1104
+ };
1105
+
1106
+ var builtinCommands = {
1107
+ readFile,
1108
+ removeFile,
1109
+ writeFile,
1110
+ __vitest_fileInfo: _fileInfo,
1111
+ __vitest_upload: upload,
1112
+ __vitest_click: click,
1113
+ __vitest_dblClick: dblClick,
1114
+ __vitest_tripleClick: tripleClick,
1115
+ __vitest_screenshot: screenshot,
1116
+ __vitest_type: type,
1117
+ __vitest_clear: clear,
1118
+ __vitest_fill: fill,
1119
+ __vitest_tab: tab,
1120
+ __vitest_keyboard: keyboard,
1121
+ __vitest_selectOptions: selectOptions,
1122
+ __vitest_dragAndDrop: dragAndDrop,
1123
+ __vitest_hover: hover,
1124
+ __vitest_cleanup: keyboardCleanup
1125
+ };
1126
+
1127
+ const VIRTUAL_ID_CONTEXT = "\0@vitest/browser/context";
1128
+ const ID_CONTEXT = "@vitest/browser/context";
1129
+ const __dirname = dirname(fileURLToPath(import.meta.url));
1130
+ function BrowserContext(server) {
1131
+ const project = server.project;
1132
+ project.config.browser.commands ??= {};
1133
+ for (const [name, command] of Object.entries(builtinCommands)) {
1134
+ project.config.browser.commands[name] ??= command;
1135
+ }
1136
+ for (const command in project.config.browser.commands) {
1137
+ if (!/^[a-z_$][\w$]*$/i.test(command)) {
1138
+ throw new Error(
1139
+ `Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`
1140
+ );
1141
+ }
1360
1142
  }
1361
1143
  return {
1362
- pressed
1144
+ name: "vitest:browser:virtual-module:context",
1145
+ enforce: "pre",
1146
+ resolveId(id) {
1147
+ if (id === ID_CONTEXT) {
1148
+ return VIRTUAL_ID_CONTEXT;
1149
+ }
1150
+ },
1151
+ load(id) {
1152
+ if (id === VIRTUAL_ID_CONTEXT) {
1153
+ return generateContextFile.call(this, server);
1154
+ }
1155
+ }
1363
1156
  };
1364
1157
  }
1365
- function focusIframe() {
1366
- if (!document.activeElement || document.activeElement.ownerDocument !== document || document.activeElement === document.body) {
1367
- window.focus();
1368
- }
1158
+ async function generateContextFile(server) {
1159
+ const commands = Object.keys(server.project.config.browser.commands ?? {});
1160
+ const filepathCode = "__vitest_worker__.filepath || __vitest_worker__.current?.file?.filepath || undefined";
1161
+ const provider = server.provider;
1162
+ const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
1163
+ return ` ["${command}"]: (...args) => rpc().triggerCommand(contextId, "${command}", filepath(), args),`;
1164
+ }).join("\n");
1165
+ const userEventNonProviderImport = await getUserEventImport(
1166
+ provider,
1167
+ this.resolve.bind(this)
1168
+ );
1169
+ const distContextPath = slash(`/@fs/${resolve(__dirname, "context.js")}`);
1170
+ return `
1171
+ import { page, createUserEvent, cdp } from '${distContextPath}'
1172
+ ${userEventNonProviderImport}
1173
+ const filepath = () => ${filepathCode}
1174
+ const rpc = () => __vitest_worker__.rpc
1175
+ const contextId = __vitest_browser_runner__.contextId
1176
+
1177
+ export const server = {
1178
+ platform: ${JSON.stringify(process.platform)},
1179
+ version: ${JSON.stringify(process.version)},
1180
+ provider: ${JSON.stringify(provider.name)},
1181
+ browser: ${JSON.stringify(server.project.config.browser.name)},
1182
+ commands: {
1183
+ ${commandsCode}
1184
+ },
1185
+ config: __vitest_browser_runner__.config,
1369
1186
  }
1370
- function selectAll() {
1371
- const element = document.activeElement;
1372
- if (element && element.select) {
1373
- element.select();
1187
+ export const commands = server.commands
1188
+ export const userEvent = createUserEvent(_userEventSetup)
1189
+ export { page, cdp }
1190
+ `;
1191
+ }
1192
+ async function getUserEventImport(provider, resolve2) {
1193
+ if (provider.name !== "preview") {
1194
+ return "const _userEventSetup = undefined";
1195
+ }
1196
+ const resolved = await resolve2("@testing-library/user-event", __dirname);
1197
+ if (!resolved) {
1198
+ throw new Error(`Failed to resolve user-event package from ${__dirname}`);
1374
1199
  }
1200
+ return `import { userEvent as __vitest_user_event__ } from '${slash(`/@fs/${resolved.id}`)}'
1201
+ const _userEventSetup = __vitest_user_event__
1202
+ `;
1375
1203
  }
1376
1204
 
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
1205
+ function replacer(code, values) {
1206
+ return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
1207
+ }
1208
+ const builtinProviders = ["webdriverio", "playwright", "preview"];
1209
+ async function getBrowserProvider(options, project) {
1210
+ if (options.provider == null || builtinProviders.includes(options.provider)) {
1211
+ const providers = await import('./providers.js');
1212
+ const provider = options.provider || "preview";
1213
+ return providers[provider];
1214
+ }
1215
+ let customProviderModule;
1216
+ try {
1217
+ customProviderModule = await project.runner.executeId(
1218
+ options.provider
1393
1219
  );
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
1220
+ } catch (error) {
1221
+ throw new Error(
1222
+ `Failed to load custom BrowserProvider from ${options.provider}`,
1223
+ { cause: error }
1412
1224
  );
1413
- } else {
1414
- throw new TypeError(`Provider "${context.provider.name}" does not support typing`);
1415
1225
  }
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`);
1226
+ if (customProviderModule.default == null) {
1227
+ throw new Error(
1228
+ `Custom BrowserProvider loaded from ${options.provider} was not the default export`
1229
+ );
1434
1230
  }
1435
- };
1231
+ return customProviderModule.default;
1232
+ }
1436
1233
 
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`);
1234
+ async function resolveOrchestrator(server, url, res) {
1235
+ const project = server.project;
1236
+ let contextId = url.searchParams.get("contextId");
1237
+ if (!contextId) {
1238
+ const contexts = [...server.state.orchestrators.keys()];
1239
+ contextId = contexts[contexts.length - 1] ?? "none";
1447
1240
  }
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;
1241
+ const files = server.state.getContext(contextId)?.files ?? [];
1242
+ const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1243
+ const injector = replacer(injectorJs, {
1244
+ __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1245
+ __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1246
+ __VITEST_VITE_CONFIG__: JSON.stringify({
1247
+ root: server.vite.config.root
1248
+ }),
1249
+ __VITEST_FILES__: JSON.stringify(files),
1250
+ __VITEST_TYPE__: '"orchestrator"',
1251
+ __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1252
+ __VITEST_TESTER_ID__: '"none"',
1253
+ __VITEST_PROVIDED_CONTEXT__: "{}"
1254
+ });
1255
+ res.removeHeader("Content-Security-Policy");
1256
+ if (!server.orchestratorScripts) {
1257
+ server.orchestratorScripts = (await server.formatScripts(
1258
+ project.config.browser.orchestratorScripts
1259
+ )).map((script) => {
1260
+ let html = "<script ";
1261
+ for (const attr in script.attrs || {}) {
1262
+ html += `${attr}="${script.attrs[attr]}" `;
1458
1263
  }
1459
- const elementHandler = await iframe.locator(v.element).elementHandle();
1460
- if (!elementHandler) {
1461
- throw new Error(`Element not found: ${v.element}`);
1264
+ html += `>${script.children}<\/script>`;
1265
+ return html;
1266
+ }).join("\n");
1267
+ }
1268
+ let baseHtml = typeof server.orchestratorHtml === "string" ? server.orchestratorHtml : await server.orchestratorHtml;
1269
+ if (project.config.browser.ui) {
1270
+ const manifestContent = server.manifest instanceof Promise ? await server.manifest : server.manifest;
1271
+ const jsEntry = manifestContent["orchestrator.html"].file;
1272
+ const base = server.vite.config.base || "/";
1273
+ baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
1274
+ "<!-- !LOAD_METADATA! -->",
1275
+ [
1276
+ "{__VITEST_INJECTOR__}",
1277
+ "{__VITEST_ERROR_CATCHER__}",
1278
+ "{__VITEST_SCRIPTS__}",
1279
+ `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
1280
+ ].join("\n")
1281
+ );
1282
+ }
1283
+ return replacer(baseHtml, {
1284
+ __VITEST_FAVICON__: server.faviconUrl,
1285
+ __VITEST_TITLE__: "Vitest Browser Runner",
1286
+ __VITEST_SCRIPTS__: server.orchestratorScripts,
1287
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
1288
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherUrl}"><\/script>`,
1289
+ __VITEST_CONTEXT_ID__: JSON.stringify(contextId)
1290
+ });
1291
+ }
1292
+
1293
+ /// <reference types="../types/index.d.ts" />
1294
+
1295
+ // (c) 2020-present Andrea Giammarchi
1296
+
1297
+ const {parse: $parse, stringify: $stringify} = JSON;
1298
+ const {keys} = Object;
1299
+
1300
+ const Primitive = String; // it could be Number
1301
+ const primitive = 'string'; // it could be 'number'
1302
+
1303
+ const ignore = {};
1304
+ const object = 'object';
1305
+
1306
+ const noop = (_, value) => value;
1307
+
1308
+ const primitives = value => (
1309
+ value instanceof Primitive ? Primitive(value) : value
1310
+ );
1311
+
1312
+ const Primitives = (_, value) => (
1313
+ typeof value === primitive ? new Primitive(value) : value
1314
+ );
1315
+
1316
+ const revive = (input, parsed, output, $) => {
1317
+ const lazy = [];
1318
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
1319
+ const k = ke[y];
1320
+ const value = output[k];
1321
+ if (value instanceof Primitive) {
1322
+ const tmp = input[value];
1323
+ if (typeof tmp === object && !parsed.has(tmp)) {
1324
+ parsed.add(tmp);
1325
+ output[k] = ignore;
1326
+ lazy.push({k, a: [input, parsed, tmp, $]});
1462
1327
  }
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`);
1328
+ else
1329
+ output[k] = $.call(output, k, tmp);
1480
1330
  }
1481
- } else {
1482
- throw new TypeError(`Provider "${context.provider.name}" doesn't support selectOptions command`);
1331
+ else if (output[k] !== ignore)
1332
+ output[k] = $.call(output, k, value);
1333
+ }
1334
+ for (let {length} = lazy, i = 0; i < length; i++) {
1335
+ const {k, a} = lazy[i];
1336
+ output[k] = $.call(output, k, revive.apply(null, a));
1483
1337
  }
1338
+ return output;
1484
1339
  };
1485
1340
 
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;
1341
+ const set = (known, input, value) => {
1342
+ const index = Primitive(input.push(value) - 1);
1343
+ known.set(value, index);
1344
+ return index;
1345
+ };
1346
+
1347
+ /**
1348
+ * Converts a specialized flatted string into a JS value.
1349
+ * @param {string} text
1350
+ * @param {(this: any, key: string, value: any) => any} [reviver]
1351
+ * @returns {any}
1352
+ */
1353
+ const parse = (text, reviver) => {
1354
+ const input = $parse(text, Primitives).map(primitives);
1355
+ const value = input[0];
1356
+ const $ = reviver || noop;
1357
+ const tmp = typeof value === object && value ?
1358
+ revive(input, new Set, value, $) :
1359
+ value;
1360
+ return $.call({'': tmp}, '', tmp);
1361
+ };
1362
+
1363
+ /**
1364
+ * Converts a JS value into a specialized flatted string.
1365
+ * @param {any} value
1366
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
1367
+ * @param {string | number | undefined} [space]
1368
+ * @returns {string}
1369
+ */
1370
+ const stringify = (value, replacer, space) => {
1371
+ const $ = replacer && typeof replacer === object ?
1372
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
1373
+ (replacer || noop);
1374
+ const known = new Map;
1375
+ const input = [];
1376
+ const output = [];
1377
+ let i = +set(known, input, $.call({'': value}, '', value));
1378
+ let firstRun = !i;
1379
+ while (i < input.length) {
1380
+ firstRun = true;
1381
+ output[i] = $stringify(input[i++], replace, space);
1492
1382
  }
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;
1383
+ return '[' + output.join(',') + ']';
1384
+ function replace(key, value) {
1385
+ if (firstRun) {
1386
+ firstRun = !firstRun;
1387
+ return value;
1388
+ }
1389
+ const after = $.call(this, key, value);
1390
+ switch (typeof after) {
1391
+ case object:
1392
+ if (after === null) return after;
1393
+ case primitive:
1394
+ return known.get(after) || set(known, input, after);
1395
+ }
1396
+ return after;
1498
1397
  }
1499
- throw new Error(`Provider "${provider.name}" doesn't support tab command`);
1500
1398
  };
1501
1399
 
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
- }
1400
+ async function resolveTester(server, url, res, next) {
1401
+ const csp = res.getHeader("Content-Security-Policy");
1402
+ if (typeof csp === "string") {
1403
+ res.setHeader(
1404
+ "Content-Security-Policy",
1405
+ csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *")
1512
1406
  );
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
1407
  }
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
1408
+ const { contextId, testFile } = server.resolveTesterUrl(url.pathname);
1409
+ const project = server.project;
1410
+ const state = server.state;
1411
+ const { testFiles } = await project.globTestFiles();
1412
+ const tests = testFile === "__vitest_all__" || !testFiles.includes(testFile) ? "__vitest_browser_runner__.files" : JSON.stringify([testFile]);
1413
+ const iframeId = JSON.stringify(testFile);
1414
+ const context = state.getContext(contextId);
1415
+ const files = context?.files ?? [];
1416
+ const method = context?.method ?? "run";
1417
+ const injectorJs = typeof server.injectorJs === "string" ? server.injectorJs : await server.injectorJs;
1418
+ const injector = replacer(injectorJs, {
1419
+ __VITEST_PROVIDER__: JSON.stringify(server.provider.name),
1420
+ __VITEST_CONFIG__: JSON.stringify(server.getSerializableConfig()),
1421
+ __VITEST_FILES__: JSON.stringify(files),
1422
+ __VITEST_VITE_CONFIG__: JSON.stringify({
1423
+ root: server.vite.config.root
1424
+ }),
1425
+ __VITEST_TYPE__: '"tester"',
1426
+ __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1427
+ __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
1428
+ __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
1429
+ });
1430
+ const testerHtml = typeof server.testerHtml === "string" ? server.testerHtml : await server.testerHtml;
1431
+ try {
1432
+ const indexhtml = await server.vite.transformIndexHtml(url.pathname, testerHtml);
1433
+ return replacer(indexhtml, {
1434
+ __VITEST_FAVICON__: server.faviconUrl,
1435
+ __VITEST_INJECTOR__: injector,
1436
+ __VITEST_APPEND__: `
1437
+ __vitest_browser_runner__.runningFiles = ${tests}
1438
+ __vitest_browser_runner__.iframeId = ${iframeId}
1439
+ __vitest_browser_runner__.${method === "run" ? "runTests" : "collectTests"}(__vitest_browser_runner__.runningFiles)
1440
+ document.querySelector('script[data-vitest-append]').remove()
1441
+ `
1529
1442
  });
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`);
1443
+ } catch (err) {
1444
+ context?.reject(err);
1445
+ next(err);
1535
1446
  }
1536
- };
1447
+ }
1537
1448
 
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
- };
1449
+ const versionRegexp = /(?:\?|&)v=\w{8}/;
1450
+ var BrowserPlugin = (browserServer, base = "/") => {
1451
+ const project = browserServer.project;
1452
+ function isPackageExists(pkg, root) {
1453
+ return browserServer.project.ctx.packageInstaller.isPackageExists?.(pkg, {
1454
+ paths: [root]
1555
1455
  });
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.`);
1456
+ }
1457
+ return [
1458
+ {
1459
+ enforce: "pre",
1460
+ name: "vitest:browser",
1461
+ async configureServer(server) {
1462
+ browserServer.setServer(server);
1463
+ server.middlewares.use(function vitestHeaders(_req, res, next) {
1464
+ const headers = server.config.server.headers;
1465
+ if (headers) {
1466
+ for (const name in headers) {
1467
+ res.setHeader(name, headers[name]);
1468
+ }
1469
+ }
1470
+ next();
1471
+ });
1472
+ server.middlewares.use(async function vitestBrowserMode(req, res, next) {
1473
+ if (!req.url || !browserServer.provider) {
1474
+ return next();
1475
+ }
1476
+ const url = new URL(req.url, "http://localhost");
1477
+ if (!url.pathname.startsWith(browserServer.prefixTesterUrl) && url.pathname !== base) {
1478
+ return next();
1479
+ }
1480
+ res.setHeader(
1481
+ "Cache-Control",
1482
+ "no-cache, max-age=0, must-revalidate"
1483
+ );
1484
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1485
+ res.removeHeader("X-Frame-Options");
1486
+ if (url.pathname === base) {
1487
+ const html2 = await resolveOrchestrator(browserServer, url, res);
1488
+ res.write(html2, "utf-8");
1489
+ res.end();
1490
+ return;
1491
+ }
1492
+ const html = await resolveTester(browserServer, url, res, next);
1493
+ if (html) {
1494
+ res.write(html, "utf-8");
1495
+ res.end();
1496
+ }
1497
+ });
1498
+ server.middlewares.use(
1499
+ `${base}favicon.svg`,
1500
+ (_, res) => {
1501
+ const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
1502
+ res.write(content, "utf-8");
1503
+ res.end();
1504
+ }
1505
+ );
1506
+ const coverageFolder = resolveCoverageFolder(project);
1507
+ const coveragePath = coverageFolder ? coverageFolder[1] : void 0;
1508
+ if (coveragePath && base === coveragePath) {
1509
+ throw new Error(
1510
+ `The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`
1511
+ );
1512
+ }
1513
+ if (coverageFolder) {
1514
+ server.middlewares.use(
1515
+ coveragePath,
1516
+ sirv(coverageFolder[0], {
1517
+ single: true,
1518
+ dev: true,
1519
+ setHeaders: (res) => {
1520
+ res.setHeader(
1521
+ "Cache-Control",
1522
+ "public,max-age=0,must-revalidate"
1523
+ );
1524
+ }
1525
+ })
1526
+ );
1527
+ }
1528
+ const screenshotFailures = project.config.browser.ui && project.config.browser.screenshotFailures;
1529
+ if (screenshotFailures) {
1530
+ server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
1531
+ if (!req.url || !browserServer.provider) {
1532
+ res.statusCode = 404;
1533
+ res.end();
1534
+ return;
1535
+ }
1536
+ const url = new URL(req.url, "http://localhost");
1537
+ const file = url.searchParams.get("file");
1538
+ if (!file) {
1539
+ res.statusCode = 404;
1540
+ res.end();
1541
+ return;
1542
+ }
1543
+ let stat;
1544
+ try {
1545
+ stat = lstatSync(file);
1546
+ } catch {
1547
+ }
1548
+ if (!stat?.isFile()) {
1549
+ res.statusCode = 404;
1550
+ res.end();
1551
+ return;
1552
+ }
1553
+ const ext = extname(file);
1554
+ const buffer = readFileSync(file);
1555
+ res.setHeader(
1556
+ "Cache-Control",
1557
+ "public,max-age=0,must-revalidate"
1558
+ );
1559
+ res.setHeader("Content-Length", buffer.length);
1560
+ res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
1561
+ res.end(buffer);
1562
+ });
1563
+ }
1564
+ server.middlewares.use((req, res, next) => {
1565
+ if (req.url && versionRegexp.test(req.url) && !req.url.includes("chunk-")) {
1566
+ res.setHeader("Cache-Control", "no-cache");
1567
+ const setHeader = res.setHeader.bind(res);
1568
+ res.setHeader = function(name, value) {
1569
+ if (name === "Cache-Control") {
1570
+ return res;
1571
+ }
1572
+ return setHeader(name, value);
1573
+ };
1574
+ }
1575
+ next();
1576
+ });
1561
1577
  }
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);
1578
+ },
1579
+ {
1580
+ name: "vitest:browser:tests",
1581
+ enforce: "pre",
1582
+ async config() {
1583
+ const { testFiles: allTestFiles } = await project.globTestFiles();
1584
+ const browserTestFiles = allTestFiles.filter(
1585
+ (file) => getFilePoolName(project, file) === "browser"
1586
+ );
1587
+ const setupFiles = toArray(project.config.setupFiles);
1588
+ const define = {};
1589
+ for (const env in project.config.env || {}) {
1590
+ const stringValue = JSON.stringify(project.config.env[env]);
1591
+ define[`import.meta.env.${env}`] = stringValue;
1590
1592
  }
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());
1593
+ const entries = [
1594
+ ...browserTestFiles,
1595
+ ...setupFiles,
1596
+ resolve(distDir, "index.js"),
1597
+ resolve(distDir, "browser.js"),
1598
+ resolve(distDir, "runners.js"),
1599
+ resolve(distDir, "utils.js"),
1600
+ ...project.config.snapshotSerializers || []
1601
+ ];
1602
+ const exclude = [
1603
+ "vitest",
1604
+ "vitest/utils",
1605
+ "vitest/browser",
1606
+ "vitest/runners",
1607
+ "@vitest/browser",
1608
+ "@vitest/browser/client",
1609
+ "@vitest/utils",
1610
+ "@vitest/utils/source-map",
1611
+ "@vitest/runner",
1612
+ "@vitest/spy",
1613
+ "@vitest/utils/error",
1614
+ "@vitest/snapshot",
1615
+ "@vitest/expect",
1616
+ "std-env",
1617
+ "tinybench",
1618
+ "tinyspy",
1619
+ "tinyrainbow",
1620
+ "pathe",
1621
+ "msw",
1622
+ "msw/browser"
1623
+ ];
1624
+ if (project.config.diff) {
1625
+ entries.push(project.config.diff);
1626
+ }
1627
+ if (project.ctx.coverageProvider) {
1628
+ const coverage = project.ctx.config.coverage;
1629
+ const provider = coverage.provider;
1630
+ if (provider === "v8") {
1631
+ const path = tryResolve("@vitest/coverage-v8", [project.config.root]);
1632
+ if (path) {
1633
+ entries.push(path);
1634
+ exclude.push("@vitest/coverage-v8/browser");
1598
1635
  }
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);
1636
+ } else if (provider === "istanbul") {
1637
+ const path = tryResolve("@vitest/coverage-istanbul", [project.config.root]);
1638
+ if (path) {
1639
+ entries.push(path);
1640
+ exclude.push("@vitest/coverage-istanbul");
1616
1641
  }
1642
+ } else if (provider === "custom" && coverage.customProviderModule) {
1643
+ entries.push(coverage.customProviderModule);
1644
+ }
1617
1645
  }
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');
1646
+ const include = [
1647
+ "vitest > expect-type",
1648
+ "vitest > @vitest/snapshot > magic-string",
1649
+ "vitest > chai",
1650
+ "vitest > chai > loupe",
1651
+ "vitest > @vitest/utils > loupe",
1652
+ "@vitest/browser > @testing-library/user-event",
1653
+ "@vitest/browser > @testing-library/dom"
1654
+ ];
1655
+ const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root;
1656
+ const svelte = isPackageExists("vitest-browser-svelte", fileRoot);
1657
+ if (svelte) {
1658
+ exclude.push("vitest-browser-svelte");
1659
+ }
1660
+ const vueTestUtils = isPackageExists("@vue/test-utils", fileRoot);
1661
+ if (vueTestUtils) {
1662
+ include.push("@vue/test-utils");
1663
+ }
1664
+ return {
1665
+ define,
1666
+ resolve: {
1667
+ dedupe: ["vitest"]
1668
+ },
1669
+ optimizeDeps: {
1670
+ entries,
1671
+ exclude,
1672
+ include
1673
+ }
1674
+ };
1675
+ },
1676
+ async resolveId(id) {
1677
+ if (!/\?browserv=\w+$/.test(id)) {
1678
+ return;
1679
+ }
1680
+ let useId = id.slice(0, id.lastIndexOf("?"));
1681
+ if (useId.startsWith("/@fs/")) {
1682
+ useId = useId.slice(5);
1683
+ }
1684
+ if (/^\w:/.test(useId)) {
1685
+ useId = useId.replace(/\\/g, "/");
1686
+ }
1687
+ return useId;
1688
+ }
1689
+ },
1690
+ {
1691
+ name: "vitest:browser:resolve-virtual",
1692
+ async resolveId(rawId) {
1693
+ if (rawId === "/mockServiceWorker.js") {
1694
+ return this.resolve("msw/mockServiceWorker.js", distRoot, {
1695
+ skipSelf: true
1696
+ });
1697
+ }
1698
+ }
1699
+ },
1700
+ {
1701
+ name: "vitest:browser:assets",
1702
+ configureServer(server) {
1703
+ server.middlewares.use(
1704
+ "/__vitest__",
1705
+ sirv(resolve(distRoot, "client/__vitest__"))
1706
+ );
1707
+ },
1708
+ resolveId(id) {
1709
+ if (id.startsWith("/__vitest_browser__/")) {
1710
+ return resolve(distRoot, "client", id.slice(1));
1711
+ }
1712
+ },
1713
+ transform(code, id) {
1714
+ if (id.includes(browserServer.vite.config.cacheDir) && id.includes("loupe.js")) {
1715
+ const utilRequire = "nodeUtil = require_util();";
1716
+ return code.replace(utilRequire, " ".repeat(utilRequire.length));
1717
+ }
1718
+ }
1719
+ },
1720
+ BrowserContext(browserServer),
1721
+ dynamicImportPlugin({
1722
+ globalThisAccessor: '"__vitest_browser_runner__"',
1723
+ filter(id) {
1724
+ if (id.includes(distRoot)) {
1725
+ return false;
1726
+ }
1727
+ return true;
1728
+ }
1729
+ }),
1730
+ {
1731
+ name: "vitest:browser:config",
1732
+ enforce: "post",
1733
+ async config(viteConfig) {
1734
+ // Enables using ignore hint for coverage providers with @preserve keyword
1735
+ if (viteConfig.esbuild !== false) {
1736
+ viteConfig.esbuild ||= {};
1737
+ viteConfig.esbuild.legalComments = "inline";
1738
+ }
1739
+ const defaultPort = project.ctx._browserLastPort++;
1740
+ const api = resolveApiServerConfig(
1741
+ viteConfig.test?.browser || {},
1742
+ defaultPort
1743
+ );
1744
+ viteConfig.server = {
1745
+ ...viteConfig.server,
1746
+ port: defaultPort,
1747
+ ...api,
1748
+ middlewareMode: false,
1749
+ open: false
1645
1750
  };
1646
- Object.freeze(this);
1647
- for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
1648
- Object.freeze(extensions);
1751
+ viteConfig.server.fs ??= {};
1752
+ viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
1753
+ viteConfig.server.fs.allow.push(
1754
+ ...resolveFsAllow(
1755
+ project.ctx.config.root,
1756
+ project.ctx.server.config.configFile
1757
+ ),
1758
+ distRoot
1759
+ );
1760
+ return {
1761
+ resolve: {
1762
+ alias: viteConfig.test?.alias
1763
+ }
1764
+ };
1765
+ }
1766
+ },
1767
+ {
1768
+ name: "vitest:browser:in-source-tests",
1769
+ transform(code, id) {
1770
+ if (!project.isTestFile(id) || !code.includes("import.meta.vitest")) {
1771
+ return;
1649
1772
  }
1650
- return this;
1651
- }
1652
- _getTestState() {
1773
+ const s = new MagicString(code, { filename: cleanUrl(id) });
1774
+ s.prepend(
1775
+ `import.meta.vitest = __vitest_index__;
1776
+ `
1777
+ );
1653
1778
  return {
1654
- types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
1655
- extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f"),
1779
+ code: s.toString(),
1780
+ map: s.generateMap({ hires: true })
1656
1781
  };
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
1782
  }
1809
1783
  },
1810
- load(id) {
1811
- if (id === VIRTUAL_ID_CONTEXT) {
1812
- return generateContextFile.call(this, server);
1784
+ {
1785
+ name: "vitest:browser:worker",
1786
+ transform(code, id, _options) {
1787
+ if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
1788
+ const s = new MagicString(code);
1789
+ s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
1790
+ return {
1791
+ code: s.toString(),
1792
+ map: s.generateMap({ hires: "boundary" })
1793
+ };
1794
+ }
1795
+ }
1796
+ },
1797
+ {
1798
+ name: "vitest:browser:transform-tester-html",
1799
+ enforce: "pre",
1800
+ async transformIndexHtml(html, ctx) {
1801
+ if (!ctx.path.startsWith(browserServer.prefixTesterUrl)) {
1802
+ return;
1803
+ }
1804
+ if (!browserServer.testerScripts) {
1805
+ const testerScripts2 = await browserServer.formatScripts(
1806
+ project.config.browser.testerScripts
1807
+ );
1808
+ browserServer.testerScripts = testerScripts2;
1809
+ }
1810
+ const stateJs = typeof browserServer.stateJs === "string" ? browserServer.stateJs : await browserServer.stateJs;
1811
+ const testerScripts = [];
1812
+ if (resolve(distRoot, "client/tester/tester.html") !== browserServer.testerFilepath) {
1813
+ const manifestContent = browserServer.manifest instanceof Promise ? await browserServer.manifest : browserServer.manifest;
1814
+ const testerEntry = manifestContent["tester/tester.html"];
1815
+ testerScripts.push({
1816
+ tag: "script",
1817
+ attrs: {
1818
+ type: "module",
1819
+ crossorigin: "",
1820
+ src: `${browserServer.base}${testerEntry.file}`
1821
+ },
1822
+ injectTo: "head"
1823
+ });
1824
+ for (const importName of testerEntry.imports || []) {
1825
+ const entryManifest = manifestContent[importName];
1826
+ if (entryManifest) {
1827
+ testerScripts.push(
1828
+ {
1829
+ tag: "link",
1830
+ attrs: {
1831
+ href: `${browserServer.base}${entryManifest.file}`,
1832
+ rel: "modulepreload",
1833
+ crossorigin: ""
1834
+ },
1835
+ injectTo: "head"
1836
+ }
1837
+ );
1838
+ }
1839
+ }
1840
+ }
1841
+ return [
1842
+ {
1843
+ tag: "script",
1844
+ children: "{__VITEST_INJECTOR__}",
1845
+ injectTo: "head-prepend"
1846
+ },
1847
+ {
1848
+ tag: "script",
1849
+ children: stateJs,
1850
+ injectTo: "head-prepend"
1851
+ },
1852
+ {
1853
+ tag: "script",
1854
+ attrs: {
1855
+ type: "module",
1856
+ src: browserServer.errorCatcherUrl
1857
+ },
1858
+ injectTo: "head"
1859
+ },
1860
+ browserServer.locatorsUrl ? {
1861
+ tag: "script",
1862
+ attrs: {
1863
+ type: "module",
1864
+ src: browserServer.locatorsUrl
1865
+ },
1866
+ injectTo: "head"
1867
+ } : null,
1868
+ ...browserServer.testerScripts,
1869
+ ...testerScripts,
1870
+ {
1871
+ tag: "script",
1872
+ attrs: {
1873
+ "type": "module",
1874
+ "data-vitest-append": ""
1875
+ },
1876
+ children: "{__VITEST_APPEND__}",
1877
+ injectTo: "body"
1878
+ }
1879
+ ].filter((s) => s != null);
1880
+ }
1881
+ },
1882
+ {
1883
+ name: "vitest:browser:support-testing-library",
1884
+ config() {
1885
+ return {
1886
+ optimizeDeps: {
1887
+ esbuildOptions: {
1888
+ plugins: [
1889
+ {
1890
+ name: "test-utils-rewrite",
1891
+ setup(build) {
1892
+ build.onResolve({ filter: /^@vue\/test-utils$/ }, (args) => {
1893
+ const _require2 = getRequire();
1894
+ const resolved = _require2.resolve(args.path, {
1895
+ paths: [args.importer]
1896
+ });
1897
+ return { path: resolved };
1898
+ });
1899
+ }
1900
+ }
1901
+ ]
1902
+ }
1903
+ }
1904
+ };
1813
1905
  }
1814
1906
  }
1815
- };
1816
- }
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,
1907
+ ];
1908
+ };
1909
+ function tryResolve(path, paths) {
1910
+ try {
1911
+ const _require2 = getRequire();
1912
+ return _require2.resolve(path, { paths });
1913
+ } catch {
1914
+ return void 0;
1915
+ }
1845
1916
  }
1846
- export const commands = server.commands
1847
- export const userEvent = createUserEvent(_userEventSetup)
1848
- export { page, cdp }
1849
- `;
1917
+ let _require;
1918
+ function getRequire() {
1919
+ if (!_require) {
1920
+ _require = createRequire(import.meta.url);
1921
+ }
1922
+ return _require;
1850
1923
  }
1851
- async function getUserEventImport(provider, resolve2) {
1852
- if (provider.name !== "preview") {
1853
- return "const _userEventSetup = undefined";
1924
+ function resolveCoverageFolder(project) {
1925
+ const options = project.ctx.config;
1926
+ const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
1927
+ if (typeof reporter === "string") {
1928
+ return reporter === "html";
1929
+ }
1930
+ return reporter[0] === "html";
1931
+ }) : void 0;
1932
+ if (!htmlReporter) {
1933
+ return void 0;
1854
1934
  }
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}`);
1935
+ const root = resolve(
1936
+ options.root || process.cwd(),
1937
+ options.coverage.reportsDirectory || coverageConfigDefaults.reportsDirectory
1938
+ );
1939
+ const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : void 0;
1940
+ if (!subdir || typeof subdir !== "string") {
1941
+ return [root, `/${basename(root)}/`];
1858
1942
  }
1859
- return `import { userEvent as __vitest_user_event__ } from '${slash(
1860
- `/@fs/${resolved.id}`
1861
- )}'
1862
- const _userEventSetup = __vitest_user_event__.setup()
1863
- `;
1943
+ return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
1944
+ }
1945
+ const postfixRE = /[?#].*$/;
1946
+ function cleanUrl(url) {
1947
+ return url.replace(postfixRE, "");
1864
1948
  }
1865
1949
 
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__: "{}"
1950
+ const DEFAULT_TIMEOUT = 6e4;
1951
+ function defaultSerialize(i) {
1952
+ return i;
1953
+ }
1954
+ const defaultDeserialize = defaultSerialize;
1955
+ const { clearTimeout, setTimeout } = globalThis;
1956
+ const random = Math.random.bind(Math);
1957
+ function createBirpc(functions, options) {
1958
+ const {
1959
+ post,
1960
+ on,
1961
+ off = () => {
1962
+ },
1963
+ eventNames = [],
1964
+ serialize = defaultSerialize,
1965
+ deserialize = defaultDeserialize,
1966
+ resolver,
1967
+ bind = "rpc",
1968
+ timeout = DEFAULT_TIMEOUT
1969
+ } = options;
1970
+ const rpcPromiseMap = /* @__PURE__ */ new Map();
1971
+ let _promise;
1972
+ let closed = false;
1973
+ const rpc = new Proxy({}, {
1974
+ get(_, method) {
1975
+ if (method === "$functions")
1976
+ return functions;
1977
+ if (method === "$close")
1978
+ return close;
1979
+ if (method === "then" && !eventNames.includes("then") && !("then" in functions))
1980
+ return void 0;
1981
+ const sendEvent = (...args) => {
1982
+ post(serialize({ m: method, a: args, t: "q" }));
1983
+ };
1984
+ if (eventNames.includes(method)) {
1985
+ sendEvent.asEvent = sendEvent;
1986
+ return sendEvent;
1987
+ }
1988
+ const sendCall = async (...args) => {
1989
+ if (closed)
1990
+ throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
1991
+ if (_promise) {
1992
+ try {
1993
+ await _promise;
1994
+ } finally {
1995
+ _promise = void 0;
1996
+ }
1997
+ }
1998
+ return new Promise((resolve, reject) => {
1999
+ const id = nanoid();
2000
+ let timeoutId;
2001
+ if (timeout >= 0) {
2002
+ timeoutId = setTimeout(() => {
2003
+ try {
2004
+ options.onTimeoutError?.(method, args);
2005
+ throw new Error(`[birpc] timeout on calling "${method}"`);
2006
+ } catch (e) {
2007
+ reject(e);
2008
+ }
2009
+ rpcPromiseMap.delete(id);
2010
+ }, timeout);
2011
+ if (typeof timeoutId === "object")
2012
+ timeoutId = timeoutId.unref?.();
2013
+ }
2014
+ rpcPromiseMap.set(id, { resolve, reject, timeoutId, method });
2015
+ post(serialize({ m: method, a: args, i: id, t: "q" }));
2016
+ });
2017
+ };
2018
+ sendCall.asEvent = sendEvent;
2019
+ return sendCall;
2020
+ }
1886
2021
  });
1887
- res.removeHeader("Content-Security-Policy");
1888
- if (!server.orchestratorScripts) {
1889
- server.orchestratorScripts = await server.formatScripts(
1890
- project.config.browser.orchestratorScripts
1891
- );
2022
+ function close() {
2023
+ closed = true;
2024
+ rpcPromiseMap.forEach(({ reject, method }) => {
2025
+ reject(new Error(`[birpc] rpc is closed, cannot call "${method}"`));
2026
+ });
2027
+ rpcPromiseMap.clear();
2028
+ off(onMessage);
1892
2029
  }
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
- );
2030
+ async function onMessage(data, ...extra) {
2031
+ const msg = deserialize(data);
2032
+ if (msg.t === "q") {
2033
+ const { m: method, a: args } = msg;
2034
+ let result, error;
2035
+ const fn = resolver ? resolver(method, functions[method]) : functions[method];
2036
+ if (!fn) {
2037
+ error = new Error(`[birpc] function "${method}" not found`);
2038
+ } else {
2039
+ try {
2040
+ result = await fn.apply(bind === "rpc" ? rpc : functions, args);
2041
+ } catch (e) {
2042
+ error = e;
2043
+ }
2044
+ }
2045
+ if (msg.i) {
2046
+ if (error && options.onError)
2047
+ options.onError(error, method, args);
2048
+ post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra);
2049
+ }
2050
+ } else {
2051
+ const { i: ack, r: result, e: error } = msg;
2052
+ const promise = rpcPromiseMap.get(ack);
2053
+ if (promise) {
2054
+ clearTimeout(promise.timeoutId);
2055
+ if (error)
2056
+ promise.reject(error);
2057
+ else
2058
+ promise.resolve(result);
2059
+ }
2060
+ rpcPromiseMap.delete(ack);
2061
+ }
1907
2062
  }
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
- });
2063
+ _promise = on(onMessage);
2064
+ return rpc;
1916
2065
  }
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
- });
2066
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
2067
+ function nanoid(size = 21) {
2068
+ let id = "";
2069
+ let i = size;
2070
+ while (i--)
2071
+ id += urlAlphabet[random() * 64 | 0];
2072
+ return id;
1974
2073
  }
1975
2074
 
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();
2002
- }
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;
2075
+ const debug$1 = createDebugger("vitest:browser:api");
2076
+ const BROWSER_API_PATH = "/__vitest_browser_api__";
2077
+ function setupBrowserRpc(server) {
2078
+ const project = server.project;
2079
+ const vite = server.vite;
2080
+ const ctx = project.ctx;
2081
+ const wss = new WebSocketServer({ noServer: true });
2082
+ vite.httpServer?.on("upgrade", (request, socket, head) => {
2083
+ if (!request.url) {
2084
+ return;
2085
+ }
2086
+ const { pathname, searchParams } = new URL(request.url, "http://localhost");
2087
+ if (pathname !== BROWSER_API_PATH) {
2088
+ return;
2089
+ }
2090
+ const type = searchParams.get("type") ?? "tester";
2091
+ const sessionId = searchParams.get("sessionId") ?? "0";
2092
+ wss.handleUpgrade(request, socket, head, (ws) => {
2093
+ wss.emit("connection", ws, request);
2094
+ const rpc = setupClient(sessionId, ws);
2095
+ const state = server.state;
2096
+ const clients = type === "tester" ? state.testers : state.orchestrators;
2097
+ clients.set(sessionId, rpc);
2098
+ debug$1?.("[%s] Browser API connected to %s", sessionId, type);
2099
+ ws.on("close", () => {
2100
+ debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
2101
+ clients.delete(sessionId);
2102
+ server.state.removeCDPHandler(sessionId);
2103
+ });
2104
+ });
2105
+ });
2106
+ function checkFileAccess(path) {
2107
+ if (!isFileServingAllowed(path, vite)) {
2108
+ throw new Error(
2109
+ `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
2110
+ );
2111
+ }
2112
+ }
2113
+ function setupClient(sessionId, ws) {
2114
+ const mockResolver = new ServerMockResolver(server.vite, {
2115
+ moduleDirectories: project.config.server?.deps?.moduleDirectories
2116
+ });
2117
+ const rpc = createBirpc(
2118
+ {
2119
+ async onUnhandledError(error, type) {
2120
+ if (error && typeof error === "object") {
2121
+ const _error = error;
2122
+ _error.stacks = server.parseErrorStacktrace(_error);
2014
2123
  }
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();
2124
+ ctx.state.catchError(error, type);
2125
+ },
2126
+ async onCollected(files) {
2127
+ ctx.state.collectFiles(project, files);
2128
+ await ctx.report("onCollected", files);
2129
+ },
2130
+ async onTaskUpdate(packs) {
2131
+ ctx.state.updateTasks(packs);
2132
+ await ctx.report("onTaskUpdate", packs);
2133
+ },
2134
+ onAfterSuiteRun(meta) {
2135
+ ctx.coverageProvider?.onAfterSuiteRun(meta);
2136
+ },
2137
+ sendLog(log) {
2138
+ return ctx.report("onUserConsoleLog", log);
2139
+ },
2140
+ resolveSnapshotPath(testPath) {
2141
+ return ctx.snapshot.resolvePath(testPath);
2142
+ },
2143
+ resolveSnapshotRawPath(testPath, rawPath) {
2144
+ return ctx.snapshot.resolveRawPath(testPath, rawPath);
2145
+ },
2146
+ snapshotSaved(snapshot) {
2147
+ ctx.snapshot.add(snapshot);
2148
+ },
2149
+ async readSnapshotFile(snapshotPath) {
2150
+ checkFileAccess(snapshotPath);
2151
+ if (!existsSync(snapshotPath)) {
2152
+ return null;
2025
2153
  }
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);
2154
+ return promises.readFile(snapshotPath, "utf-8");
2155
+ },
2156
+ async saveSnapshotFile(id, content) {
2157
+ checkFileAccess(id);
2158
+ await promises.mkdir(dirname(id), { recursive: true });
2159
+ return promises.writeFile(id, content, "utf-8");
2160
+ },
2161
+ async removeSnapshotFile(id) {
2162
+ checkFileAccess(id);
2163
+ if (!existsSync(id)) {
2164
+ throw new Error(`Snapshot file "${id}" does not exist.`);
2153
2165
  }
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
2166
+ return promises.unlink(id);
2167
+ },
2168
+ getBrowserFileSourceMap(id) {
2169
+ const mod = server.vite.moduleGraph.getModuleById(id);
2170
+ return mod?.transformResult?.map;
2171
+ },
2172
+ onCancel(reason) {
2173
+ ctx.cancelCurrentRun(reason);
2174
+ },
2175
+ async resolveId(id, importer) {
2176
+ return mockResolver.resolveId(id, importer);
2177
+ },
2178
+ debug(...args) {
2179
+ ctx.logger.console.debug(...args);
2180
+ },
2181
+ getCountOfFailedTests() {
2182
+ return ctx.state.getCountOfFailedTests();
2183
+ },
2184
+ async triggerCommand(contextId, command, testPath, payload) {
2185
+ debug$1?.('[%s] Triggering command "%s"', contextId, command);
2186
+ const provider = server.provider;
2187
+ if (!provider) {
2188
+ throw new Error("Commands are only available for browser tests.");
2184
2189
  }
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));
2190
+ const commands = project.config.browser?.commands;
2191
+ if (!commands || !commands[command]) {
2192
+ throw new Error(`Unknown command "${command}".`);
2193
+ }
2194
+ if (provider.beforeCommand) {
2195
+ await provider.beforeCommand(command, payload);
2196
+ }
2197
+ const context = Object.assign(
2198
+ {
2199
+ testPath,
2200
+ project,
2201
+ provider,
2202
+ contextId
2203
+ },
2204
+ provider.getCommandsContext(contextId)
2205
+ );
2206
+ let result;
2207
+ try {
2208
+ result = await commands[command](context, ...payload);
2209
+ } finally {
2210
+ if (provider.afterCommand) {
2211
+ await provider.afterCommand(command, payload);
2212
+ }
2213
+ }
2214
+ return result;
2215
+ },
2216
+ finishBrowserTests(contextId) {
2217
+ debug$1?.("[%s] Finishing browser tests for context", contextId);
2218
+ return server.state.getContext(contextId)?.resolve();
2219
+ },
2220
+ resolveMock(rawId, importer, options) {
2221
+ return mockResolver.resolveMock(rawId, importer, options);
2222
+ },
2223
+ invalidate(ids) {
2224
+ return mockResolver.invalidate(ids);
2225
+ },
2226
+ // CDP
2227
+ async sendCdpEvent(contextId, event, payload) {
2228
+ const cdp = await server.ensureCDPHandler(contextId, sessionId);
2229
+ return cdp.send(event, payload);
2230
+ },
2231
+ async trackCdpEvent(contextId, type, event, listenerId) {
2232
+ const cdp = await server.ensureCDPHandler(contextId, sessionId);
2233
+ cdp[type](event, listenerId);
2222
2234
  }
2223
2235
  },
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));
2236
+ {
2237
+ post: (msg) => ws.send(msg),
2238
+ on: (fn) => ws.on("message", fn),
2239
+ eventNames: ["onCancel", "cdpEvent"],
2240
+ serialize: (data) => stringify(data, stringifyReplace),
2241
+ deserialize: parse,
2242
+ onTimeoutError(functionName) {
2243
+ throw new Error(`[vitest-api]: Timeout calling "${functionName}"`);
2228
2244
  }
2229
2245
  }
2230
- },
2231
- BrowserContext(browserServer),
2232
- dynamicImportPlugin({
2233
- globalThisAccessor: '"__vitest_browser_runner__"'
2246
+ );
2247
+ ctx.onCancel((reason) => rpc.onCancel(reason));
2248
+ return rpc;
2249
+ }
2250
+ }
2251
+ function cloneByOwnProperties(value) {
2252
+ return Object.getOwnPropertyNames(value).reduce(
2253
+ (clone, prop) => ({
2254
+ ...clone,
2255
+ [prop]: value[prop]
2234
2256
  }),
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
2257
+ {}
2258
+ );
2259
+ }
2260
+ function stringifyReplace(key, value) {
2261
+ if (value instanceof Error) {
2262
+ const cloned = cloneByOwnProperties(value);
2263
+ return {
2264
+ name: value.name,
2265
+ message: value.message,
2266
+ stack: value.stack,
2267
+ ...cloned
2268
+ };
2269
+ } else {
2270
+ return value;
2271
+ }
2272
+ }
2273
+
2274
+ class BrowserServerCDPHandler {
2275
+ constructor(session, tester) {
2276
+ this.session = session;
2277
+ this.tester = tester;
2278
+ }
2279
+ listenerIds = {};
2280
+ listeners = {};
2281
+ send(method, params) {
2282
+ return this.session.send(method, params);
2283
+ }
2284
+ on(event, id, once = false) {
2285
+ if (!this.listenerIds[event]) {
2286
+ this.listenerIds[event] = [];
2287
+ }
2288
+ this.listenerIds[event].push(id);
2289
+ if (!this.listeners[event]) {
2290
+ this.listeners[event] = (payload) => {
2291
+ this.tester.cdpEvent(
2292
+ event,
2293
+ payload
2264
2294
  );
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;
2295
+ if (once) {
2296
+ this.off(event, id);
2297
+ }
2298
+ };
2299
+ this.session.on(event, this.listeners[event]);
2300
+ }
2301
+ }
2302
+ off(event, id) {
2303
+ if (!this.listenerIds[event]) {
2304
+ this.listenerIds[event] = [];
2305
+ }
2306
+ this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
2307
+ if (!this.listenerIds[event].length) {
2308
+ this.session.off(event, this.listeners[event]);
2309
+ delete this.listeners[event];
2310
+ }
2311
+ }
2312
+ once(event, listener) {
2313
+ this.on(event, listener, true);
2314
+ }
2315
+ }
2316
+
2317
+ class BrowserServerState {
2318
+ orchestrators = /* @__PURE__ */ new Map();
2319
+ testers = /* @__PURE__ */ new Map();
2320
+ cdps = /* @__PURE__ */ new Map();
2321
+ contexts = /* @__PURE__ */ new Map();
2322
+ getContext(contextId) {
2323
+ return this.contexts.get(contextId);
2324
+ }
2325
+ createAsyncContext(method, contextId, files) {
2326
+ const defer = createDefer();
2327
+ this.contexts.set(contextId, {
2328
+ files,
2329
+ method,
2330
+ resolve: () => {
2331
+ defer.resolve();
2332
+ this.contexts.delete(contextId);
2333
+ },
2334
+ reject: defer.reject
2335
+ });
2336
+ return defer;
2337
+ }
2338
+ async removeCDPHandler(sessionId) {
2339
+ this.cdps.delete(sessionId);
2340
+ }
2341
+ }
2342
+
2343
+ class BrowserServer {
2344
+ constructor(project, base) {
2345
+ this.project = project;
2346
+ this.base = base;
2347
+ this.stackTraceOptions = {
2348
+ frameFilter: project.config.onStackTrace,
2349
+ getSourceMap: (id) => {
2350
+ const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
2351
+ return result?.map;
2352
+ },
2353
+ getFileName: (id) => {
2354
+ const mod = this.vite.moduleGraph.getModuleById(id);
2355
+ if (mod?.file) {
2356
+ return mod.file;
2277
2357
  }
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
- };
2358
+ const modUrl = this.vite.moduleGraph.urlToModuleMap.get(id);
2359
+ if (modUrl?.file) {
2360
+ return modUrl.file;
2299
2361
  }
2362
+ return id;
2300
2363
  }
2301
- },
2302
- // TODO: remove this when @testing-library/vue supports ESM
2303
- {
2304
- name: "vitest:browser:support-testing-library",
2305
- config() {
2364
+ };
2365
+ this.state = new BrowserServerState();
2366
+ const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
2367
+ const distRoot = resolve(pkgRoot, "dist");
2368
+ this.prefixTesterUrl = `${base}__vitest_test__/__test__/`;
2369
+ this.faviconUrl = `${base}__vitest__/favicon.svg`;
2370
+ this.manifest = (async () => {
2371
+ return JSON.parse(
2372
+ await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8")
2373
+ );
2374
+ })().then((manifest) => this.manifest = manifest);
2375
+ const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
2376
+ if (!existsSync(testerHtmlPath)) {
2377
+ throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
2378
+ }
2379
+ this.testerFilepath = testerHtmlPath;
2380
+ this.testerHtml = readFile$1(
2381
+ testerHtmlPath,
2382
+ "utf8"
2383
+ ).then((html) => this.testerHtml = html);
2384
+ 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);
2385
+ this.injectorJs = readFile$1(
2386
+ resolve(distRoot, "client/esm-client-injector.js"),
2387
+ "utf8"
2388
+ ).then((js) => this.injectorJs = js);
2389
+ this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
2390
+ const builtinProviders = ["playwright", "webdriverio", "preview"];
2391
+ const providerName = project.config.browser.provider || "preview";
2392
+ if (builtinProviders.includes(providerName)) {
2393
+ this.locatorsUrl = join("/@fs/", distRoot, "locators", `${providerName}.js`);
2394
+ }
2395
+ this.stateJs = readFile$1(
2396
+ resolve(distRoot, "state.js"),
2397
+ "utf-8"
2398
+ ).then((js) => this.stateJs = js);
2399
+ }
2400
+ faviconUrl;
2401
+ prefixTesterUrl;
2402
+ orchestratorScripts;
2403
+ testerScripts;
2404
+ manifest;
2405
+ testerHtml;
2406
+ testerFilepath;
2407
+ orchestratorHtml;
2408
+ injectorJs;
2409
+ errorCatcherUrl;
2410
+ locatorsUrl;
2411
+ stateJs;
2412
+ state;
2413
+ provider;
2414
+ vite;
2415
+ stackTraceOptions;
2416
+ setServer(server) {
2417
+ this.vite = server;
2418
+ }
2419
+ getSerializableConfig() {
2420
+ const config = wrapConfig(this.project.getSerializableConfig());
2421
+ config.env ??= {};
2422
+ config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
2423
+ return config;
2424
+ }
2425
+ resolveTesterUrl(pathname) {
2426
+ const [contextId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
2427
+ const decodedTestFile = decodeURIComponent(testFile);
2428
+ return { contextId, testFile: decodedTestFile };
2429
+ }
2430
+ async formatScripts(scripts) {
2431
+ if (!scripts?.length) {
2432
+ return [];
2433
+ }
2434
+ const server = this.vite;
2435
+ const promises = scripts.map(
2436
+ async ({ content, src, async, id, type = "module" }, index) => {
2437
+ const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : void 0) || src;
2438
+ const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
2439
+ await server.moduleGraph.ensureEntryFromUrl(transformId);
2440
+ const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
2306
2441
  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
2442
+ tag: "script",
2443
+ attrs: {
2444
+ type,
2445
+ ...async ? { async: "" } : {},
2446
+ ...srcLink ? {
2447
+ src: srcLink.startsWith("http") ? srcLink : slash(`/@fs/${srcLink}`)
2448
+ } : {}
2319
2449
  },
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
- }
2450
+ injectTo: "head",
2451
+ children: contentProcessed || ""
2338
2452
  };
2339
2453
  }
2454
+ );
2455
+ return await Promise.all(promises);
2456
+ }
2457
+ async initBrowserProvider() {
2458
+ if (this.provider) {
2459
+ return;
2340
2460
  }
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;
2461
+ const Provider = await getBrowserProvider(this.project.config.browser, this.project);
2462
+ this.provider = new Provider();
2463
+ const browser = this.project.config.browser.name;
2464
+ if (!browser) {
2465
+ throw new Error(
2466
+ `[${this.project.getName()}] Browser name is required. Please, set \`test.browser.name\` option manually.`
2467
+ );
2468
+ }
2469
+ const supportedBrowsers = this.provider.getSupportedBrowsers();
2470
+ if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
2471
+ throw new Error(
2472
+ `[${this.project.getName()}] Browser "${browser}" is not supported by the browser provider "${this.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`
2473
+ );
2474
+ }
2475
+ const providerOptions = this.project.config.browser.providerOptions;
2476
+ await this.provider.initialize(this.project, {
2477
+ browser,
2478
+ options: providerOptions
2479
+ });
2349
2480
  }
2350
- }
2351
- let _require;
2352
- function getRequire() {
2353
- if (!_require) {
2354
- _require = createRequire(import.meta.url);
2481
+ parseErrorStacktrace(e, options = {}) {
2482
+ return parseErrorStacktrace(e, {
2483
+ ...this.stackTraceOptions,
2484
+ ...options
2485
+ });
2355
2486
  }
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";
2487
+ parseStacktrace(trace, options = {}) {
2488
+ return parseStacktrace(trace, {
2489
+ ...this.stackTraceOptions,
2490
+ ...options
2491
+ });
2492
+ }
2493
+ cdpSessionsPromises = /* @__PURE__ */ new Map();
2494
+ async ensureCDPHandler(contextId, sessionId) {
2495
+ const cachedHandler = this.state.cdps.get(sessionId);
2496
+ if (cachedHandler) {
2497
+ return cachedHandler;
2363
2498
  }
2364
- return reporter[0] === "html";
2365
- }) : void 0;
2366
- if (!htmlReporter) {
2367
- return void 0;
2499
+ const provider = this.provider;
2500
+ if (!provider.getCDPSession) {
2501
+ throw new Error(`CDP is not supported by the provider "${provider.name}".`);
2502
+ }
2503
+ const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
2504
+ const promise2 = provider.getCDPSession(contextId).finally(() => {
2505
+ this.cdpSessionsPromises.delete(sessionId);
2506
+ });
2507
+ this.cdpSessionsPromises.set(sessionId, promise2);
2508
+ return promise2;
2509
+ })();
2510
+ const session = await promise;
2511
+ const rpc = this.state.testers.get(sessionId);
2512
+ if (!rpc) {
2513
+ throw new Error(`Tester RPC "${sessionId}" was not established.`);
2514
+ }
2515
+ const handler = new BrowserServerCDPHandler(session, rpc);
2516
+ this.state.cdps.set(
2517
+ sessionId,
2518
+ handler
2519
+ );
2520
+ return handler;
2368
2521
  }
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)}/`];
2522
+ async close() {
2523
+ await this.vite.close();
2376
2524
  }
2377
- return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
2378
2525
  }
2379
- const postfixRE = /[?#].*$/;
2380
- function cleanUrl(url) {
2381
- return url.replace(postfixRE, "");
2526
+ function wrapConfig(config) {
2527
+ return {
2528
+ ...config,
2529
+ // workaround RegExp serialization
2530
+ testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : void 0
2531
+ };
2382
2532
  }
2383
2533
 
2384
2534
  const debug = createDebugger("vitest:browser:pool");
@@ -2451,8 +2601,8 @@ function createBrowserPool(ctx) {
2451
2601
  );
2452
2602
  const url = new URL("/", origin);
2453
2603
  url.searchParams.set("contextId", contextId);
2454
- const page = provider.openPage(contextId, url.toString(), () => setBreakpoint(contextId, files2[0])).then(() => waitPromise);
2455
- promises.push(page);
2604
+ const page = provider.openPage(contextId, url.toString(), () => setBreakpoint(contextId, files2[0]));
2605
+ promises.push(page, waitPromise);
2456
2606
  }
2457
2607
  });
2458
2608
  await Promise.all(promises);
@@ -2513,7 +2663,9 @@ Update your dependencies and make sure the versions match.`
2513
2663
  const server = new BrowserServer(project, "/");
2514
2664
  const configPath = typeof configFile === "string" ? configFile : false;
2515
2665
  const logLevel = process.env.VITEST_BROWSER_DEBUG ?? "info";
2516
- const logger = createViteLogger(logLevel);
2666
+ const logger = createViteLogger(project.logger, logLevel, {
2667
+ allowClearScreen: false
2668
+ });
2517
2669
  const vite = await createViteServer({
2518
2670
  ...project.options,
2519
2671
  // spread project config inlined in root workspace config
@@ -2556,4 +2708,4 @@ ${c.bold("[vitest]")} Vite unexpectedly reloaded a test. This may cause tests to
2556
2708
  return server;
2557
2709
  }
2558
2710
 
2559
- export { createBrowserPool, createBrowserServer };
2711
+ export { createBrowserPool, createBrowserServer, distRoot };