@vitest/browser 3.1.2 → 3.2.0-beta.1

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.
@@ -1,5 +1,8 @@
1
1
  import { createManualModuleSource } from '@vitest/mocker/node';
2
+ import c from 'tinyrainbow';
3
+ import { createDebugger } from 'vitest/node';
2
4
 
5
+ const debug$1 = createDebugger("vitest:browser:playwright");
3
6
  const playwrightBrowsers = [
4
7
  "firefox",
5
8
  "webkit",
@@ -16,25 +19,41 @@ class PlaywrightBrowserProvider {
16
19
  pages = new Map();
17
20
  browserPromise = null;
18
21
  mocker;
22
+ closing = false;
19
23
  getSupportedBrowsers() {
20
24
  return playwrightBrowsers;
21
25
  }
22
26
  initialize(project, { browser, options }) {
27
+ this.closing = false;
23
28
  this.project = project;
24
29
  this.browserName = browser;
25
30
  this.options = options;
26
31
  this.mocker = this.createMocker();
27
32
  }
28
33
  async openBrowser() {
34
+ await this._throwIfClosing();
29
35
  if (this.browserPromise) {
36
+ debug$1?.("[%s] the browser is resolving, reusing the promise", this.browserName);
30
37
  return this.browserPromise;
31
38
  }
32
39
  if (this.browser) {
40
+ debug$1?.("[%s] the browser is resolved, reusing it", this.browserName);
33
41
  return this.browser;
34
42
  }
35
43
  this.browserPromise = (async () => {
36
44
  const options = this.project.config.browser;
37
45
  const playwright = await import('playwright');
46
+ if (this.options?.connect) {
47
+ if (this.options.launch) {
48
+ this.project.vitest.logger.warn(c.yellow(`Found both ${c.bold(c.italic(c.yellow("connect")))} and ${c.bold(c.italic(c.yellow("launch")))} options in browser instance configuration.
49
+ Ignoring ${c.bold(c.italic(c.yellow("launch")))} options and using ${c.bold(c.italic(c.yellow("connect")))} mode.
50
+ You probably want to remove one of the two options and keep only the one you want to use.`));
51
+ }
52
+ const browser = await playwright[this.browserName].connect(this.options.connect.wsEndpoint, this.options.connect.options);
53
+ this.browser = browser;
54
+ this.browserPromise = null;
55
+ return this.browser;
56
+ }
38
57
  const launchOptions = {
39
58
  ...this.options?.launch,
40
59
  headless: options.headless
@@ -55,8 +74,8 @@ class PlaywrightBrowserProvider {
55
74
  launchOptions.args.push("--start-maximized");
56
75
  }
57
76
  }
58
- const browser = await playwright[this.browserName].launch(launchOptions);
59
- this.browser = browser;
77
+ debug$1?.("[%s] initializing the browser with launch options: %O", this.browserName, launchOptions);
78
+ this.browser = await playwright[this.browserName].launch(launchOptions);
60
79
  this.browserPromise = null;
61
80
  return this.browser;
62
81
  })();
@@ -166,10 +185,13 @@ class PlaywrightBrowserProvider {
166
185
  };
167
186
  }
168
187
  async createContext(sessionId) {
188
+ await this._throwIfClosing();
169
189
  if (this.contexts.has(sessionId)) {
190
+ debug$1?.("[%s][%s] the context already exists, reusing it", sessionId, this.browserName);
170
191
  return this.contexts.get(sessionId);
171
192
  }
172
193
  const browser = await this.openBrowser();
194
+ await this._throwIfClosing(browser);
173
195
  const { actionTimeout,...contextOptions } = this.options?.context ?? {};
174
196
  const options = {
175
197
  ...contextOptions,
@@ -179,9 +201,11 @@ class PlaywrightBrowserProvider {
179
201
  options.viewport = null;
180
202
  }
181
203
  const context = await browser.newContext(options);
204
+ await this._throwIfClosing(context);
182
205
  if (actionTimeout) {
183
206
  context.setDefaultTimeout(actionTimeout);
184
207
  }
208
+ debug$1?.("[%s][%s] the context is ready", sessionId, this.browserName);
185
209
  this.contexts.set(sessionId, context);
186
210
  return context;
187
211
  }
@@ -219,13 +243,17 @@ class PlaywrightBrowserProvider {
219
243
  };
220
244
  }
221
245
  async openBrowserPage(sessionId) {
246
+ await this._throwIfClosing();
222
247
  if (this.pages.has(sessionId)) {
248
+ debug$1?.("[%s][%s] the page already exists, closing the old one", sessionId, this.browserName);
223
249
  const page = this.pages.get(sessionId);
224
250
  await page.close();
225
251
  this.pages.delete(sessionId);
226
252
  }
227
253
  const context = await this.createContext(sessionId);
228
254
  const page = await context.newPage();
255
+ debug$1?.("[%s][%s] the page is ready", sessionId, this.browserName);
256
+ await this._throwIfClosing(page);
229
257
  this.pages.set(sessionId, page);
230
258
  if (process.env.VITEST_PW_DEBUG) {
231
259
  page.on("requestfailed", (request) => {
@@ -235,9 +263,23 @@ class PlaywrightBrowserProvider {
235
263
  return page;
236
264
  }
237
265
  async openPage(sessionId, url, beforeNavigate) {
266
+ debug$1?.("[%s][%s] creating the browser page for %s", sessionId, this.browserName, url);
238
267
  const browserPage = await this.openBrowserPage(sessionId);
239
268
  await beforeNavigate?.();
269
+ debug$1?.("[%s][%s] browser page is created, opening %s", sessionId, this.browserName, url);
240
270
  await browserPage.goto(url, { timeout: 0 });
271
+ await this._throwIfClosing(browserPage);
272
+ }
273
+ async _throwIfClosing(disposable) {
274
+ if (this.closing) {
275
+ debug$1?.("[%s] provider was closed, cannot perform the action on %s", this.browserName, String(disposable));
276
+ await disposable?.close();
277
+ this.pages.clear();
278
+ this.contexts.clear();
279
+ this.browser = null;
280
+ this.browserPromise = null;
281
+ throw new Error(`[vitest] The provider was closed.`);
282
+ }
241
283
  }
242
284
  async getCDPSession(sessionid) {
243
285
  const page = this.getPage(sessionid);
@@ -259,13 +301,20 @@ class PlaywrightBrowserProvider {
259
301
  };
260
302
  }
261
303
  async close() {
304
+ debug$1?.("[%s] closing provider", this.browserName);
305
+ this.closing = true;
262
306
  const browser = this.browser;
263
307
  this.browser = null;
308
+ if (this.browserPromise) {
309
+ await this.browserPromise;
310
+ this.browserPromise = null;
311
+ }
264
312
  await Promise.all([...this.pages.values()].map((p) => p.close()));
265
313
  this.pages.clear();
266
314
  await Promise.all([...this.contexts.values()].map((c) => c.close()));
267
315
  this.contexts.clear();
268
316
  await browser?.close();
317
+ debug$1?.("[%s] provider is closed", this.browserName);
269
318
  }
270
319
  }
271
320
  function getHeaders(config) {
@@ -295,6 +344,7 @@ function isDirectCSSRequest(request) {
295
344
  return CSS_LANGS_RE.test(request) && directRequestRE.test(request);
296
345
  }
297
346
 
347
+ const debug = createDebugger("vitest:browser:wdio");
298
348
  const webdriverBrowsers = [
299
349
  "firefox",
300
350
  "chrome",
@@ -308,10 +358,12 @@ class WebdriverBrowserProvider {
308
358
  browserName;
309
359
  project;
310
360
  options;
361
+ closing = false;
311
362
  getSupportedBrowsers() {
312
363
  return webdriverBrowsers;
313
364
  }
314
365
  async initialize(ctx, { browser, options }) {
366
+ this.closing = false;
315
367
  this.project = ctx;
316
368
  this.browserName = browser;
317
369
  this.options = options;
@@ -337,7 +389,9 @@ class WebdriverBrowserProvider {
337
389
  return { browser: this.browser };
338
390
  }
339
391
  async openBrowser() {
392
+ await this._throwIfClosing("opening the browser");
340
393
  if (this.browser) {
394
+ debug?.("[%s] the browser is already opened, reusing it", this.browserName);
341
395
  return this.browser;
342
396
  }
343
397
  const options = this.project.config.browser;
@@ -347,11 +401,14 @@ class WebdriverBrowserProvider {
347
401
  }
348
402
  }
349
403
  const { remote } = await import('webdriverio');
350
- this.browser = await remote({
404
+ const remoteOptions = {
351
405
  ...this.options,
352
406
  logLevel: "error",
353
407
  capabilities: this.buildCapabilities()
354
- });
408
+ };
409
+ debug?.("[%s] opening the browser with options: %O", this.browserName, remoteOptions);
410
+ this.browser = await remote(remoteOptions);
411
+ await this._throwIfClosing();
355
412
  return this.browser;
356
413
  }
357
414
  buildCapabilities() {
@@ -386,11 +443,24 @@ class WebdriverBrowserProvider {
386
443
  }
387
444
  return capabilities;
388
445
  }
389
- async openPage(_sessionId, url) {
446
+ async openPage(sessionId, url) {
447
+ await this._throwIfClosing("creating the browser");
448
+ debug?.("[%s][%s] creating the browser page for %s", sessionId, this.browserName, url);
390
449
  const browserInstance = await this.openBrowser();
450
+ debug?.("[%s][%s] browser page is created, opening %s", sessionId, this.browserName, url);
391
451
  await browserInstance.url(url);
452
+ await this._throwIfClosing("opening the url");
453
+ }
454
+ async _throwIfClosing(action) {
455
+ if (this.closing) {
456
+ debug?.(`[%s] provider was closed, cannot perform the action${action ? ` ${action}` : ""}`, this.browserName);
457
+ await (this.browser?.sessionId ? this.browser?.deleteSession?.() : null);
458
+ throw new Error(`[vitest] The provider was closed.`);
459
+ }
392
460
  }
393
461
  async close() {
462
+ debug?.("[%s] closing provider", this.browserName);
463
+ this.closing = true;
394
464
  await Promise.all([this.browser?.sessionId ? this.browser?.deleteSession?.() : null]);
395
465
  process.exit();
396
466
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "3.1.2",
4
+ "version": "3.2.0-beta.1",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -66,7 +66,7 @@
66
66
  "peerDependencies": {
67
67
  "playwright": "*",
68
68
  "webdriverio": "^7.0.0 || ^8.0.0 || ^9.0.0",
69
- "vitest": "3.1.2"
69
+ "vitest": "3.2.0-beta.1"
70
70
  },
71
71
  "peerDependenciesMeta": {
72
72
  "playwright": {
@@ -86,27 +86,27 @@
86
86
  "sirv": "^3.0.1",
87
87
  "tinyrainbow": "^2.0.0",
88
88
  "ws": "^8.18.1",
89
- "@vitest/utils": "3.1.2",
90
- "@vitest/mocker": "3.1.2"
89
+ "@vitest/mocker": "3.2.0-beta.1",
90
+ "@vitest/utils": "3.2.0-beta.1"
91
91
  },
92
92
  "devDependencies": {
93
93
  "@types/ws": "^8.18.1",
94
94
  "@wdio/protocols": "^9.12.5",
95
- "@wdio/types": "^9.12.3",
95
+ "@wdio/types": "^9.12.6",
96
96
  "birpc": "2.3.0",
97
97
  "flatted": "^3.3.3",
98
98
  "ivya": "^1.6.0",
99
99
  "mime": "^4.0.7",
100
100
  "pathe": "^2.0.3",
101
101
  "periscopic": "^4.0.2",
102
- "playwright": "^1.51.1",
103
- "playwright-core": "^1.51.1",
102
+ "playwright": "^1.52.0",
103
+ "playwright-core": "^1.52.0",
104
104
  "safaridriver": "^1.0.0",
105
- "webdriverio": "^9.12.5",
106
- "@vitest/runner": "3.1.2",
107
- "@vitest/ui": "3.1.2",
108
- "@vitest/ws-client": "3.1.2",
109
- "vitest": "3.1.2"
105
+ "webdriverio": "^9.12.7",
106
+ "@vitest/runner": "3.2.0-beta.1",
107
+ "@vitest/ui": "3.2.0-beta.1",
108
+ "vitest": "3.2.0-beta.1",
109
+ "@vitest/ws-client": "3.2.0-beta.1"
110
110
  },
111
111
  "scripts": {
112
112
  "build": "rimraf dist && pnpm build:node && pnpm build:client",
@@ -5,7 +5,8 @@ import type {
5
5
  FrameLocator,
6
6
  LaunchOptions,
7
7
  Page,
8
- CDPSession
8
+ CDPSession,
9
+ ConnectOptions
9
10
  } from 'playwright'
10
11
  import { Protocol } from 'playwright-core/types/protocol'
11
12
  import '../matchers.js'
@@ -14,6 +15,10 @@ import type {} from "vitest/node"
14
15
  declare module 'vitest/node' {
15
16
  export interface BrowserProviderOptions {
16
17
  launch?: LaunchOptions
18
+ connect?: {
19
+ wsEndpoint: string
20
+ options?: ConnectOptions
21
+ }
17
22
  context?: Omit<
18
23
  BrowserContextOptions,
19
24
  'ignoreHTTPSErrors' | 'serviceWorkers'