-
Notifications
You must be signed in to change notification settings - Fork 190
Expand file tree
/
Copy pathplaywright.config.ts
More file actions
214 lines (201 loc) · 7.19 KB
/
Copy pathplaywright.config.ts
File metadata and controls
214 lines (201 loc) · 7.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import path from 'path'
import { parseArgs } from 'node:util'
import { defineConfig, devices } from '@playwright/test'
import type { ReporterDescription } from '@playwright/test'
import { getTestReportDirectory } from '../envUtils'
import type { BrowserConfiguration } from '../browsers.conf'
// Single config covering all e2e browser configurations:
//
// - chromium / firefox / webkit / android: current Playwright (1.60)-bundled browsers.
// - chromium-pinned (Chrome 120) / firefox-pinned (FF 119) / webkit-pinned (WK 17.4):
// replicate the old BrowserStack matrix locally via a pinned Playwright 1.40.1
// `run-server` and a translation proxy (test/e2e/scripts/pinnedProxy.ts) that bridges
// the 1.60 client and the 1.40 server.
//
// Project selection is read from `--project=<name>` flags on argv (Playwright's filter):
// - When no `--project` is passed, only chromium runs (fast local feedback loop).
// - When at least one `--project` is passed, all projects are registered and Playwright
// filters to the selected ones. The pinned web servers (`run-server` + proxy) only boot
// when a pinned project is selected, so non-pinned runs don't pay the boot cost.
//
// Initial install of the pinned browser binaries:
// yarn test:e2e:setup
const isCi = !!process.env.CI
const isLocal = !isCi
// Playwright re-imports this config in each worker process, where `--project=<name>` is no
// longer on argv. To keep the project list consistent across main and workers, decide once
// in the main process (based on argv) and propagate via an env var that workers inherit.
if (getSelectedProjects().length > 0) {
process.env.E2E_INCLUDE_ALL_PROJECTS = '1'
}
const PINNED_WS_ENDPOINT = 'ws://127.0.0.1:5400/'
const proxyDir = path.join(__dirname, 'scripts')
const reporters: ReporterDescription[] = [['line'], ['./noticeReporter.ts']]
const testReportDirectory = getTestReportDirectory()
if (testReportDirectory) {
// Multiple matrix cells (one per --project) write into the same folder; include the
// project name in the filename so they don't collide. PW_BROWSER is set by CI; locally
// it falls back to a single shared filename.
const junitFilename = process.env.PW_BROWSER ? `results-${process.env.PW_BROWSER}.xml` : 'results.xml'
reporters.push(['junit', { outputFile: path.join(process.cwd(), testReportDirectory, junitFilename) }])
} else {
reporters.push(['html'])
}
const baseWebServers = [
...(isLocal
? [
{
name: 'dev server',
stdout: 'pipe' as const,
cwd: path.join(__dirname, '../..'),
command: 'yarn dev-server start --no-daemon',
wait: {
stdout: /Dev server listening on port (?<dev_server_port>\d+)/,
},
},
]
: []),
{
name: 'nextjs app router',
stdout: 'pipe' as const,
cwd: path.join(__dirname, '../apps/nextjs'),
command: 'yarn start',
wait: {
stdout: /- Local:\s+http:\/\/localhost:(?<nextjs_app_router_port>\d+)/,
},
},
{
name: 'vue router app',
stdout: 'pipe' as const,
cwd: path.join(__dirname, '../apps/vue-router-app'),
command: isLocal ? 'yarn dev' : 'yarn preview',
// NO_COLOR=1 prevents Vite from wrapping "Local" in ANSI bold codes when
// FORCE_COLOR=1 is set in CI, which would break the wait.stdout regex.
env: { NO_COLOR: '1' },
wait: {
stdout: /Local:\s+http:\/\/localhost:(?<vue_router_app_port>\d+)/,
},
},
{
name: 'vue router v4 app',
stdout: 'pipe' as const,
cwd: path.join(__dirname, '../apps/vue-router-v4-app'),
command: isLocal ? 'yarn dev' : 'yarn preview',
env: { NO_COLOR: '1' },
wait: {
stdout: /Local:\s+http:\/\/localhost:(?<vue_router_v4_app_port>\d+)/,
},
},
{
name: 'nuxt app',
stdout: 'pipe' as const,
cwd: path.join(__dirname, '../apps/nuxt-app'),
command: isLocal ? 'yarn dev' : 'yarn start',
env: { NO_COLOR: '1' },
wait: {
// yarn dev logs: "➜ Local: http://localhost:PORT"
// yarn start logs: "Listening on http://[::]:PORT"
stdout: /(?:Local:\s+http:\/\/localhost|Listening on http:\/\/(?:\[[^\]]+\]|[^:]+)):(?<nuxt_app_port>\d+)/,
},
},
{
name: 'nuxt vue router v4 app',
stdout: 'pipe' as const,
cwd: path.join(__dirname, '../apps/nuxt-vue-router-v4-app'),
command: isLocal ? 'yarn dev' : 'yarn start',
env: { NO_COLOR: '1', PORT: '3001', NITRO_PORT: '3001' },
wait: {
stdout:
/(?:Local:\s+http:\/\/localhost|Listening on http:\/\/(?:\[[^\]]+\]|[^:]+)):(?<nuxt_vue_router_v4_app_port>\d+)/,
},
},
]
const pinnedWebServers = [
{
name: 'pinned playwright run-server',
stdout: 'pipe' as const,
cwd: proxyDir,
command: 'yarn dlx -p playwright@1.40.1 playwright run-server --port 5401',
wait: { stdout: /Listening on/ },
},
{
name: 'pinned proxy',
stdout: 'pipe' as const,
cwd: proxyDir,
command: 'node pinnedProxy.ts --listen 5400 --upstream 127.0.0.1:5401',
wait: { stdout: /pinnedProxy] listening/ },
},
]
// eslint-disable-next-line import-x/no-default-export
export default defineConfig({
testDir: './scenario',
testMatch: ['**/*.scenario.ts'],
tsconfig: './tsconfig.json',
fullyParallel: true,
forbidOnly: isCi,
maxFailures: 0,
retries: isCi ? 2 : 0,
workers: 5,
reporter: reporters,
use: {
trace: isCi ? 'off' : 'retain-on-failure',
},
webServer: needsPinnedServers() ? [...baseWebServers, ...pinnedWebServers] : baseWebServers,
projects: getProjects(),
})
function getProjects() {
if (process.env.E2E_INCLUDE_ALL_PROJECTS !== '1') {
return [project('chromium', 'Desktop Chrome')]
}
return [
project('chromium', 'Desktop Chrome'),
project('firefox', 'Desktop Firefox'),
project('webkit', 'Desktop Safari'),
project('android', 'Pixel 7'),
pinnedProject('chromium-pinned', 'Chromium 120', 'Desktop Chrome', '120'),
pinnedProject('firefox-pinned', 'Firefox 119', 'Desktop Firefox', '119'),
pinnedProject('webkit-pinned', 'WebKit 17.4', 'Desktop Safari', '17.4'),
]
}
function project(name: string, device: string) {
const isChromium = name === 'chromium'
return {
name,
metadata: { sessionName: device, name } satisfies BrowserConfiguration,
use: {
...devices[device],
// Required for experimental APIs (e.g. Network Efficiency Guardrails).
// Only passed to current Chromium — pinned browsers use pinnedProject() and may not support it.
...(isChromium ? { launchOptions: { args: ['--enable-experimental-web-platform-features'] } } : {}),
},
}
}
function pinnedProject(name: string, sessionName: string, device: string, version: string) {
return {
name,
metadata: { sessionName, name, version } satisfies BrowserConfiguration,
use: {
...devices[device],
connectOptions: { wsEndpoint: PINNED_WS_ENDPOINT },
},
}
}
function needsPinnedServers(): boolean {
return getSelectedProjects().some((p) => p.endsWith('-pinned'))
}
function getSelectedProjects(): string[] {
const {
values: { project },
} = parseArgs({
args: process.argv.slice(2),
options: {
project: {
type: 'string',
short: 'p',
multiple: true,
},
},
strict: false,
})
return (project as string[] | undefined) ?? []
}