Skip to content

Commit 136d6dd

Browse files
hi-ogawaclaude
andauthored
test: allow running browser mode tests with docker playwright + add docs examples (#9600)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 209b1b0 commit 136d6dd

8 files changed

Lines changed: 100 additions & 51 deletions

File tree

docs/config/browser/playwright.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,59 @@ Note that Vitest will push debugging flags to `launch.args` if [`--inspect`](/gu
7171

7272
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).
7373

74+
Use `connectOptions.wsEndpoint` to connect to an existing Playwright server instead of launching browsers locally. This is useful for running browsers in Docker, in CI, or on a remote machine.
75+
7476
::: warning
7577
Since this command connects to an existing Playwright server, any `launch` options will be ignored.
7678
:::
7779

80+
::: details Example: Running a Playwright Server in Docker
81+
To run browsers in a Docker container (see [Playwright Docker guide](https://playwright.dev/docs/docker#remote-connection)):
82+
83+
Start a Playwright server using Docker Compose:
84+
85+
```yaml [docker-compose.yml]
86+
services:
87+
playwright:
88+
image: mcr.microsoft.com/playwright:v1.58.1-noble
89+
command: /bin/sh -c "npx -y playwright@1.58.1 run-server --port 6677 --host 0.0.0.0"
90+
init: true
91+
ipc: host
92+
user: pwuser
93+
ports:
94+
- '6677:6677'
95+
```
96+
97+
```sh
98+
docker compose up -d
99+
```
100+
101+
Then configure Vitest to connect to it. The [`exposeNetwork`](https://playwright.dev/docs/api/class-browsertype#browser-type-connect-option-expose-network) option lets the containerized browser reach Vitest's dev server on the host:
102+
103+
```ts [vitest.config.ts]
104+
import { playwright } from '@vitest/browser-playwright'
105+
import { defineConfig } from 'vitest/config'
106+
107+
export default defineConfig({
108+
test: {
109+
browser: {
110+
provider: playwright({
111+
connectOptions: {
112+
wsEndpoint: 'ws://127.0.0.1:6677/',
113+
exposeNetwork: '<loopback>',
114+
},
115+
}),
116+
instances: [
117+
{ browser: 'chromium' },
118+
{ browser: 'firefox' },
119+
{ browser: 'webkit' },
120+
],
121+
},
122+
},
123+
})
124+
```
125+
:::
126+
78127
## contextOptions
79128

80129
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-browser#browser-new-context).

test/browser/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Browser Tests
2+
3+
## Using docker playwright
4+
5+
Some test suites don't support running it remotely (`fixtures/inspect` and `fixtures/insecure-context`).
6+
7+
```sh
8+
# Start playwright browser server
9+
pnpm docker up -d
10+
11+
# Run tests with BROWSER_WS_ENDPOINT
12+
BROWSER_WS_ENDPOINT=ws://127.0.0.1:6677/ pnpm test:playwright
13+
```

test/browser/docker-compose.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
services:
2+
playwright:
3+
image: mcr.microsoft.com/playwright:v1.58.1-noble
4+
command: /bin/sh -c "npx -y playwright@1.58.1 run-server --port 6677 --host 0.0.0.0"
5+
init: true
6+
ipc: host
7+
user: pwuser
8+
ports:
9+
- '6677:6677'

test/browser/fixtures/trace-view/vitest.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { fileURLToPath } from 'node:url'
22
import { defineConfig } from 'vitest/config'
3-
import { playwright } from '@vitest/browser-playwright'
3+
import { providers } from '../../settings'
44

55
export default defineConfig({
66
cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)),
77
test: {
88
browser: {
99
enabled: true,
10-
provider: playwright(),
10+
provider: providers.playwright(),
1111
instances: [
1212
{ browser: 'chromium' },
1313
{ browser: 'firefox' },

test/browser/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"test:browser:preview": "PROVIDER=preview vitest",
2727
"test:browser:playwright": "PROVIDER=playwright vitest",
2828
"test:browser:webdriverio": "PROVIDER=webdriverio vitest",
29-
"test:browser:playwright:html": "PROVIDER=playwright vitest --reporter=html"
29+
"test:browser:playwright:html": "PROVIDER=playwright vitest --reporter=html",
30+
"docker": "docker compose"
3031
},
3132
"devDependencies": {
3233
"@types/react": "^19.2.10",

test/browser/settings.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@ import { preview } from '@vitest/browser-preview'
44
import { webdriverio } from '@vitest/browser-webdriverio'
55

66
const providerName = (process.env.PROVIDER || 'playwright') as 'playwright' | 'webdriverio' | 'preview'
7+
78
export const providers = {
8-
playwright,
9+
playwright: (options?: Parameters<typeof playwright>[0]) => playwright(process.env.BROWSER_WS_ENDPOINT
10+
? {
11+
...options,
12+
connectOptions: {
13+
wsEndpoint: process.env.BROWSER_WS_ENDPOINT,
14+
exposeNetwork: '<loopback>',
15+
},
16+
}
17+
: options),
918
preview,
1019
webdriverio,
1120
}
1221

1322
export const provider = providers[providerName]()
14-
export const browser = process.env.BROWSER || (provider.name !== 'playwright' ? 'chromium' : 'chrome')
15-
16-
const devInstances: BrowserInstanceOption[] = [
17-
{ browser },
18-
]
1923

2024
const playwrightInstances: BrowserInstanceOption[] = [
2125
{ browser: 'chromium' },
@@ -30,8 +34,13 @@ const webdriverioInstances: BrowserInstanceOption[] = [
3034
{ browser: 'firefox' },
3135
]
3236

33-
export const instances = process.env.BROWSER
34-
? devInstances
37+
export const instances: BrowserInstanceOption[] = process.env.BROWSER
38+
? [
39+
{
40+
browser: process.env.BROWSER as any,
41+
headless: process.env.BROWSER === 'safari' ? false : undefined,
42+
},
43+
]
3544
: provider.name === 'playwright'
3645
? playwrightInstances
3746
: webdriverioInstances

test/browser/specs/utils.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import type { UserConfig as ViteUserConfig } from 'vite'
22
import type { TestUserConfig } from 'vitest/node'
33
import type { RunVitestConfig, TestFsStructure, VitestRunnerCLIOptions } from '../../test-utils'
44
import { runInlineTests, runVitest } from '../../test-utils'
5-
import { browser, instances, provider } from '../settings'
5+
import { instances, provider } from '../settings'
66

7-
export { browser, instances, provider } from '../settings'
7+
export { instances, provider } from '../settings'
88

99
export async function runInlineBrowserTests(
1010
structure: TestFsStructure,
@@ -21,7 +21,7 @@ export async function runInlineBrowserTests(
2121
enabled: true,
2222
provider,
2323
instances,
24-
headless: browser !== 'safari',
24+
headless: true,
2525
...config?.browser,
2626
} as TestUserConfig['browser'],
2727
},
@@ -39,10 +39,7 @@ export async function runBrowserTests(
3939
watch: false,
4040
reporters: 'none',
4141
...config,
42-
browser: {
43-
headless: browser !== 'safari',
44-
...config?.browser,
45-
} as TestUserConfig['browser'],
42+
browser: { headless: true, ...config?.browser },
4643
$viteConfig: viteOverrides,
4744
}, include, runnerOptions)
4845
}

test/browser/vitest.config.mts

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
1-
import type { BrowserCommand, BrowserInstanceOption } from 'vitest/node'
1+
import type { BrowserCommand } from 'vitest/node'
22
import { dirname, resolve } from 'node:path'
33
import { fileURLToPath } from 'node:url'
44
import * as util from 'node:util'
5-
import { playwright } from '@vitest/browser-playwright'
6-
import { preview } from '@vitest/browser-preview'
7-
import { webdriverio } from '@vitest/browser-webdriverio'
85
import { defineConfig } from 'vitest/config'
6+
import { instances, provider } from './settings'
97

108
const dir = dirname(fileURLToPath(import.meta.url))
119

12-
const providerName = process.env.PROVIDER || 'playwright'
13-
const browser = process.env.BROWSER as 'firefox' || (providerName === 'playwright' ? 'chromium' : 'chrome')
14-
const provider = {
15-
playwright,
16-
preview,
17-
webdriverio,
18-
}[providerName]
19-
2010
const myCustomCommand: BrowserCommand<[arg1: string, arg2: string]> = ({ testPath }, arg1, arg2) => {
2111
return { testPath, arg1, arg2 }
2212
}
@@ -25,21 +15,6 @@ const stripVTControlCharacters: BrowserCommand<[text: string]> = (_, text) => {
2515
return util.stripVTControlCharacters(text)
2616
}
2717

28-
const devInstances: BrowserInstanceOption[] = [
29-
{ browser },
30-
]
31-
32-
const playwrightInstances: BrowserInstanceOption[] = [
33-
{ browser: 'chromium' },
34-
{ browser: 'firefox' },
35-
{ browser: 'webkit' },
36-
]
37-
38-
const webdriverioInstances: BrowserInstanceOption[] = [
39-
{ browser: 'chrome' },
40-
{ browser: 'firefox' },
41-
]
42-
4318
export default defineConfig({
4419
server: {
4520
headers: {
@@ -66,12 +41,8 @@ export default defineConfig({
6641
browser: {
6742
enabled: true,
6843
headless: false,
69-
instances: process.env.BROWSER
70-
? devInstances
71-
: providerName === 'playwright'
72-
? playwrightInstances
73-
: webdriverioInstances,
74-
provider: provider(),
44+
instances,
45+
provider,
7546
// isolate: false,
7647
testerHtmlPath: './custom-tester.html',
7748
orchestratorScripts: [

0 commit comments

Comments
 (0)