Skip to content

Commit 029c078

Browse files
feat(browser): implement connect option for playwright browser provider (#7915)
Co-authored-by: Vladimir <[email protected]>
1 parent 465bdb3 commit 029c078

File tree

8 files changed

+127
-3
lines changed

8 files changed

+127
-3
lines changed

docs/guide/browser/playwright.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ Alternatively, you can also add it to `compilerOptions.types` field in your `tsc
1616
}
1717
```
1818

19-
Vitest opens a single page to run all tests in the same file. You can configure the `launch` and `context` properties in `instances`:
19+
Vitest opens a single page to run all tests in the same file. You can configure the `launch`, `connect` and `context` properties in `instances`:
2020

21-
```ts{9-10} [vitest.config.ts]
21+
```ts{9-11} [vitest.config.ts]
2222
import { defineConfig } from 'vitest/config'
2323
2424
export default defineConfig({
@@ -28,6 +28,7 @@ export default defineConfig({
2828
{
2929
browser: 'firefox',
3030
launch: {},
31+
connect: {},
3132
context: {},
3233
},
3334
],
@@ -65,6 +66,14 @@ Vitest will ignore `launch.headless` option. Instead, use [`test.browser.headles
6566
Note that Vitest will push debugging flags to `launch.args` if [`--inspect`](/guide/cli#inspect) is enabled.
6667
:::
6768

69+
## connect <Version>3.2.0</Version> {#connect}
70+
71+
These options are directly passed down to `playwright[browser].connect` command. You can read more about the command and available arguments in the [Playwright documentation](https://playwright.dev/docs/api/class-browsertype#browser-type-connect).
72+
73+
::: warning
74+
Since this command connects to an existing Playwright server, any `launch` options will be ignored.
75+
:::
76+
6877
## context
6978

7079
Vitest creates a new context for every test file by calling [`browser.newContext()`](https://playwright.dev/docs/api/class-browsercontext). You can configure this behaviour by specifying [custom arguments](https://playwright.dev/docs/api/class-apirequest#api-request-new-context).

packages/browser/providers/playwright.d.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import type {
55
FrameLocator,
66
LaunchOptions,
77
Page,
8-
CDPSession
8+
CDPSession,
9+
ConnectOptions
910
} from 'playwright'
1011
import { Protocol } from 'playwright-core/types/protocol'
1112
import '../matchers.js'
@@ -14,6 +15,10 @@ import type {} from "vitest/node"
1415
declare module 'vitest/node' {
1516
export interface BrowserProviderOptions {
1617
launch?: LaunchOptions
18+
connect?: {
19+
wsEndpoint: string
20+
options?: ConnectOptions
21+
}
1722
context?: Omit<
1823
BrowserContextOptions,
1924
'ignoreHTTPSErrors' | 'serviceWorkers'

packages/browser/src/node/providers/playwright.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
Browser,
44
BrowserContext,
55
BrowserContextOptions,
6+
ConnectOptions,
67
Frame,
78
FrameLocator,
89
LaunchOptions,
@@ -18,6 +19,7 @@ import type {
1819
TestProject,
1920
} from 'vitest/node'
2021
import { createManualModuleSource } from '@vitest/mocker/node'
22+
import c from 'tinyrainbow'
2123
import { createDebugger } from 'vitest/node'
2224

2325
const debug = createDebugger('vitest:browser:playwright')
@@ -41,6 +43,10 @@ export class PlaywrightBrowserProvider implements BrowserProvider {
4143

4244
private options?: {
4345
launch?: LaunchOptions
46+
connect?: {
47+
wsEndpoint: string
48+
options?: ConnectOptions
49+
}
4450
context?: BrowserContextOptions & { actionTimeout?: number }
4551
}
4652

@@ -86,6 +92,20 @@ export class PlaywrightBrowserProvider implements BrowserProvider {
8692

8793
const playwright = await import('playwright')
8894

95+
if (this.options?.connect) {
96+
if (this.options.launch) {
97+
this.project.vitest.logger.warn(
98+
c.yellow(`Found both ${c.bold(c.italic(c.yellow('connect')))} and ${c.bold(c.italic(c.yellow('launch')))} options in browser instance configuration.
99+
Ignoring ${c.bold(c.italic(c.yellow('launch')))} options and using ${c.bold(c.italic(c.yellow('connect')))} mode.
100+
You probably want to remove one of the two options and keep only the one you want to use.`),
101+
)
102+
}
103+
const browser = await playwright[this.browserName].connect(this.options.connect.wsEndpoint, this.options.connect.options)
104+
this.browser = browser
105+
this.browserPromise = null
106+
return this.browser
107+
}
108+
89109
const launchOptions = {
90110
...this.options?.launch,
91111
headless: options.headless,

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { test, expect } from 'vitest';
2+
3+
test('[playwright] Run basic test in browser via connect mode', () => {
4+
expect(1).toBe(1)
5+
})
6+
7+
test('[playwright] Run browser-only test in browser via connect mode', () => {
8+
const element = document.createElement("div")
9+
expect(element instanceof HTMLDivElement).toBe(true)
10+
expect(element instanceof HTMLElement).toBe(true)
11+
expect(element instanceof HTMLInputElement).not.toBe(true)
12+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { defineConfig } from 'vitest/config';
2+
import { fileURLToPath } from 'node:url'
3+
4+
const provider = process.env.PROVIDER || 'playwright'
5+
6+
export default defineConfig({
7+
clearScreen: false,
8+
cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)),
9+
test: {
10+
browser: {
11+
provider: provider,
12+
enabled: true,
13+
headless: true,
14+
screenshotFailures: false,
15+
},
16+
},
17+
})

test/browser/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"@vitest/bundled-lib": "link:./bundled-lib",
3232
"@vitest/cjs-lib": "link:./cjs-lib",
3333
"@vitest/injected-lib": "link:./injected-lib",
34+
"playwright": "^1.52.0",
3435
"react": "^19.1.0",
3536
"react-dom": "^19.1.0",
3637
"url": "^0.11.4",
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { chromium } from 'playwright'
2+
import { expect, test } from 'vitest'
3+
import { provider } from '../settings'
4+
import { runBrowserTests } from './utils'
5+
6+
test.runIf(provider === 'playwright')('[playwright] runs in connect mode', async () => {
7+
const browserServer = await chromium.launchServer()
8+
const wsEndpoint = browserServer.wsEndpoint()
9+
10+
const { stdout, exitCode, stderr } = await runBrowserTests({
11+
root: './fixtures/playwright-connect',
12+
browser: {
13+
instances: [
14+
{
15+
browser: 'chromium',
16+
name: 'chromium',
17+
connect: {
18+
wsEndpoint,
19+
},
20+
},
21+
],
22+
},
23+
})
24+
25+
await browserServer.close()
26+
27+
expect(stdout).toContain('Tests 2 passed')
28+
expect(exitCode).toBe(0)
29+
expect(stderr).toBe('')
30+
})
31+
32+
test.runIf(provider === 'playwright')('[playwright] warns if both connect and launch mode are configured', async () => {
33+
const browserServer = await chromium.launchServer()
34+
const wsEndpoint = browserServer.wsEndpoint()
35+
36+
const { stdout, exitCode, stderr } = await runBrowserTests({
37+
root: './fixtures/playwright-connect',
38+
browser: {
39+
instances: [
40+
{
41+
browser: 'chromium',
42+
name: 'chromium',
43+
connect: {
44+
wsEndpoint,
45+
},
46+
launch: {},
47+
},
48+
],
49+
},
50+
})
51+
52+
await browserServer.close()
53+
54+
expect(stdout).toContain('Tests 2 passed')
55+
expect(exitCode).toBe(0)
56+
expect(stderr).toContain('Found both connect and launch options in browser instance configuration.')
57+
})

0 commit comments

Comments
 (0)