-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathplaywright.config.js
More file actions
200 lines (186 loc) · 7.35 KB
/
Copy pathplaywright.config.js
File metadata and controls
200 lines (186 loc) · 7.35 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
import fs from 'node:fs';
import { defineConfig, devices } from '@playwright/test';
const PORT = process.env.PLAYWRIGHT_PORT ? Number(process.env.PLAYWRIGHT_PORT) : 3000;
const LOCAL_STACK = process.env.LOCAL_STACK === '1' || process.env.LOCAL_STACK === 'true';
const BASE_URL = process.env.PLAYWRIGHT_BASE_URL ?? `http://localhost:${PORT}`;
function stackProxyDefaults() {
const inDocker = process.env.STORMBOX_IN_DOCKER === '1' || fs.existsSync('/.dockerenv');
const host = inDocker ? '172.17.0.1' : '127.0.0.1';
return {
KEYCLOAK_PROXY: process.env.KEYCLOAK_PROXY ?? `http://${host}:8999`,
STALWART_JMAP_PROXY: process.env.STALWART_JMAP_PROXY ?? `http://${host}:8081`,
};
}
const stackProxies = LOCAL_STACK ? stackProxyDefaults() : {};
const localStackViteEnv = LOCAL_STACK
? {
VITE_JMAP_SERVER_URL: process.env.VITE_JMAP_SERVER_URL ?? `${BASE_URL}/stalwart-jmap`,
VITE_OIDC_ISSUER: process.env.VITE_OIDC_ISSUER ?? `${BASE_URL}/realms/tbpro`,
VITE_OIDC_CLIENT_ID: process.env.VITE_OIDC_CLIENT_ID ?? 'thunderbird-stormbox-test',
VITE_SENDER_AVATAR_PROXY_URL: process.env.VITE_SENDER_AVATAR_PROXY_URL ?? `${BASE_URL}/sender-avatar`,
// The proxy rewrites Keycloak's advertised origin to this value, so a
// lane on a non-default port needs it to follow the lane rather than
// fall back to the dev default.
VITE_LOCAL_PUBLIC_ORIGIN: process.env.VITE_LOCAL_PUBLIC_ORIGIN ?? BASE_URL,
VITE_LOCAL_STACK: '1',
}
: {};
function buildWebServer() {
if (LOCAL_STACK) {
return {
command: `npm run dev -- --host 0.0.0.0 --port ${PORT}`,
url: BASE_URL,
// Default to reusing an already-running vite under LOCAL_STACK so
// iterative local runs skip the ~5s vite boot. Opt out with
// PLAYWRIGHT_NO_REUSE=1 if you suspect a stale dev server.
reuseExistingServer: process.env.PLAYWRIGHT_NO_REUSE !== '1',
timeout: 60_000,
ignoreHTTPSErrors: true,
env: {
...process.env,
...localStackViteEnv,
...stackProxies,
},
};
}
if (process.env.PLAYWRIGHT_NO_REUSE) {
return {
command: `npm run dev -- --host 0.0.0.0 --port ${PORT}`,
url: BASE_URL,
reuseExistingServer: false,
timeout: 60_000,
ignoreHTTPSErrors: true,
};
}
return {
command: 'true',
url: BASE_URL,
reuseExistingServer: true,
timeout: 1_000,
ignoreHTTPSErrors: true,
};
}
// Per-browser OIDC storage state captured by tests/e2e/auth.setup.js.
// We split chromium / firefox because oidc-spa's localStorage layout
// can encode browser-specific quirks even though Keycloak's SSO
// cookies are themselves engine-agnostic.
const STORAGE_STATE_CHROMIUM = '.playwright-auth/chromium.json';
const STORAGE_STATE_FIREFOX = '.playwright-auth/firefox.json';
// Firefox is the default LOCAL_STACK lane because the suite has
// historically uncovered more Firefox-only regressions than
// Chromium-only ones. Set INCLUDE_CHROMIUM=1 to also run the chromium
// lane (e.g. for pre-merge / nightly), or pass --project=chromium
// explicitly to run chromium only.
const INCLUDE_CHROMIUM =
process.env.INCLUDE_CHROMIUM === '1' || process.env.INCLUDE_CHROMIUM === 'true';
function localStackProjects() {
if (!LOCAL_STACK) {
return [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
];
}
const chromiumSetupUse = {
...devices['Desktop Chrome'],
launchOptions: { args: ['--ignore-certificate-errors'] },
};
// Firefox throttles JS in background/unfocused tabs by default
// (setTimeout clamped to 1 s, microtask budget reduced). Playwright
// doesn't always keep its windows in OS foreground when other
// apps are active, so the test page can land in throttled mode
// mid-test — observed as 30 s+ delays between StateChange WS
// delivery and the corresponding UI re-render. These prefs disable
// the throttling for the test browser only; production Firefox
// continues to throttle as designed.
const firefoxNoThrottle = {
'dom.min_background_timeout_value': 4,
'dom.timeout.background_throttling_max_budget': -1,
'dom.timeout.background_budget_regeneration_rate': -1,
'dom.timeout.foreground_throttling_max_budget': -1,
};
const firefoxSetupUse = {
...devices['Desktop Firefox'],
launchOptions: { firefoxUserPrefs: firefoxNoThrottle },
};
// Setup projects capture a shared OIDC session per browser so the
// dependent test projects boot already authenticated. Each setup
// project writes its own storageState file (see auth.setup.js
// which keys off project.name); chromium and firefox sessions
// don't stomp on each other.
//
// Both browser projects (when INCLUDE_CHROMIUM=1) run serially
// under workers: 1 because the single shared Stalwart account
// can't tolerate parallel mutation. The per-test speedup comes
// from storageState (Keycloak login skipped, ~3-5s per test),
// not from worker parallelism.
const firefoxProjects = [
{
name: 'setup-firefox',
testMatch: /auth\.setup\.js/,
use: firefoxSetupUse,
},
{
name: 'firefox',
testMatch: /\.spec\.js$/,
dependencies: ['setup-firefox'],
use: { ...firefoxSetupUse, storageState: STORAGE_STATE_FIREFOX },
},
];
if (!INCLUDE_CHROMIUM) {
return firefoxProjects;
}
return [
{
name: 'setup-chromium',
testMatch: /auth\.setup\.js/,
use: chromiumSetupUse,
},
{
name: 'chromium',
testMatch: /\.spec\.js$/,
dependencies: ['setup-chromium'],
use: { ...chromiumSetupUse, storageState: STORAGE_STATE_CHROMIUM },
},
...firefoxProjects,
];
}
export default defineConfig({
testDir: './tests/e2e',
testMatch: /.*(\.spec\.js|\.setup\.js)$/,
timeout: 60_000,
expect: { timeout: 10_000 },
fullyParallel: false,
// One worker so specs do not parallel-mutate the single shared
// Stalwart account. Two browser projects under INCLUDE_CHROMIUM=1
// would race on the same mailbox; lifting workers to 2 needs a
// second Thundermail principal first. The per-test speedup comes
// from storageState (Keycloak login skipped), not from worker
// parallelism.
//
// This bounds one process only. A second `npm run test:e2e:local:full`
// races the first just as surely; the lane lock in global setup is what
// stops that.
workers: LOCAL_STACK ? 1 : undefined,
forbidOnly: !!process.env.CI,
// One retry covers the occasional Keycloak SSO blip; real
// regressions fail twice in a row.
retries: LOCAL_STACK ? 1 : 0,
reporter: process.env.CI ? 'github' : 'list',
globalSetup: LOCAL_STACK ? './tests/e2e/global-setup.js' : undefined,
// Releases the single-lane lock the setup takes. A run killed outright
// never gets here, so the lock also clears itself once its holder is gone.
globalTeardown: LOCAL_STACK ? './tests/e2e/global-teardown.js' : undefined,
use: {
baseURL: BASE_URL,
ignoreHTTPSErrors: true,
// A click or fill whose target never becomes actionable fails here
// instead of consuming the whole test budget; waits that are genuinely
// long belong in an explicit expect or waitFor with its own timeout.
actionTimeout: 15_000,
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: localStackProjects(),
webServer: buildWebServer(),
});