@vitest/browser 2.0.0-beta.11 → 2.0.0-beta.12

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.
@@ -25,8 +25,8 @@
25
25
  </style>
26
26
  <script>{__VITEST_INJECTOR__}</script>
27
27
  {__VITEST_SCRIPTS__}
28
- <script type="module" crossorigin src="/__vitest_browser__/orchestrator-DQ4hbmZ_.js"></script>
29
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-D6HtJ5Rb.js">
28
+ <script type="module" crossorigin src="/__vitest_browser__/orchestrator-DJ6H4qlM.js"></script>
29
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-Dz5Ebwug.js">
30
30
  </head>
31
31
  <body>
32
32
  <div id="vitest-tester"></div>
@@ -19,8 +19,8 @@
19
19
  <script>{__VITEST_INJECTOR__}</script>
20
20
  <script>{__VITEST_STATE__}</script>
21
21
  {__VITEST_SCRIPTS__}
22
- <script type="module" crossorigin src="/__vitest_browser__/tester-IF8AbWCS.js"></script>
23
- <link rel="modulepreload" crossorigin href="/__vitest_browser__/rpc-D6HtJ5Rb.js">
22
+ <script type="module" crossorigin src="/__vitest_browser__/tester-DHXll_4H.js"></script>
23
+ <link rel="modulepreload" crossorigin href="/__vitest_browser__/client-Dz5Ebwug.js">
24
24
  </head>
25
25
  <body
26
26
  data-vitest-body
package/dist/context.js CHANGED
@@ -125,6 +125,9 @@ function getSimpleSelectOptions(element, value) {
125
125
  return v;
126
126
  });
127
127
  }
128
+ function cdp() {
129
+ return runner().cdp;
130
+ }
128
131
  const screenshotIds = {};
129
132
  const page = {
130
133
  get config() {
@@ -172,4 +175,4 @@ function getTaskFullName(task) {
172
175
  return task.suite ? `${getTaskFullName(task.suite)} ${task.name}` : task.name;
173
176
  }
174
177
 
175
- export { page, userEvent };
178
+ export { cdp, page, userEvent };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BrowserServerState as BrowserServerState$1, BrowserServerStateContext, BrowserServer as BrowserServer$1, WorkspaceProject, Vite, BrowserProvider, BrowserScript, Vitest, ProcessPool } from 'vitest/node';
1
+ import { CDPSession, BrowserServerState as BrowserServerState$1, BrowserServerStateContext, BrowserServer as BrowserServer$1, WorkspaceProject, Vite, BrowserProvider, BrowserScript, Vitest, ProcessPool } from 'vitest/node';
2
2
  import { Plugin } from 'vitest/config';
3
3
  import { File, TaskResultPack, AfterSuiteRunMeta, CancelReason, UserConsoleLog, SnapshotResult, ResolvedConfig } from 'vitest';
4
4
 
@@ -46,10 +46,13 @@ interface WebSocketBrowserHandlers {
46
46
  getBrowserFileSourceMap: (id: string) => SourceMap | null | {
47
47
  mappings: '';
48
48
  } | undefined;
49
+ sendCdpEvent: (contextId: string, event: string, payload?: Record<string, unknown>) => unknown;
50
+ trackCdpEvent: (contextId: string, type: 'on' | 'once' | 'off', event: string, listenerId: string) => void;
49
51
  }
50
52
  interface WebSocketBrowserEvents {
51
53
  onCancel: (reason: CancelReason) => void;
52
54
  createTesters: (files: string[]) => Promise<void>;
55
+ cdpEvent: (event: string, payload: unknown) => void;
53
56
  }
54
57
  type WebSocketBrowserRPC = BirpcReturn<WebSocketBrowserEvents, WebSocketBrowserHandlers>;
55
58
  interface SourceMap {
@@ -63,12 +66,26 @@ interface SourceMap {
63
66
  toUrl: () => string;
64
67
  }
65
68
 
69
+ declare class BrowserServerCDPHandler {
70
+ private session;
71
+ private tester;
72
+ private listenerIds;
73
+ private listeners;
74
+ constructor(session: CDPSession, tester: WebSocketBrowserRPC);
75
+ send(method: string, params?: Record<string, unknown>): Promise<unknown>;
76
+ on(event: string, id: string, once?: boolean): void;
77
+ off(event: string, id: string): void;
78
+ once(event: string, listener: string): void;
79
+ }
80
+
66
81
  declare class BrowserServerState implements BrowserServerState$1 {
67
- orchestrators: Map<string, WebSocketBrowserRPC>;
68
- testers: Map<string, WebSocketBrowserRPC>;
82
+ readonly orchestrators: Map<string, WebSocketBrowserRPC>;
83
+ readonly testers: Map<string, WebSocketBrowserRPC>;
84
+ readonly cdps: Map<string, BrowserServerCDPHandler>;
69
85
  private contexts;
70
86
  getContext(contextId: string): BrowserServerStateContext | undefined;
71
87
  createAsyncContext(contextId: string, files: string[]): Promise<void>;
88
+ removeCDPHandler(sessionId: string): Promise<void>;
72
89
  }
73
90
 
74
91
  declare class BrowserServer implements BrowserServer$1 {
@@ -95,6 +112,8 @@ declare class BrowserServer implements BrowserServer$1 {
95
112
  };
96
113
  formatScripts(scripts: BrowserScript[] | undefined): Promise<string>;
97
114
  initBrowserProvider(): Promise<void>;
115
+ private cdpSessions;
116
+ ensureCDPHandler(contextId: string, sessionId: string): Promise<BrowserServerCDPHandler>;
98
117
  close(): Promise<void>;
99
118
  }
100
119
 
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url';
8
8
  import { createDefer, slash, toArray } from '@vitest/utils';
9
9
  import sirv from 'sirv';
10
10
  import { defaultBrowserPort, coverageConfigDefaults } from 'vitest/config';
11
- import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-BRud6NtS.js';
11
+ import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from './webdriver-CJA71Bgl.js';
12
12
  import { resolve as resolve$1, dirname as dirname$1, normalize as normalize$1 } from 'node:path';
13
13
  import MagicString from 'magic-string';
14
14
  import { esmWalker } from '@vitest/utils/ast';
@@ -517,7 +517,7 @@ function setupBrowserRpc(server) {
517
517
  const sessionId = searchParams.get("sessionId") ?? "0";
518
518
  wss.handleUpgrade(request, socket, head, (ws) => {
519
519
  wss.emit("connection", ws, request);
520
- const rpc = setupClient(ws);
520
+ const rpc = setupClient(sessionId, ws);
521
521
  const state = server.state;
522
522
  const clients = type === "tester" ? state.testers : state.orchestrators;
523
523
  clients.set(sessionId, rpc);
@@ -525,6 +525,7 @@ function setupBrowserRpc(server) {
525
525
  ws.on("close", () => {
526
526
  debug$1?.("[%s] Browser API disconnected from %s", sessionId, type);
527
527
  clients.delete(sessionId);
528
+ server.state.removeCDPHandler(sessionId);
528
529
  });
529
530
  });
530
531
  });
@@ -535,7 +536,7 @@ function setupBrowserRpc(server) {
535
536
  );
536
537
  }
537
538
  }
538
- function setupClient(ws) {
539
+ function setupClient(sessionId, ws) {
539
540
  const rpc = createBirpc(
540
541
  {
541
542
  async onUnhandledError(error, type) {
@@ -653,12 +654,21 @@ function setupBrowserRpc(server) {
653
654
  moduleGraph.invalidateModule(module, /* @__PURE__ */ new Set(), Date.now(), true);
654
655
  }
655
656
  });
657
+ },
658
+ // CDP
659
+ async sendCdpEvent(contextId, event, payload) {
660
+ const cdp = await server.ensureCDPHandler(contextId, sessionId);
661
+ return cdp.send(event, payload);
662
+ },
663
+ async trackCdpEvent(contextId, type, event, listenerId) {
664
+ const cdp = await server.ensureCDPHandler(contextId, sessionId);
665
+ cdp[type](event, listenerId);
656
666
  }
657
667
  },
658
668
  {
659
669
  post: (msg) => ws.send(msg),
660
670
  on: (fn) => ws.on("message", fn),
661
- eventNames: ["onCancel"],
671
+ eventNames: ["onCancel", "cdpEvent"],
662
672
  serialize: (data) => stringify(data, stringifyReplace),
663
673
  deserialize: parse,
664
674
  onTimeoutError(functionName) {
@@ -696,6 +706,7 @@ function stringifyReplace(key, value) {
696
706
  class BrowserServerState {
697
707
  orchestrators = /* @__PURE__ */ new Map();
698
708
  testers = /* @__PURE__ */ new Map();
709
+ cdps = /* @__PURE__ */ new Map();
699
710
  contexts = /* @__PURE__ */ new Map();
700
711
  getContext(contextId) {
701
712
  return this.contexts.get(contextId);
@@ -712,6 +723,9 @@ class BrowserServerState {
712
723
  });
713
724
  return defer;
714
725
  }
726
+ async removeCDPHandler(sessionId) {
727
+ this.cdps.delete(sessionId);
728
+ }
715
729
  }
716
730
 
717
731
  function replacer(code, values) {
@@ -743,6 +757,49 @@ async function getBrowserProvider(options, project) {
743
757
  return customProviderModule.default;
744
758
  }
745
759
 
760
+ class BrowserServerCDPHandler {
761
+ constructor(session, tester) {
762
+ this.session = session;
763
+ this.tester = tester;
764
+ }
765
+ listenerIds = {};
766
+ listeners = {};
767
+ send(method, params) {
768
+ return this.session.send(method, params);
769
+ }
770
+ on(event, id, once = false) {
771
+ if (!this.listenerIds[event]) {
772
+ this.listenerIds[event] = [];
773
+ }
774
+ this.listenerIds[event].push(id);
775
+ if (!this.listeners[event]) {
776
+ this.listeners[event] = (payload) => {
777
+ this.tester.cdpEvent(
778
+ event,
779
+ payload
780
+ );
781
+ if (once) {
782
+ this.off(event, id);
783
+ }
784
+ };
785
+ this.session.on(event, this.listeners[event]);
786
+ }
787
+ }
788
+ off(event, id) {
789
+ if (!this.listenerIds[event]) {
790
+ this.listenerIds[event] = [];
791
+ }
792
+ this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
793
+ if (!this.listenerIds[event].length) {
794
+ this.session.off(event, this.listeners[event]);
795
+ delete this.listeners[event];
796
+ }
797
+ }
798
+ once(event, listener) {
799
+ this.on(event, listener, true);
800
+ }
801
+ }
802
+
746
803
  class BrowserServer {
747
804
  constructor(project, base) {
748
805
  this.project = project;
@@ -837,6 +894,35 @@ class BrowserServer {
837
894
  options: providerOptions
838
895
  });
839
896
  }
897
+ cdpSessions = /* @__PURE__ */ new Map();
898
+ async ensureCDPHandler(contextId, sessionId) {
899
+ const cachedHandler = this.state.cdps.get(sessionId);
900
+ if (cachedHandler) {
901
+ return cachedHandler;
902
+ }
903
+ const provider = this.provider;
904
+ if (!provider.getCDPSession) {
905
+ throw new Error(`CDP is not supported by the provider "${provider.name}".`);
906
+ }
907
+ const promise = this.cdpSessions.get(sessionId) ?? await (async () => {
908
+ const promise2 = provider.getCDPSession(contextId).finally(() => {
909
+ this.cdpSessions.delete(sessionId);
910
+ });
911
+ this.cdpSessions.set(sessionId, promise2);
912
+ return promise2;
913
+ })();
914
+ const session = await promise;
915
+ const rpc = this.state.testers.get(sessionId);
916
+ if (!rpc) {
917
+ throw new Error(`Tester RPC "${sessionId}" was not established.`);
918
+ }
919
+ const handler = new BrowserServerCDPHandler(session, rpc);
920
+ this.state.cdps.set(
921
+ sessionId,
922
+ handler
923
+ );
924
+ return handler;
925
+ }
840
926
  async close() {
841
927
  await this.vite.close();
842
928
  }
@@ -852,7 +938,7 @@ function wrapConfig(config) {
852
938
  const click = async (context, xpath, options = {}) => {
853
939
  const provider = context.provider;
854
940
  if (provider instanceof PlaywrightBrowserProvider) {
855
- const tester = context.frame;
941
+ const tester = context.iframe;
856
942
  await tester.locator(`xpath=${xpath}`).click({
857
943
  timeout: 1e3,
858
944
  ...options
@@ -868,7 +954,7 @@ const click = async (context, xpath, options = {}) => {
868
954
  const dblClick = async (context, xpath, options = {}) => {
869
955
  const provider = context.provider;
870
956
  if (provider instanceof PlaywrightBrowserProvider) {
871
- const tester = context.frame;
957
+ const tester = context.iframe;
872
958
  await tester.locator(`xpath=${xpath}`).dblclick(options);
873
959
  } else if (provider instanceof WebdriverBrowserProvider) {
874
960
  const browser = context.browser;
@@ -1250,7 +1336,8 @@ const keyboard = async (context, text) => {
1250
1336
  }
1251
1337
  }
1252
1338
  if (context.provider instanceof PlaywrightBrowserProvider) {
1253
- await context.frame.evaluate(focusIframe);
1339
+ const frame = await context.frame();
1340
+ await frame.evaluate(focusIframe);
1254
1341
  } else if (context.provider instanceof WebdriverBrowserProvider) {
1255
1342
  await context.browser.execute(focusIframe);
1256
1343
  }
@@ -1266,7 +1353,8 @@ const keyboard = async (context, text) => {
1266
1353
  }
1267
1354
  }
1268
1355
  if (context.provider instanceof PlaywrightBrowserProvider) {
1269
- await context.frame.evaluate(selectAll);
1356
+ const frame = await context.frame();
1357
+ await frame.evaluate(selectAll);
1270
1358
  } else if (context.provider instanceof WebdriverBrowserProvider) {
1271
1359
  await context.browser.execute(selectAll);
1272
1360
  } else {
@@ -1350,8 +1438,8 @@ async function keyboardImplementation(provider, contextId, text, selectAll, skip
1350
1438
  const type = async (context, xpath, text, options = {}) => {
1351
1439
  const { skipClick = false, skipAutoClose = false } = options;
1352
1440
  if (context.provider instanceof PlaywrightBrowserProvider) {
1353
- const { frame } = context;
1354
- const element = frame.locator(`xpath=${xpath}`);
1441
+ const { iframe } = context;
1442
+ const element = iframe.locator(`xpath=${xpath}`);
1355
1443
  if (!skipClick) {
1356
1444
  await element.focus();
1357
1445
  }
@@ -1388,8 +1476,8 @@ const type = async (context, xpath, text, options = {}) => {
1388
1476
 
1389
1477
  const clear = async (context, xpath) => {
1390
1478
  if (context.provider instanceof PlaywrightBrowserProvider) {
1391
- const { frame } = context;
1392
- const element = frame.locator(`xpath=${xpath}`);
1479
+ const { iframe } = context;
1480
+ const element = iframe.locator(`xpath=${xpath}`);
1393
1481
  await element.clear({
1394
1482
  timeout: 1e3
1395
1483
  });
@@ -1405,8 +1493,8 @@ const clear = async (context, xpath) => {
1405
1493
 
1406
1494
  const fill = async (context, xpath, text, options = {}) => {
1407
1495
  if (context.provider instanceof PlaywrightBrowserProvider) {
1408
- const { frame } = context;
1409
- const element = frame.locator(`xpath=${xpath}`);
1496
+ const { iframe } = context;
1497
+ const element = iframe.locator(`xpath=${xpath}`);
1410
1498
  await element.fill(text, { timeout: 1e3, ...options });
1411
1499
  } else if (context.provider instanceof WebdriverBrowserProvider) {
1412
1500
  const browser = context.browser;
@@ -1420,13 +1508,13 @@ const fill = async (context, xpath, text, options = {}) => {
1420
1508
  const selectOptions = async (context, xpath, userValues, options = {}) => {
1421
1509
  if (context.provider instanceof PlaywrightBrowserProvider) {
1422
1510
  const value = userValues;
1423
- const { frame } = context;
1424
- const selectElement = frame.locator(`xpath=${xpath}`);
1511
+ const { iframe } = context;
1512
+ const selectElement = iframe.locator(`xpath=${xpath}`);
1425
1513
  const values = await Promise.all(value.map(async (v) => {
1426
1514
  if (typeof v === "string") {
1427
1515
  return v;
1428
1516
  }
1429
- const elementHandler = await frame.locator(`xpath=${v.element}`).elementHandle();
1517
+ const elementHandler = await iframe.locator(`xpath=${v.element}`).elementHandle();
1430
1518
  if (!elementHandler) {
1431
1519
  throw new Error(`Element not found: ${v.element}`);
1432
1520
  }
@@ -1472,7 +1560,8 @@ const tab = async (context, options = {}) => {
1472
1560
 
1473
1561
  const dragAndDrop = async (context, source, target, options) => {
1474
1562
  if (context.provider instanceof PlaywrightBrowserProvider) {
1475
- await context.frame.dragAndDrop(
1563
+ const frame = await context.frame();
1564
+ await frame.dragAndDrop(
1476
1565
  `xpath=${source}`,
1477
1566
  `xpath=${target}`,
1478
1567
  {
@@ -1494,7 +1583,7 @@ const dragAndDrop = async (context, source, target, options) => {
1494
1583
 
1495
1584
  const hover = async (context, xpath, options = {}) => {
1496
1585
  if (context.provider instanceof PlaywrightBrowserProvider) {
1497
- await context.frame.locator(`xpath=${xpath}`).hover({
1586
+ await context.iframe.locator(`xpath=${xpath}`).hover({
1498
1587
  timeout: 1e3,
1499
1588
  ...options
1500
1589
  });
@@ -1551,11 +1640,13 @@ const screenshot = async (context, name, options = {}) => {
1551
1640
  if (context.provider instanceof PlaywrightBrowserProvider) {
1552
1641
  if (options.element) {
1553
1642
  const { element: elementXpath, ...config } = options;
1554
- const iframe = context.frame;
1555
- const element = iframe.locator(`xpath=${elementXpath}`);
1643
+ const element = context.iframe.locator(`xpath=${elementXpath}`);
1556
1644
  await element.screenshot({ ...config, path: savePath });
1557
1645
  } else {
1558
- await context.frame.locator("body").screenshot({ ...options, path: savePath });
1646
+ await context.iframe.locator("body").screenshot({
1647
+ ...options,
1648
+ path: savePath
1649
+ });
1559
1650
  }
1560
1651
  return path;
1561
1652
  }
@@ -1650,7 +1741,7 @@ async function generateContextFile(server) {
1650
1741
  );
1651
1742
  const distContextPath = slash(`/@fs/${resolve(__dirname, "context.js")}`);
1652
1743
  return `
1653
- import { page, userEvent as __userEvent_CDP__ } from '${distContextPath}'
1744
+ import { page, userEvent as __userEvent_CDP__, cdp } from '${distContextPath}'
1654
1745
  ${userEventNonProviderImport}
1655
1746
  const filepath = () => ${filepathCode}
1656
1747
  const rpc = () => __vitest_worker__.rpc
@@ -1667,7 +1758,7 @@ export const server = {
1667
1758
  }
1668
1759
  export const commands = server.commands
1669
1760
  export const userEvent = ${getUserEvent(provider)}
1670
- export { page }
1761
+ export { page, cdp }
1671
1762
  `;
1672
1763
  }
1673
1764
  function getUserEvent(provider) {
@@ -1713,11 +1804,13 @@ ${err.message}`);
1713
1804
  onImportMeta() {
1714
1805
  },
1715
1806
  onDynamicImport(node) {
1716
- const replace = "__vitest_browser_runner__.wrapModule(() => import(";
1807
+ const replaceString = "__vitest_browser_runner__.wrapModule(() => import(";
1808
+ const importSubstring = code.substring(node.start, node.end);
1809
+ const hasIgnore = importSubstring.includes("/* @vite-ignore */");
1717
1810
  s.overwrite(
1718
1811
  node.start,
1719
1812
  node.source.start,
1720
- replace
1813
+ replaceString + (hasIgnore ? "/* @vite-ignore */ " : "")
1721
1814
  );
1722
1815
  s.overwrite(node.end - 1, node.end, "))");
1723
1816
  }
@@ -1848,6 +1941,7 @@ var BrowserPlugin = (browserServer, base = "/") => {
1848
1941
  const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
1849
1942
  const distRoot = resolve(pkgRoot, "dist");
1850
1943
  const project = browserServer.project;
1944
+ let loupePath;
1851
1945
  return [
1852
1946
  {
1853
1947
  enforce: "pre",
@@ -1926,16 +2020,45 @@ var BrowserPlugin = (browserServer, base = "/") => {
1926
2020
  (file) => getFilePoolName(project, file) === "browser"
1927
2021
  );
1928
2022
  const setupFiles = toArray(project.config.setupFiles);
2023
+ const define = {};
2024
+ for (const env in project.config.env || {}) {
2025
+ const stringValue = JSON.stringify(project.config.env[env]);
2026
+ define[`process.env.${env}`] = stringValue;
2027
+ define[`import.meta.env.${env}`] = stringValue;
2028
+ }
2029
+ const entries = [
2030
+ ...browserTestFiles,
2031
+ ...setupFiles,
2032
+ resolve(distDir, "index.js"),
2033
+ resolve(distDir, "browser.js"),
2034
+ resolve(distDir, "runners.js"),
2035
+ resolve(distDir, "utils.js"),
2036
+ ...project.config.snapshotSerializers || []
2037
+ ];
2038
+ if (project.config.diff) {
2039
+ entries.push(project.config.diff);
2040
+ }
2041
+ if (project.ctx.coverageProvider) {
2042
+ const coverage = project.ctx.config.coverage;
2043
+ const provider = coverage.provider;
2044
+ if (provider === "v8") {
2045
+ const path = tryResolve("@vitest/coverage-v8", [project.ctx.config.root]);
2046
+ if (path) {
2047
+ entries.push(path);
2048
+ }
2049
+ } else if (provider === "istanbul") {
2050
+ const path = tryResolve("@vitest/coverage-istanbul", [project.ctx.config.root]);
2051
+ if (path) {
2052
+ entries.push(path);
2053
+ }
2054
+ } else if (provider === "custom" && coverage.customProviderModule) {
2055
+ entries.push(coverage.customProviderModule);
2056
+ }
2057
+ }
1929
2058
  return {
2059
+ define,
1930
2060
  optimizeDeps: {
1931
- entries: [
1932
- ...browserTestFiles,
1933
- ...setupFiles,
1934
- resolve(distDir, "index.js"),
1935
- resolve(distDir, "browser.js"),
1936
- resolve(distDir, "runners.js"),
1937
- resolve(distDir, "utils.js")
1938
- ],
2061
+ entries,
1939
2062
  exclude: [
1940
2063
  "vitest",
1941
2064
  "vitest/utils",
@@ -1966,6 +2089,7 @@ var BrowserPlugin = (browserServer, base = "/") => {
1966
2089
  "vitest > chai > loupe",
1967
2090
  "vitest > @vitest/runner > p-limit",
1968
2091
  "vitest > @vitest/utils > diff-sequences",
2092
+ "vitest > @vitest/utils > loupe",
1969
2093
  "@vitest/browser > @testing-library/user-event",
1970
2094
  "@vitest/browser > @testing-library/dom"
1971
2095
  ]
@@ -1992,7 +2116,7 @@ var BrowserPlugin = (browserServer, base = "/") => {
1992
2116
  if (rawId.startsWith("/__virtual_vitest__")) {
1993
2117
  const url = new URL(rawId, "http://localhost");
1994
2118
  if (!url.searchParams.has("id")) {
1995
- throw new TypeError(`Invalid virtual module id: ${rawId}, requires "id" query.`);
2119
+ return;
1996
2120
  }
1997
2121
  const id = decodeURIComponent(url.searchParams.get("id"));
1998
2122
  const resolved = await this.resolve(id, distRoot, {
@@ -2013,6 +2137,15 @@ var BrowserPlugin = (browserServer, base = "/") => {
2013
2137
  if (id.startsWith("/__vitest_browser__/") || id.startsWith("/__vitest__/")) {
2014
2138
  return resolve(distRoot, "client", id.slice(1));
2015
2139
  }
2140
+ },
2141
+ configResolved(config) {
2142
+ loupePath = resolve(config.cacheDir, "deps/loupe.js");
2143
+ },
2144
+ transform(code, id) {
2145
+ if (id.startsWith(loupePath)) {
2146
+ const utilRequire = "nodeUtil = require_util();";
2147
+ return code.replace(utilRequire, " ".repeat(utilRequire.length));
2148
+ }
2016
2149
  }
2017
2150
  },
2018
2151
  BrowserContext(browserServer),
@@ -2022,10 +2155,8 @@ var BrowserPlugin = (browserServer, base = "/") => {
2022
2155
  enforce: "post",
2023
2156
  async config(viteConfig) {
2024
2157
  // Enables using ignore hint for coverage providers with @preserve keyword
2025
- if (viteConfig.esbuild !== false) {
2026
- viteConfig.esbuild ||= {};
2027
- viteConfig.esbuild.legalComments = "inline";
2028
- }
2158
+ viteConfig.esbuild ||= {};
2159
+ viteConfig.esbuild.legalComments = "inline";
2029
2160
  const server = resolveApiServerConfig(
2030
2161
  viteConfig.test?.browser || {},
2031
2162
  defaultBrowserPort
@@ -2055,18 +2186,31 @@ var BrowserPlugin = (browserServer, base = "/") => {
2055
2186
  },
2056
2187
  // TODO: remove this when @testing-library/vue supports ESM
2057
2188
  {
2058
- name: "vitest:browser:support-vue-testing-library",
2189
+ name: "vitest:browser:support-testing-library",
2059
2190
  config() {
2060
2191
  return {
2192
+ define: {
2193
+ // testing-library/preact
2194
+ "process.env.PTL_SKIP_AUTO_CLEANUP": !!process.env.PTL_SKIP_AUTO_CLEANUP,
2195
+ // testing-library/react
2196
+ "process.env.RTL_SKIP_AUTO_CLEANUP": !!process.env.RTL_SKIP_AUTO_CLEANUP,
2197
+ "process.env?.RTL_SKIP_AUTO_CLEANUP": !!process.env.RTL_SKIP_AUTO_CLEANUP,
2198
+ // testing-library/svelte, testing-library/solid
2199
+ "process.env.STL_SKIP_AUTO_CLEANUP": !!process.env.STL_SKIP_AUTO_CLEANUP,
2200
+ // testing-library/vue
2201
+ "process.env.VTL_SKIP_AUTO_CLEANUP": !!process.env.VTL_SKIP_AUTO_CLEANUP,
2202
+ // dom.debug()
2203
+ "process.env.DEBUG_PRINT_LIMIT": process.env.DEBUG_PRINT_LIMIT || 7e3
2204
+ },
2061
2205
  optimizeDeps: {
2062
2206
  esbuildOptions: {
2063
2207
  plugins: [
2064
2208
  {
2065
2209
  name: "test-utils-rewrite",
2066
2210
  setup(build) {
2067
- const _require = createRequire(import.meta.url);
2068
2211
  build.onResolve({ filter: /@vue\/test-utils/ }, (args) => {
2069
- const resolved = _require.resolve(args.path, {
2212
+ const _require2 = getRequire();
2213
+ const resolved = _require2.resolve(args.path, {
2070
2214
  paths: [args.importer]
2071
2215
  });
2072
2216
  return { path: resolved };
@@ -2081,6 +2225,21 @@ var BrowserPlugin = (browserServer, base = "/") => {
2081
2225
  }
2082
2226
  ];
2083
2227
  };
2228
+ function tryResolve(path, paths) {
2229
+ try {
2230
+ const _require2 = getRequire();
2231
+ return _require2.resolve(path, { paths });
2232
+ } catch {
2233
+ return void 0;
2234
+ }
2235
+ }
2236
+ let _require;
2237
+ function getRequire() {
2238
+ if (!_require) {
2239
+ _require = createRequire(import.meta.url);
2240
+ }
2241
+ return _require;
2242
+ }
2084
2243
  function resolveCoverageFolder(project) {
2085
2244
  const options = project.ctx.config;
2086
2245
  const htmlReporter = options.coverage?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
@@ -2172,7 +2331,14 @@ function createBrowserPool(ctx) {
2172
2331
  files.push(file);
2173
2332
  groupedFiles.set(project, files);
2174
2333
  }
2334
+ let isCancelled = false;
2335
+ ctx.onCancel(() => {
2336
+ isCancelled = true;
2337
+ });
2175
2338
  for (const [project, files] of groupedFiles.entries()) {
2339
+ if (isCancelled) {
2340
+ break;
2341
+ }
2176
2342
  await runTests(project, files);
2177
2343
  }
2178
2344
  };
@@ -2206,14 +2372,13 @@ async function createBrowserServer(project, configFile, prePlugins = [], postPlu
2206
2372
  ...project.options,
2207
2373
  // spread project config inlined in root workspace config
2208
2374
  base: "/",
2209
- logLevel: "error",
2375
+ logLevel: process.env.VITEST_BROWSER_DEBUG ?? "info",
2210
2376
  mode: project.config.mode,
2211
2377
  configFile: configPath,
2212
2378
  // watch is handled by Vitest
2213
2379
  server: {
2214
2380
  hmr: false,
2215
- watch: null,
2216
- preTransformRequests: false
2381
+ watch: null
2217
2382
  },
2218
2383
  plugins: [
2219
2384
  ...prePlugins,
package/dist/providers.js CHANGED
@@ -1,4 +1,4 @@
1
- import { W as WebdriverBrowserProvider, P as PlaywrightBrowserProvider } from './webdriver-BRud6NtS.js';
1
+ import { W as WebdriverBrowserProvider, P as PlaywrightBrowserProvider } from './webdriver-CJA71Bgl.js';
2
2
 
3
3
  class PreviewBrowserProvider {
4
4
  name = "preview";
package/dist/state.js CHANGED
@@ -1 +1 @@
1
- const{parse:$parse,stringify:$stringify}=JSON;const{keys}=Object;const Primitive=String;const primitive="string";const ignore={};const object="object";const noop=(_,value)=>value;const primitives=value=>value instanceof Primitive?Primitive(value):value;const Primitives=(_,value)=>typeof value===primitive?new Primitive(value):value;const revive=(input,parsed,output,$)=>{const lazy=[];for(let ke=keys(output),{length}=ke,y=0;y<length;y++){const k=ke[y];const value=output[k];if(value instanceof Primitive){const tmp=input[value];if(typeof tmp===object&&!parsed.has(tmp)){parsed.add(tmp);output[k]=ignore;lazy.push({k,a:[input,parsed,tmp,$]})}else output[k]=$.call(output,k,tmp)}else if(output[k]!==ignore)output[k]=$.call(output,k,value)}for(let{length}=lazy,i=0;i<length;i++){const{k,a}=lazy[i];output[k]=$.call(output,k,revive.apply(null,a))}return output};const parse=(text,reviver)=>{const input=$parse(text,Primitives).map(primitives);const value=input[0];const $=noop;const tmp=typeof value===object&&value?revive(input,new Set,value,$):value;return $.call({"":tmp},"",tmp)};function getBrowserState(){return window.__vitest_browser_runner__}const config=getBrowserState().config;const providedContext=parse(getBrowserState().providedContext);const state={ctx:{pool:"browser",worker:"./browser.js",workerId:1,config,projectName:config.name||"",files:[],environment:{name:"browser",options:null},providedContext,invalidates:[]},onCancel:null,mockMap:new Map,config,environment:{name:"browser",transformMode:"web",setup(){throw new Error("Not called in the browser")}},moduleCache:getBrowserState().moduleCache,rpc:null,durations:{environment:0,prepare:performance.now()},providedContext};globalThis.__vitest_browser__=true;globalThis.__vitest_worker__=state;
1
+ const{parse:$parse,stringify:$stringify}=JSON;const{keys}=Object;const Primitive=String;const primitive="string";const ignore={};const object="object";const noop=(_,value)=>value;const primitives=value=>value instanceof Primitive?Primitive(value):value;const Primitives=(_,value)=>typeof value===primitive?new Primitive(value):value;const revive=(input,parsed,output,$)=>{const lazy=[];for(let ke=keys(output),{length}=ke,y=0;y<length;y++){const k=ke[y];const value=output[k];if(value instanceof Primitive){const tmp=input[value];if(typeof tmp===object&&!parsed.has(tmp)){parsed.add(tmp);output[k]=ignore;lazy.push({k,a:[input,parsed,tmp,$]})}else output[k]=$.call(output,k,tmp)}else if(output[k]!==ignore)output[k]=$.call(output,k,value)}for(let{length}=lazy,i=0;i<length;i++){const{k,a}=lazy[i];output[k]=$.call(output,k,revive.apply(null,a))}return output};const parse=(text,reviver)=>{const input=$parse(text,Primitives).map(primitives);const value=input[0];const $=noop;const tmp=typeof value===object&&value?revive(input,new Set,value,$):value;return $.call({"":tmp},"",tmp)};function getBrowserState(){return window.__vitest_browser_runner__}const config=getBrowserState().config;const contextId=getBrowserState().contextId;const providedContext=parse(getBrowserState().providedContext);const state={ctx:{pool:"browser",worker:"./browser.js",workerId:1,config,projectName:config.name||"",files:[],environment:{name:"browser",options:null},providedContext,invalidates:[]},onCancel:null,mockMap:new Map,config,environment:{name:"browser",transformMode:"web",setup(){throw new Error("Not called in the browser")}},moduleCache:getBrowserState().moduleCache,rpc:null,durations:{environment:0,prepare:performance.now()},providedContext};globalThis.__vitest_browser__=true;globalThis.__vitest_worker__=state;getBrowserState().cdp=createCdp();function rpc(){return state.rpc}function createCdp(){const listenersMap=new WeakMap;function getId(listener){const id=listenersMap.get(listener)||crypto.randomUUID();listenersMap.set(listener,id);return id}const listeners={};const error=err=>{window.dispatchEvent(new ErrorEvent("error",{error:err}))};const cdp={send(method,params){return rpc().sendCdpEvent(contextId,method,params)},on(event,listener){const listenerId=getId(listener);listeners[event]=listeners[event]||[];listeners[event].push(listener);rpc().trackCdpEvent(contextId,"on",event,listenerId).catch(error);return cdp},once(event,listener){const listenerId=getId(listener);const handler=data=>{listener(data);cdp.off(event,listener)};listeners[event]=listeners[event]||[];listeners[event].push(handler);rpc().trackCdpEvent(contextId,"once",event,listenerId).catch(error);return cdp},off(event,listener){const listenerId=getId(listener);if(listeners[event]){listeners[event]=listeners[event].filter(l=>l!==listener)}rpc().trackCdpEvent(contextId,"off",event,listenerId).catch(error);return cdp},emit(event,payload){if(listeners[event]){listeners[event].forEach(l=>{try{l(payload)}catch(err){error(err)}})}}};return cdp}