@vitest/browser 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ import { channel, client } from '@vitest/browser/client'
2
+
3
+ function on(event, listener) {
4
+ window.addEventListener(event, listener)
5
+ return () => window.removeEventListener(event, listener)
6
+ }
7
+
8
+ function serializeError(unhandledError) {
9
+ if (typeof unhandledError !== 'object' || !unhandledError) {
10
+ return {
11
+ message: String(unhandledError),
12
+ }
13
+ }
14
+
15
+ return {
16
+ name: unhandledError.name,
17
+ message: unhandledError.message,
18
+ stack: String(unhandledError.stack),
19
+ }
20
+ }
21
+
22
+ function catchWindowErrors(cb) {
23
+ let userErrorListenerCount = 0
24
+ function throwUnhandlerError(e) {
25
+ if (userErrorListenerCount === 0 && e.error != null) {
26
+ cb(e)
27
+ }
28
+ else {
29
+ console.error(e.error)
30
+ }
31
+ }
32
+ const addEventListener = window.addEventListener.bind(window)
33
+ const removeEventListener = window.removeEventListener.bind(window)
34
+ window.addEventListener('error', throwUnhandlerError)
35
+ window.addEventListener = function (...args) {
36
+ if (args[0] === 'error') {
37
+ userErrorListenerCount++
38
+ }
39
+ return addEventListener.apply(this, args)
40
+ }
41
+ window.removeEventListener = function (...args) {
42
+ if (args[0] === 'error' && userErrorListenerCount) {
43
+ userErrorListenerCount--
44
+ }
45
+ return removeEventListener.apply(this, args)
46
+ }
47
+ return function clearErrorHandlers() {
48
+ window.removeEventListener('error', throwUnhandlerError)
49
+ }
50
+ }
51
+
52
+ function registerUnexpectedErrors() {
53
+ catchWindowErrors(event =>
54
+ reportUnexpectedError('Error', event.error),
55
+ )
56
+ on('unhandledrejection', event =>
57
+ reportUnexpectedError('Unhandled Rejection', event.reason))
58
+ }
59
+
60
+ async function reportUnexpectedError(
61
+ type,
62
+ error,
63
+ ) {
64
+ const processedError = serializeError(error)
65
+ await client.rpc.onUnhandledError(processedError, type)
66
+ const state = __vitest_browser_runner__
67
+
68
+ if (state.type === 'orchestrator') {
69
+ return
70
+ }
71
+
72
+ if (!state.runTests || !__vitest_worker__.current) {
73
+ channel.postMessage({
74
+ type: 'done',
75
+ filenames: state.files,
76
+ id: state.iframeId,
77
+ })
78
+ }
79
+ }
80
+
81
+ registerUnexpectedErrors()
@@ -23,6 +23,7 @@ window.__vitest_browser_runner__ = {
23
23
  files: { __VITEST_FILES__ },
24
24
  type: { __VITEST_TYPE__ },
25
25
  contextId: { __VITEST_CONTEXT_ID__ },
26
+ testerId: { __VITEST_TESTER_ID__ },
26
27
  provider: { __VITEST_PROVIDER__ },
27
28
  providedContext: { __VITEST_PROVIDED_CONTEXT__ },
28
29
  };
@@ -23,10 +23,11 @@
23
23
  height: 100%;
24
24
  }
25
25
  </style>
26
- <script>{__VITEST_INJECTOR__}</script>
26
+ {__VITEST_INJECTOR__}
27
+ {__VITEST_ERROR_CATCHER__}
27
28
  {__VITEST_SCRIPTS__}
28
- <script type="module" crossorigin src="/__vitest_browser__/orchestrator-x0A1t8rC.js"></script>
29
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-dLyjuL0K.js">
29
+ <script type="module" crossorigin src="/__vitest_browser__/orchestrator-CNOAigTE.js"></script>
30
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/preload-helper-Btt6SgIy.js">
30
31
  </head>
31
32
  <body>
32
33
  <div id="vitest-tester"></div>
@@ -16,11 +16,12 @@
16
16
  min-height: 100vh;
17
17
  }
18
18
  </style>
19
- <script>{__VITEST_INJECTOR__}</script>
19
+ {__VITEST_INJECTOR__}
20
20
  <script>{__VITEST_STATE__}</script>
21
+ {__VITEST_ERROR_CATCHER__}
21
22
  {__VITEST_SCRIPTS__}
22
- <script type="module" crossorigin src="/__vitest_browser__/tester-BdcP5piS.js"></script>
23
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-dLyjuL0K.js">
23
+ <script type="module" crossorigin src="/__vitest_browser__/tester-DoK-7PCe.js"></script>
24
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/preload-helper-Btt6SgIy.js">
24
25
  </head>
25
26
  <body
26
27
  data-vitest-body
package/dist/client.js ADDED
@@ -0,0 +1,333 @@
1
+ const DEFAULT_TIMEOUT = 6e4;
2
+ function defaultSerialize(i) {
3
+ return i;
4
+ }
5
+ const defaultDeserialize = defaultSerialize;
6
+ const { clearTimeout: clearTimeout$1, setTimeout: setTimeout$1 } = globalThis;
7
+ const random = Math.random.bind(Math);
8
+ function createBirpc(functions, options) {
9
+ const {
10
+ post,
11
+ on,
12
+ eventNames = [],
13
+ serialize = defaultSerialize,
14
+ deserialize = defaultDeserialize,
15
+ resolver,
16
+ timeout = DEFAULT_TIMEOUT
17
+ } = options;
18
+ const rpcPromiseMap = /* @__PURE__ */ new Map();
19
+ let _promise;
20
+ const rpc = new Proxy({}, {
21
+ get(_, method) {
22
+ if (method === "$functions")
23
+ return functions;
24
+ if (method === "then" && !eventNames.includes("then") && !("then" in functions))
25
+ return void 0;
26
+ const sendEvent = (...args) => {
27
+ post(serialize({ m: method, a: args, t: "q" }));
28
+ };
29
+ if (eventNames.includes(method)) {
30
+ sendEvent.asEvent = sendEvent;
31
+ return sendEvent;
32
+ }
33
+ const sendCall = async (...args) => {
34
+ await _promise;
35
+ return new Promise((resolve, reject) => {
36
+ const id = nanoid();
37
+ let timeoutId;
38
+ if (timeout >= 0) {
39
+ timeoutId = setTimeout$1(() => {
40
+ try {
41
+ options.onTimeoutError?.(method, args);
42
+ throw new Error(`[birpc] timeout on calling "${method}"`);
43
+ } catch (e) {
44
+ reject(e);
45
+ }
46
+ rpcPromiseMap.delete(id);
47
+ }, timeout);
48
+ if (typeof timeoutId === "object")
49
+ timeoutId = timeoutId.unref?.();
50
+ }
51
+ rpcPromiseMap.set(id, { resolve, reject, timeoutId });
52
+ post(serialize({ m: method, a: args, i: id, t: "q" }));
53
+ });
54
+ };
55
+ sendCall.asEvent = sendEvent;
56
+ return sendCall;
57
+ }
58
+ });
59
+ _promise = on(async (data, ...extra) => {
60
+ const msg = deserialize(data);
61
+ if (msg.t === "q") {
62
+ const { m: method, a: args } = msg;
63
+ let result, error;
64
+ const fn = resolver ? resolver(method, functions[method]) : functions[method];
65
+ if (!fn) {
66
+ error = new Error(`[birpc] function "${method}" not found`);
67
+ } else {
68
+ try {
69
+ result = await fn.apply(rpc, args);
70
+ } catch (e) {
71
+ error = e;
72
+ }
73
+ }
74
+ if (msg.i) {
75
+ if (error && options.onError)
76
+ options.onError(error, method, args);
77
+ post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra);
78
+ }
79
+ } else {
80
+ const { i: ack, r: result, e: error } = msg;
81
+ const promise = rpcPromiseMap.get(ack);
82
+ if (promise) {
83
+ clearTimeout$1(promise.timeoutId);
84
+ if (error)
85
+ promise.reject(error);
86
+ else
87
+ promise.resolve(result);
88
+ }
89
+ rpcPromiseMap.delete(ack);
90
+ }
91
+ });
92
+ return rpc;
93
+ }
94
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
95
+ function nanoid(size = 21) {
96
+ let id = "";
97
+ let i = size;
98
+ while (i--)
99
+ id += urlAlphabet[random() * 64 | 0];
100
+ return id;
101
+ }
102
+
103
+ /// <reference types="../types/index.d.ts" />
104
+
105
+ // (c) 2020-present Andrea Giammarchi
106
+
107
+ const {parse: $parse, stringify: $stringify} = JSON;
108
+ const {keys} = Object;
109
+
110
+ const Primitive = String; // it could be Number
111
+ const primitive = 'string'; // it could be 'number'
112
+
113
+ const ignore = {};
114
+ const object = 'object';
115
+
116
+ const noop = (_, value) => value;
117
+
118
+ const primitives = value => (
119
+ value instanceof Primitive ? Primitive(value) : value
120
+ );
121
+
122
+ const Primitives = (_, value) => (
123
+ typeof value === primitive ? new Primitive(value) : value
124
+ );
125
+
126
+ const revive = (input, parsed, output, $) => {
127
+ const lazy = [];
128
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
129
+ const k = ke[y];
130
+ const value = output[k];
131
+ if (value instanceof Primitive) {
132
+ const tmp = input[value];
133
+ if (typeof tmp === object && !parsed.has(tmp)) {
134
+ parsed.add(tmp);
135
+ output[k] = ignore;
136
+ lazy.push({k, a: [input, parsed, tmp, $]});
137
+ }
138
+ else
139
+ output[k] = $.call(output, k, tmp);
140
+ }
141
+ else if (output[k] !== ignore)
142
+ output[k] = $.call(output, k, value);
143
+ }
144
+ for (let {length} = lazy, i = 0; i < length; i++) {
145
+ const {k, a} = lazy[i];
146
+ output[k] = $.call(output, k, revive.apply(null, a));
147
+ }
148
+ return output;
149
+ };
150
+
151
+ const set = (known, input, value) => {
152
+ const index = Primitive(input.push(value) - 1);
153
+ known.set(value, index);
154
+ return index;
155
+ };
156
+
157
+ /**
158
+ * Converts a specialized flatted string into a JS value.
159
+ * @param {string} text
160
+ * @param {(this: any, key: string, value: any) => any} [reviver]
161
+ * @returns {any}
162
+ */
163
+ const parse = (text, reviver) => {
164
+ const input = $parse(text, Primitives).map(primitives);
165
+ const value = input[0];
166
+ const $ = reviver || noop;
167
+ const tmp = typeof value === object && value ?
168
+ revive(input, new Set, value, $) :
169
+ value;
170
+ return $.call({'': tmp}, '', tmp);
171
+ };
172
+
173
+ /**
174
+ * Converts a JS value into a specialized flatted string.
175
+ * @param {any} value
176
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
177
+ * @param {string | number | undefined} [space]
178
+ * @returns {string}
179
+ */
180
+ const stringify = (value, replacer, space) => {
181
+ const $ = replacer && typeof replacer === object ?
182
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
183
+ (replacer || noop);
184
+ const known = new Map;
185
+ const input = [];
186
+ const output = [];
187
+ let i = +set(known, input, $.call({'': value}, '', value));
188
+ let firstRun = !i;
189
+ while (i < input.length) {
190
+ firstRun = true;
191
+ output[i] = $stringify(input[i++], replace, space);
192
+ }
193
+ return '[' + output.join(',') + ']';
194
+ function replace(key, value) {
195
+ if (firstRun) {
196
+ firstRun = !firstRun;
197
+ return value;
198
+ }
199
+ const after = $.call(this, key, value);
200
+ switch (typeof after) {
201
+ case object:
202
+ if (after === null) return after;
203
+ case primitive:
204
+ return known.get(after) || set(known, input, after);
205
+ }
206
+ return after;
207
+ }
208
+ };
209
+
210
+ // @__NO_SIDE_EFFECTS__
211
+ function getBrowserState() {
212
+ return window.__vitest_browser_runner__;
213
+ }
214
+
215
+ const channel = new BroadcastChannel(
216
+ `vitest:${getBrowserState().contextId}`
217
+ );
218
+ const globalChannel = new BroadcastChannel("vitest:global");
219
+ function waitForChannel(event) {
220
+ return new Promise((resolve) => {
221
+ channel.addEventListener(
222
+ "message",
223
+ (e) => {
224
+ if (e.data?.type === event) {
225
+ resolve();
226
+ }
227
+ },
228
+ { once: true }
229
+ );
230
+ });
231
+ }
232
+
233
+ const PAGE_TYPE = getBrowserState().type;
234
+ const PORT = location.port;
235
+ const HOST = [location.hostname, PORT].filter(Boolean).join(":");
236
+ const SESSION_ID = PAGE_TYPE === "orchestrator" ? getBrowserState().contextId : getBrowserState().testerId;
237
+ const ENTRY_URL = `${location.protocol === "https:" ? "wss:" : "ws:"}//${HOST}/__vitest_browser_api__?type=${PAGE_TYPE}&sessionId=${SESSION_ID}`;
238
+ let setCancel = (_) => {
239
+ };
240
+ const onCancel = new Promise((resolve) => {
241
+ setCancel = resolve;
242
+ });
243
+ function createClient() {
244
+ const reconnectInterval = 2e3;
245
+ const reconnectTries = 10;
246
+ const connectTimeout = 6e4;
247
+ let tries = reconnectTries;
248
+ const ctx = {
249
+ ws: new WebSocket(ENTRY_URL),
250
+ waitForConnection
251
+ };
252
+ let onMessage;
253
+ ctx.rpc = createBirpc(
254
+ {
255
+ onCancel: setCancel,
256
+ async createTesters(files) {
257
+ if (PAGE_TYPE !== "orchestrator") {
258
+ return;
259
+ }
260
+ getBrowserState().createTesters?.(files);
261
+ },
262
+ cdpEvent(event, payload) {
263
+ const cdp = getBrowserState().cdp;
264
+ if (!cdp) {
265
+ return;
266
+ }
267
+ cdp.emit(event, payload);
268
+ }
269
+ },
270
+ {
271
+ post: (msg) => ctx.ws.send(msg),
272
+ on: (fn) => onMessage = fn,
273
+ serialize: (e) => stringify(e, (_, v) => {
274
+ if (v instanceof Error) {
275
+ return {
276
+ name: v.name,
277
+ message: v.message,
278
+ stack: v.stack
279
+ };
280
+ }
281
+ return v;
282
+ }),
283
+ deserialize: parse,
284
+ onTimeoutError(functionName) {
285
+ throw new Error(`[vitest-browser]: Timeout calling "${functionName}"`);
286
+ }
287
+ }
288
+ );
289
+ let openPromise;
290
+ function reconnect(reset = false) {
291
+ if (reset) {
292
+ tries = reconnectTries;
293
+ }
294
+ ctx.ws = new WebSocket(ENTRY_URL);
295
+ registerWS();
296
+ }
297
+ function registerWS() {
298
+ openPromise = new Promise((resolve, reject) => {
299
+ const timeout = setTimeout(() => {
300
+ reject(
301
+ new Error(
302
+ `Cannot connect to the server in ${connectTimeout / 1e3} seconds`
303
+ )
304
+ );
305
+ }, connectTimeout)?.unref?.();
306
+ if (ctx.ws.OPEN === ctx.ws.readyState) {
307
+ resolve();
308
+ }
309
+ ctx.ws.addEventListener("open", () => {
310
+ tries = reconnectTries;
311
+ resolve();
312
+ clearTimeout(timeout);
313
+ });
314
+ });
315
+ ctx.ws.addEventListener("message", (v) => {
316
+ onMessage(v.data);
317
+ });
318
+ ctx.ws.addEventListener("close", () => {
319
+ tries -= 1;
320
+ if (tries > 0) {
321
+ setTimeout(reconnect, reconnectInterval);
322
+ }
323
+ });
324
+ }
325
+ registerWS();
326
+ function waitForConnection() {
327
+ return openPromise;
328
+ }
329
+ return ctx;
330
+ }
331
+ const client = createClient();
332
+
333
+ export { ENTRY_URL, HOST, PORT, SESSION_ID, channel, client, globalChannel, onCancel, waitForChannel };
package/dist/index.d.ts CHANGED
@@ -103,6 +103,7 @@ declare class BrowserServer implements BrowserServer$1 {
103
103
  testerHtml: Promise<string> | string;
104
104
  orchestratorHtml: Promise<string> | string;
105
105
  injectorJs: Promise<string> | string;
106
+ errorCatcherPath: Promise<string> | string;
106
107
  stateJs: Promise<string> | string;
107
108
  state: BrowserServerState;
108
109
  provider: BrowserProvider;
@@ -119,7 +120,7 @@ declare class BrowserServer implements BrowserServer$1 {
119
120
  initBrowserProvider(): Promise<void>;
120
121
  parseErrorStacktrace(e: ErrorWithDiff, options?: StackTraceParserOptions): vitest.ParsedStack[];
121
122
  parseStacktrace(trace: string, options?: StackTraceParserOptions): vitest.ParsedStack[];
122
- private cdpSessions;
123
+ private cdpSessionsPromises;
123
124
  ensureCDPHandler(contextId: string, sessionId: string): Promise<BrowserServerCDPHandler>;
124
125
  close(): Promise<void>;
125
126
  }
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
- import { createDebugger, isFileServingAllowed as isFileServingAllowed$1, getFilePoolName, resolveApiServerConfig, resolveFsAllow, distDir, createServer } from 'vitest/node';
1
+ import { createDebugger, isFileServingAllowed, getFilePoolName, resolveApiServerConfig, resolveFsAllow, distDir, createServer } from 'vitest/node';
2
2
  import fs, { existsSync, readdirSync, readFileSync, promises } from 'node:fs';
3
3
  import { WebSocketServer } from 'ws';
4
- import { isFileServingAllowed } from 'vite';
5
4
  import { builtinModules, createRequire } from 'node:module';
6
5
  import { readFile as readFile$1, mkdir } from 'node:fs/promises';
7
6
  import { fileURLToPath } from 'node:url';
@@ -13,8 +12,8 @@ import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from '.
13
12
  import { resolve as resolve$1, dirname as dirname$1, normalize as normalize$1 } from 'node:path';
14
13
  import MagicString from 'magic-string';
15
14
  import { esmWalker } from '@vitest/utils/ast';
16
- import * as nodeos from 'node:os';
17
15
  import crypto from 'node:crypto';
16
+ import * as nodeos from 'node:os';
18
17
 
19
18
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
20
19
  function normalizeWindowsPath(input = "") {
@@ -868,6 +867,7 @@ class BrowserServer {
868
867
  resolve(distRoot, "client/esm-client-injector.js"),
869
868
  "utf8"
870
869
  ).then((js) => this.injectorJs = js);
870
+ this.errorCatcherPath = resolve(distRoot, "client/error-catcher.js");
871
871
  this.stateJs = readFile$1(
872
872
  resolve(distRoot, "state.js"),
873
873
  "utf-8"
@@ -881,6 +881,7 @@ class BrowserServer {
881
881
  testerHtml;
882
882
  orchestratorHtml;
883
883
  injectorJs;
884
+ errorCatcherPath;
884
885
  stateJs;
885
886
  state;
886
887
  provider;
@@ -952,7 +953,7 @@ class BrowserServer {
952
953
  ...options
953
954
  });
954
955
  }
955
- cdpSessions = /* @__PURE__ */ new Map();
956
+ cdpSessionsPromises = /* @__PURE__ */ new Map();
956
957
  async ensureCDPHandler(contextId, sessionId) {
957
958
  const cachedHandler = this.state.cdps.get(sessionId);
958
959
  if (cachedHandler) {
@@ -962,11 +963,11 @@ class BrowserServer {
962
963
  if (!provider.getCDPSession) {
963
964
  throw new Error(`CDP is not supported by the provider "${provider.name}".`);
964
965
  }
965
- const promise = this.cdpSessions.get(sessionId) ?? await (async () => {
966
+ const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
966
967
  const promise2 = provider.getCDPSession(contextId).finally(() => {
967
- this.cdpSessions.delete(sessionId);
968
+ this.cdpSessionsPromises.delete(sessionId);
968
969
  });
969
- this.cdpSessions.set(sessionId, promise2);
970
+ this.cdpSessionsPromises.set(sessionId, promise2);
970
971
  return promise2;
971
972
  })();
972
973
  const session = await promise;
@@ -1433,7 +1434,7 @@ const keyboard = async (context, text) => {
1433
1434
  throw new TypeError(`Provider "${context.provider.name}" does not support selecting all text`);
1434
1435
  }
1435
1436
  },
1436
- false
1437
+ true
1437
1438
  );
1438
1439
  };
1439
1440
  async function keyboardImplementation(provider, contextId, text, selectAll, skipRelease) {
@@ -1662,7 +1663,7 @@ const hover = async (context, selector, options = {}) => {
1662
1663
  };
1663
1664
 
1664
1665
  function assertFileAccess(path, project) {
1665
- if (!isFileServingAllowed$1(path, project.server) && !isFileServingAllowed$1(path, project.ctx.server)) {
1666
+ if (!isFileServingAllowed(path, project.server) && !isFileServingAllowed(path, project.ctx.server)) {
1666
1667
  throw new Error(
1667
1668
  `Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`
1668
1669
  );
@@ -1930,6 +1931,7 @@ async function resolveOrchestrator(server, url, res) {
1930
1931
  __VITEST_FILES__: JSON.stringify(files),
1931
1932
  __VITEST_TYPE__: '"orchestrator"',
1932
1933
  __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1934
+ __VITEST_TESTER_ID__: '"none"',
1933
1935
  __VITEST_PROVIDED_CONTEXT__: "{}"
1934
1936
  });
1935
1937
  res.removeHeader("Content-Security-Policy");
@@ -1946,7 +1948,8 @@ async function resolveOrchestrator(server, url, res) {
1946
1948
  baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
1947
1949
  "<!-- !LOAD_METADATA! -->",
1948
1950
  [
1949
- "<script>{__VITEST_INJECTOR__}<\/script>",
1951
+ "{__VITEST_INJECTOR__}",
1952
+ "{__VITEST_ERROR_CATCHER__}",
1950
1953
  "{__VITEST_SCRIPTS__}",
1951
1954
  `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
1952
1955
  ].join("\n")
@@ -1956,7 +1959,8 @@ async function resolveOrchestrator(server, url, res) {
1956
1959
  __VITEST_FAVICON__: server.faviconUrl,
1957
1960
  __VITEST_TITLE__: "Vitest Browser Runner",
1958
1961
  __VITEST_SCRIPTS__: server.orchestratorScripts,
1959
- __VITEST_INJECTOR__: injector,
1962
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
1963
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherPath}"><\/script>`,
1960
1964
  __VITEST_CONTEXT_ID__: JSON.stringify(contextId)
1961
1965
  });
1962
1966
  }
@@ -1989,6 +1993,7 @@ async function resolveTester(server, url, res) {
1989
1993
  }),
1990
1994
  __VITEST_TYPE__: '"tester"',
1991
1995
  __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1996
+ __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
1992
1997
  __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
1993
1998
  });
1994
1999
  if (!server.testerScripts) {
@@ -2005,7 +2010,8 @@ async function resolveTester(server, url, res) {
2005
2010
  __VITEST_FAVICON__: server.faviconUrl,
2006
2011
  __VITEST_TITLE__: "Vitest Browser Tester",
2007
2012
  __VITEST_SCRIPTS__: server.testerScripts,
2008
- __VITEST_INJECTOR__: injector,
2013
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
2014
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherPath}"><\/script>`,
2009
2015
  __VITEST_APPEND__: `<script type="module">
2010
2016
  __vitest_browser_runner__.runningFiles = ${tests}
2011
2017
  __vitest_browser_runner__.iframeId = ${iframeId}
@@ -2018,7 +2024,6 @@ var BrowserPlugin = (browserServer, base = "/") => {
2018
2024
  const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
2019
2025
  const distRoot = resolve(pkgRoot, "dist");
2020
2026
  const project = browserServer.project;
2021
- let loupePath;
2022
2027
  return [
2023
2028
  {
2024
2029
  enforce: "pre",
@@ -2035,7 +2040,7 @@ var BrowserPlugin = (browserServer, base = "/") => {
2035
2040
  next();
2036
2041
  });
2037
2042
  server.middlewares.use(async function vitestBrowserMode(req, res, next) {
2038
- if (!req.url) {
2043
+ if (!req.url || !browserServer.provider) {
2039
2044
  return next();
2040
2045
  }
2041
2046
  const url = new URL(req.url, "http://localhost");
@@ -2144,6 +2149,8 @@ var BrowserPlugin = (browserServer, base = "/") => {
2144
2149
  "vitest/utils",
2145
2150
  "vitest/browser",
2146
2151
  "vitest/runners",
2152
+ "@vitest/browser",
2153
+ "@vitest/browser/client",
2147
2154
  "@vitest/utils",
2148
2155
  "@vitest/utils/source-map",
2149
2156
  "@vitest/runner",
@@ -2154,21 +2161,15 @@ var BrowserPlugin = (browserServer, base = "/") => {
2154
2161
  "std-env",
2155
2162
  "tinybench",
2156
2163
  "tinyspy",
2164
+ "tinyrainbow",
2157
2165
  "pathe",
2158
2166
  "msw",
2159
2167
  "msw/browser"
2160
2168
  ],
2161
2169
  include: [
2162
- "vitest > @vitest/utils > pretty-format",
2163
- "vitest > @vitest/snapshot > pretty-format",
2164
2170
  "vitest > @vitest/snapshot > magic-string",
2165
- "vitest > pretty-format",
2166
- "vitest > pretty-format > ansi-styles",
2167
- "vitest > pretty-format > ansi-regex",
2168
2171
  "vitest > chai",
2169
2172
  "vitest > chai > loupe",
2170
- "vitest > @vitest/runner > pretty-format",
2171
- "vitest > @vitest/utils > diff-sequences",
2172
2173
  "vitest > @vitest/utils > loupe",
2173
2174
  "@vitest/browser > @testing-library/user-event",
2174
2175
  "@vitest/browser > @testing-library/dom"
@@ -2193,17 +2194,6 @@ var BrowserPlugin = (browserServer, base = "/") => {
2193
2194
  {
2194
2195
  name: "vitest:browser:resolve-virtual",
2195
2196
  async resolveId(rawId) {
2196
- if (rawId.startsWith("/__virtual_vitest__")) {
2197
- const url = new URL(rawId, "http://localhost");
2198
- if (!url.searchParams.has("id")) {
2199
- return;
2200
- }
2201
- const id = decodeURIComponent(url.searchParams.get("id"));
2202
- const resolved = await this.resolve(id, distRoot, {
2203
- skipSelf: true
2204
- });
2205
- return resolved;
2206
- }
2207
2197
  if (rawId === "/__vitest_msw__") {
2208
2198
  return this.resolve("msw/mockServiceWorker.js", distRoot, {
2209
2199
  skipSelf: true
@@ -2218,11 +2208,8 @@ var BrowserPlugin = (browserServer, base = "/") => {
2218
2208
  return resolve(distRoot, "client", id.slice(1));
2219
2209
  }
2220
2210
  },
2221
- configResolved(config) {
2222
- loupePath = resolve(config.cacheDir, "deps/loupe.js");
2223
- },
2224
2211
  transform(code, id) {
2225
- if (id.startsWith(loupePath)) {
2212
+ if (id.includes(browserServer.vite.config.cacheDir) && id.includes("loupe.js")) {
2226
2213
  const utilRequire = "nodeUtil = require_util();";
2227
2214
  return code.replace(utilRequire, " ".repeat(utilRequire.length));
2228
2215
  }