@vitest/browser 4.1.7 → 4.1.9

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.
@@ -4,7 +4,7 @@
4
4
  "name": "utils"
5
5
  },
6
6
  "orchestrator.html": {
7
- "file": "__vitest_browser__/orchestrator-B-wB8WWP.js",
7
+ "file": "__vitest_browser__/orchestrator-KVYrTcFi.js",
8
8
  "name": "orchestrator",
9
9
  "src": "orchestrator.html",
10
10
  "isEntry": true,
@@ -13,7 +13,7 @@
13
13
  ]
14
14
  },
15
15
  "tester/tester.html": {
16
- "file": "__vitest_browser__/tester-DQAp97S5.js",
16
+ "file": "__vitest_browser__/tester-BJtsJFpD.js",
17
17
  "name": "tester",
18
18
  "src": "tester/tester.html",
19
19
  "isEntry": true,
@@ -10,6 +10,8 @@ class IframeOrchestrator {
10
10
  cancelled = false;
11
11
  recreateNonIsolatedIframe = false;
12
12
  iframes = /* @__PURE__ */ new Map();
13
+ readyIframes = /* @__PURE__ */ new Set();
14
+ readyWaiters = /* @__PURE__ */ new Map();
13
15
  eventTarget = new EventTarget();
14
16
  traces;
15
17
  constructor() {
@@ -27,6 +29,9 @@ class IframeOrchestrator {
27
29
  "message",
28
30
  (e) => this.onGlobalChannelEvent(e)
29
31
  );
32
+ void client.waitForConnection().then(() => client.rpc.onOrchestratorReady()).catch((error) => {
33
+ debug("failed to notify orchestrator readiness", error);
34
+ });
30
35
  }
31
36
  async createTesters(options) {
32
37
  await this.traces.waitInit();
@@ -63,6 +68,8 @@ class IframeOrchestrator {
63
68
  }
64
69
  this.iframes.forEach((iframe) => iframe.remove());
65
70
  this.iframes.clear();
71
+ this.readyIframes.clear();
72
+ this.readyWaiters.clear();
66
73
  for (let i = 0; i < options.files.length; i++) {
67
74
  if (this.cancelled) {
68
75
  await endSpan();
@@ -104,8 +111,7 @@ class IframeOrchestrator {
104
111
  async runNonIsolatedTests(container, options, startTime, otelContext) {
105
112
  if (this.recreateNonIsolatedIframe) {
106
113
  this.recreateNonIsolatedIframe = false;
107
- this.iframes.get(ID_ALL).remove();
108
- this.iframes.delete(ID_ALL);
114
+ this.removeIframe(ID_ALL);
109
115
  debug("recreate non-isolated iframe");
110
116
  }
111
117
  if (!this.iframes.has(ID_ALL)) {
@@ -131,8 +137,7 @@ class IframeOrchestrator {
131
137
  const { width, height } = config.browser.viewport;
132
138
  const file = spec.filepath;
133
139
  if (this.iframes.has(file)) {
134
- this.iframes.get(file).remove();
135
- this.iframes.delete(file);
140
+ this.removeIframe(file);
136
141
  }
137
142
  const iframe = await this.prepareIframe(
138
143
  container,
@@ -181,12 +186,12 @@ Expected: ${iframe.src}`
181
186
  }
182
187
  } else {
183
188
  this.iframes.set(iframeId, iframe);
184
- this.sendEventToIframe({
189
+ this.waitForReady(iframeId).then(() => this.sendEventToIframe({
185
190
  event: "prepare",
186
191
  iframeId,
187
192
  startTime,
188
193
  otelCarrier: this.traces.getContextCarrier(otelContext)
189
- }).then(resolve, (error) => reject(this.dispatchIframeError(error)));
194
+ })).then(resolve, (error) => reject(this.dispatchIframeError(error)));
190
195
  }
191
196
  };
192
197
  iframe.onerror = (e) => {
@@ -201,6 +206,29 @@ Expected: ${iframe.src}`
201
206
  });
202
207
  return iframe;
203
208
  }
209
+ markReady(iframeId) {
210
+ this.readyIframes.add(iframeId);
211
+ const waiter = this.readyWaiters.get(iframeId);
212
+ if (waiter) {
213
+ this.readyWaiters.delete(iframeId);
214
+ waiter();
215
+ }
216
+ }
217
+ waitForReady(iframeId) {
218
+ if (this.readyIframes.has(iframeId)) {
219
+ return Promise.resolve();
220
+ }
221
+ return new Promise((resolve) => {
222
+ this.readyWaiters.set(iframeId, resolve);
223
+ });
224
+ }
225
+ removeIframe(iframeId) {
226
+ const iframe = this.iframes.get(iframeId);
227
+ this.iframes.delete(iframeId);
228
+ this.readyIframes.delete(iframeId);
229
+ this.readyWaiters.delete(iframeId);
230
+ iframe == null ? void 0 : iframe.remove();
231
+ }
204
232
  loggedIframe = /* @__PURE__ */ new WeakSet();
205
233
  createWarningMessage(iframeId, location) {
206
234
  return `The iframe${iframeId === ID_ALL ? "" : ` for "${iframeId}"`} was reloaded ${location}. This can lead to unexpected behavior during tests, duplicated test results or tests hanging.
@@ -257,6 +285,10 @@ If you are using a framework that manipulates browser history (like React Router
257
285
  async onIframeEvent(e) {
258
286
  debug("iframe event", JSON.stringify(e.data));
259
287
  switch (e.data.event) {
288
+ case "ready": {
289
+ this.markReady(e.data.iframeId);
290
+ break;
291
+ }
260
292
  case "viewport": {
261
293
  const { width, height, iframeId: id } = e.data;
262
294
  const iframe = this.iframes.get(id);
@@ -2151,6 +2151,10 @@ const commands = new CommandsManager();
2151
2151
  getBrowserState().commands = commands;
2152
2152
  getBrowserState().activeTraceTaskIds = /* @__PURE__ */ new Set();
2153
2153
  getBrowserState().iframeId = iframeId;
2154
+ channel.postMessage({
2155
+ event: "ready",
2156
+ iframeId
2157
+ });
2154
2158
  let contextSwitched = false;
2155
2159
  async function prepareTestEnvironment(options) {
2156
2160
  debug == null ? void 0 : debug("trying to resolve the runner");
@@ -26,7 +26,7 @@
26
26
  {__VITEST_INJECTOR__}
27
27
  {__VITEST_ERROR_CATCHER__}
28
28
  {__VITEST_SCRIPTS__}
29
- <script type="module" crossorigin src="/__vitest_browser__/orchestrator-B-wB8WWP.js"></script>
29
+ <script type="module" crossorigin src="/__vitest_browser__/orchestrator-KVYrTcFi.js"></script>
30
30
  <link rel="modulepreload" crossorigin href="/__vitest_browser__/utils-BsFW67Gk.js">
31
31
  </head>
32
32
  <body>
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" href="{__VITEST_FAVICON__}" type="image/svg+xml">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Vitest Browser Tester</title>
8
- <script type="module" crossorigin src="/__vitest_browser__/tester-DQAp97S5.js"></script>
8
+ <script type="module" crossorigin src="/__vitest_browser__/tester-BJtsJFpD.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/__vitest_browser__/utils-BsFW67Gk.js">
10
10
  </head>
11
11
  <body>
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { PNG } from 'pngjs';
18
18
  import { diff } from '@blazediff/core';
19
19
  import { WebSocketServer } from 'ws';
20
20
 
21
- var version = "4.1.7";
21
+ var version = "4.1.9";
22
22
 
23
23
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
24
24
  function normalizeWindowsPath(input = "") {
@@ -1431,6 +1431,19 @@ class BrowserServerCDPHandler {
1431
1431
  }
1432
1432
  }
1433
1433
 
1434
+ const _startV8Coverage = async (context) => {
1435
+ const session = await context.__ensureCDPHandler();
1436
+ await session.send("Profiler.enable");
1437
+ await session.send("Profiler.startPreciseCoverage", {
1438
+ callCount: true,
1439
+ detailed: true
1440
+ });
1441
+ };
1442
+ const _takeV8Coverage = async (context) => {
1443
+ const session = await context.__ensureCDPHandler();
1444
+ return session.send("Profiler.takePreciseCoverage");
1445
+ };
1446
+
1434
1447
  const types = {
1435
1448
  'application/andrew-inset': ['ez'],
1436
1449
  'application/appinstaller': ['appinstaller'],
@@ -2495,6 +2508,8 @@ var builtinCommands = {
2495
2508
  readFile,
2496
2509
  removeFile,
2497
2510
  writeFile,
2511
+ __vitest_startV8Coverage: _startV8Coverage,
2512
+ __vitest_takeV8Coverage: _takeV8Coverage,
2498
2513
  __vitest_markTrace: _markTrace,
2499
2514
  __vitest_groupTraceStart: _groupTraceStart,
2500
2515
  __vitest_groupTraceEnd: _groupTraceEnd,
@@ -3009,7 +3024,8 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3009
3024
  }
3010
3025
  if (type === "orchestrator") {
3011
3026
  const session = sessions.getSession(sessionId);
3012
- // it's possible the session was already resolved by the preview provider
3027
+ // it's possible the session was already resolved by the preview provider,
3028
+ // but we still mark the websocket connection when the page reconnects
3013
3029
  session?.connected();
3014
3030
  }
3015
3031
  const project = vitest.getProjectByName(projectName);
@@ -3018,7 +3034,7 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3018
3034
  }
3019
3035
  wss.handleUpgrade(request, socket, head, (ws) => {
3020
3036
  wss.emit("connection", ws, request);
3021
- const { rpc, offCancel } = setupClient(project, rpcId, ws);
3037
+ const { rpc, offCancel } = setupClient(project, rpcId, ws, { sessionId });
3022
3038
  const state = project.browser.state;
3023
3039
  const clients = type === "tester" ? state.testers : state.orchestrators;
3024
3040
  clients.set(rpcId, rpc);
@@ -3049,10 +3065,22 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3049
3065
  function canWrite(project) {
3050
3066
  return project.config.browser.api.allowWrite && project.vitest.config.browser.api.allowWrite && project.config.api.allowWrite && project.vitest.config.api.allowWrite;
3051
3067
  }
3052
- function setupClient(project, rpcId, ws) {
3068
+ function isCdpAllowed(project) {
3069
+ return project.config.api.allowExec && project.config.browser.api.allowExec && project.vitest.config.api.allowExec && project.vitest.config.browser.api.allowExec && project.config.api.allowWrite && project.config.browser.api.allowWrite && project.vitest.config.api.allowWrite && project.vitest.config.browser.api.allowWrite;
3070
+ }
3071
+ function assertCdpAllowed(project) {
3072
+ if (!isCdpAllowed(project)) {
3073
+ throw new Error(`Cannot use CDP because browser API write or exec operations are disabled. See https://vitest.dev/config/browser/api.`);
3074
+ }
3075
+ }
3076
+ function setupClient(project, rpcId, ws, options) {
3053
3077
  const mockResolver = new ServerMockResolver(globalServer.vite, { moduleDirectories: project.config?.deps?.moduleDirectories });
3054
3078
  const mocker = project.browser?.provider.mocker;
3055
3079
  const rpc = createBirpc({
3080
+ onOrchestratorReady() {
3081
+ const sessions = vitest._browserSessions;
3082
+ sessions.getSession(options.sessionId)?.ready();
3083
+ },
3056
3084
  async onUnhandledError(error, type) {
3057
3085
  if (error && typeof error === "object") {
3058
3086
  const _error = error;
@@ -3193,7 +3221,8 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3193
3221
  sessionId,
3194
3222
  triggerCommand: (name, ...args) => {
3195
3223
  return project.browser.triggerCommand(name, context, ...args);
3196
- }
3224
+ },
3225
+ __ensureCDPHandler: () => globalServer.ensureCDPHandler(sessionId, rpcId)
3197
3226
  }, provider.getCommandsContext(sessionId));
3198
3227
  return await project.browser.triggerCommand(command, context, ...payload);
3199
3228
  },
@@ -3255,10 +3284,12 @@ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3255
3284
  return mocker.delete(sessionId, id);
3256
3285
  },
3257
3286
  async sendCdpEvent(sessionId, event, payload) {
3287
+ assertCdpAllowed(project);
3258
3288
  const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
3259
3289
  return cdp.send(event, payload);
3260
3290
  },
3261
3291
  async trackCdpEvent(sessionId, type, event, listenerId) {
3292
+ assertCdpAllowed(project);
3262
3293
  const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
3263
3294
  cdp[type](event, listenerId);
3264
3295
  }
package/dist/types.d.ts CHANGED
@@ -11,6 +11,7 @@ export interface WebSocketBrowserHandlers {
11
11
  onTaskArtifactRecord: <Artifact extends TestArtifact>(testId: string, artifact: Artifact) => Promise<Artifact>;
12
12
  onTaskUpdate: (method: TestExecutionMethod, packs: TaskResultPack[], events: TaskEventPack[]) => void;
13
13
  onAfterSuiteRun: (meta: AfterSuiteRunMeta) => void;
14
+ onOrchestratorReady: () => void;
14
15
  cancelCurrentRun: (reason: CancelReason) => void;
15
16
  getCountOfFailedTests: () => number;
16
17
  readSnapshotFile: (id: string) => Promise<string | null>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "4.1.7",
4
+ "version": "4.1.9",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -57,7 +57,7 @@
57
57
  "providers"
58
58
  ],
59
59
  "peerDependencies": {
60
- "vitest": "4.1.7"
60
+ "vitest": "4.1.9"
61
61
  },
62
62
  "dependencies": {
63
63
  "@blazediff/core": "1.9.1",
@@ -66,8 +66,8 @@
66
66
  "sirv": "^3.0.2",
67
67
  "tinyrainbow": "^3.1.0",
68
68
  "ws": "^8.19.0",
69
- "@vitest/mocker": "4.1.7",
70
- "@vitest/utils": "4.1.7"
69
+ "@vitest/utils": "4.1.9",
70
+ "@vitest/mocker": "4.1.9"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@opentelemetry/api": "^1.9.0",
@@ -79,8 +79,8 @@
79
79
  "ivya": "^1.8.0",
80
80
  "mime": "^4.1.0",
81
81
  "pathe": "^2.0.3",
82
- "@vitest/runner": "4.1.7",
83
- "vitest": "4.1.7"
82
+ "vitest": "4.1.9",
83
+ "@vitest/runner": "4.1.9"
84
84
  },
85
85
  "scripts": {
86
86
  "typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",