@assertrx/dino 1.0.0
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.
- package/README.md +293 -0
- package/extensions/dino.ts +70 -0
- package/extensions/mabl-mcp.ts +306 -0
- package/extensions/playwright-mcp.ts +142 -0
- package/package.json +47 -0
- package/prompts/create-pr.md +110 -0
- package/prompts/generate-bdd-from-url.md +65 -0
- package/prompts/generate-pom-from-url-cli.md +48 -0
- package/prompts/generate-pom-from-url.md +45 -0
- package/prompts/generate-spec-from-feature.md +64 -0
- package/prompts/heal-playwright-failures.md +106 -0
- package/prompts/setup-playwright.md +81 -0
- package/scripts/start-dino.cmd +8 -0
- package/scripts/start-dino.mjs +226 -0
- package/scripts/start-dino.ps1 +9 -0
- package/scripts/start-dino.sh +10 -0
- package/skills/acli/SKILL.md +292 -0
- package/skills/bdd-gherkin/SKILL.md +552 -0
- package/skills/build-review-interface/SKILL.md +96 -0
- package/skills/error-analysis/SKILL.md +164 -0
- package/skills/eval-audit/SKILL.md +183 -0
- package/skills/evaluate-rag/SKILL.md +177 -0
- package/skills/generate-synthetic-data/SKILL.md +131 -0
- package/skills/mabl-mcp/SKILL.md +79 -0
- package/skills/playwright-cli/SKILL.md +246 -0
- package/skills/playwright-cli/advanced-workflows.md +679 -0
- package/skills/playwright-cli/core-commands.md +375 -0
- package/skills/playwright-cli/device-emulation.md +475 -0
- package/skills/playwright-cli/request-mocking.md +373 -0
- package/skills/playwright-cli/running-custom-code.md +553 -0
- package/skills/playwright-cli/screenshots-and-media.md +457 -0
- package/skills/playwright-cli/session-management.md +425 -0
- package/skills/playwright-cli/storage-and-auth.md +487 -0
- package/skills/playwright-cli/test-generation.md +336 -0
- package/skills/playwright-cli/tracing-and-debugging.md +501 -0
- package/skills/playwright-core/SKILL.md +101 -0
- package/skills/playwright-core/accessibility.md +1517 -0
- package/skills/playwright-core/angular.md +1086 -0
- package/skills/playwright-core/api-testing.md +1623 -0
- package/skills/playwright-core/assertions-and-waiting.md +703 -0
- package/skills/playwright-core/auth-flows.md +1081 -0
- package/skills/playwright-core/authentication.md +1409 -0
- package/skills/playwright-core/browser-apis.md +673 -0
- package/skills/playwright-core/browser-extensions.md +319 -0
- package/skills/playwright-core/canvas-and-webgl.md +494 -0
- package/skills/playwright-core/clock-and-time-mocking.md +427 -0
- package/skills/playwright-core/common-pitfalls.md +1318 -0
- package/skills/playwright-core/component-testing.md +1179 -0
- package/skills/playwright-core/configuration.md +731 -0
- package/skills/playwright-core/crud-testing.md +945 -0
- package/skills/playwright-core/debugging.md +827 -0
- package/skills/playwright-core/drag-and-drop.md +992 -0
- package/skills/playwright-core/electron-testing.md +622 -0
- package/skills/playwright-core/error-and-edge-cases.md +1181 -0
- package/skills/playwright-core/error-index.md +1903 -0
- package/skills/playwright-core/file-operations.md +749 -0
- package/skills/playwright-core/file-upload-download.md +1012 -0
- package/skills/playwright-core/fixtures-and-hooks.md +1072 -0
- package/skills/playwright-core/flaky-tests.md +883 -0
- package/skills/playwright-core/forms-and-validation.md +1056 -0
- package/skills/playwright-core/i18n-and-localization.md +622 -0
- package/skills/playwright-core/iframes-and-shadow-dom.md +488 -0
- package/skills/playwright-core/locator-strategy.md +588 -0
- package/skills/playwright-core/locators.md +743 -0
- package/skills/playwright-core/mobile-and-responsive.md +1669 -0
- package/skills/playwright-core/multi-context-and-popups.md +581 -0
- package/skills/playwright-core/multi-user-and-collaboration.md +477 -0
- package/skills/playwright-core/network-mocking.md +1395 -0
- package/skills/playwright-core/nextjs.md +1013 -0
- package/skills/playwright-core/performance-testing.md +608 -0
- package/skills/playwright-core/react.md +1082 -0
- package/skills/playwright-core/search-and-filter.md +1366 -0
- package/skills/playwright-core/security-testing.md +624 -0
- package/skills/playwright-core/service-workers-and-pwa.md +524 -0
- package/skills/playwright-core/test-architecture.md +569 -0
- package/skills/playwright-core/test-data-management.md +1209 -0
- package/skills/playwright-core/test-organization.md +947 -0
- package/skills/playwright-core/third-party-integrations.md +754 -0
- package/skills/playwright-core/trace-analysis.md +161 -0
- package/skills/playwright-core/visual-regression.md +1027 -0
- package/skills/playwright-core/vue.md +1162 -0
- package/skills/playwright-core/websockets-and-realtime.md +623 -0
- package/skills/playwright-core/when-to-mock.md +827 -0
- package/skills/playwright-pom/SKILL.md +94 -0
- package/skills/playwright-pom/page-object-model.md +921 -0
- package/skills/playwright-pom/pom-vs-fixtures-vs-helpers.md +937 -0
- package/skills/validate-evaluator/SKILL.md +215 -0
- package/skills/write-judge-prompt/SKILL.md +144 -0
- package/themes/dino.json +66 -0
|
@@ -0,0 +1,731 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
> **When to use**: Setting up a new Playwright project, adjusting timeouts, adding browser targets, configuring CI behavior, or connecting environment-specific settings.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npx playwright init # scaffold config + first test
|
|
9
|
+
npx playwright test --config=custom.config.ts # use non-default config
|
|
10
|
+
npx playwright test --project=chromium # run single project
|
|
11
|
+
npx playwright test --reporter=html # override reporter
|
|
12
|
+
npx playwright show-report # open last HTML report
|
|
13
|
+
DEBUG=pw:api npx playwright test # verbose Playwright logging
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Production-Ready Config (Copy-Paste Starter)
|
|
17
|
+
|
|
18
|
+
### TypeScript
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// playwright.config.ts
|
|
22
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
23
|
+
import dotenv from 'dotenv';
|
|
24
|
+
import path from 'path';
|
|
25
|
+
|
|
26
|
+
// Load environment variables from .env file
|
|
27
|
+
dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
// ── Test discovery ──────────────────────────────────────────────
|
|
31
|
+
testDir: './tests',
|
|
32
|
+
testMatch: '**/*.spec.ts',
|
|
33
|
+
|
|
34
|
+
// ── Execution ───────────────────────────────────────────────────
|
|
35
|
+
fullyParallel: true,
|
|
36
|
+
forbidOnly: !!process.env.CI, // fail CI if test.only left in
|
|
37
|
+
retries: process.env.CI ? 2 : 0, // retry flakes in CI only
|
|
38
|
+
workers: process.env.CI ? '50%' : undefined, // half CPU in CI, auto locally
|
|
39
|
+
|
|
40
|
+
// ── Reporting ───────────────────────────────────────────────────
|
|
41
|
+
reporter: process.env.CI
|
|
42
|
+
? [['html', { open: 'never' }], ['github']]
|
|
43
|
+
: [['html', { open: 'on-failure' }]],
|
|
44
|
+
|
|
45
|
+
// ── Timeouts ────────────────────────────────────────────────────
|
|
46
|
+
timeout: 30_000, // per-test timeout
|
|
47
|
+
expect: {
|
|
48
|
+
timeout: 5_000, // per-assertion retry timeout
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
// ── Shared browser context options ──────────────────────────────
|
|
52
|
+
use: {
|
|
53
|
+
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
|
54
|
+
actionTimeout: 10_000, // click, fill, etc.
|
|
55
|
+
navigationTimeout: 15_000, // goto, waitForURL, etc.
|
|
56
|
+
|
|
57
|
+
// Artifact collection
|
|
58
|
+
trace: 'on-first-retry', // full trace on first retry only
|
|
59
|
+
screenshot: 'only-on-failure', // screenshot on failure
|
|
60
|
+
video: 'retain-on-failure', // video only kept for failures
|
|
61
|
+
// 1.61+ adds video modes matching trace: 'on-all-retries',
|
|
62
|
+
// 'retain-on-first-failure', 'retain-on-failure-and-retries'
|
|
63
|
+
|
|
64
|
+
// Sensible defaults
|
|
65
|
+
locale: 'en-US',
|
|
66
|
+
timezoneId: 'America/New_York',
|
|
67
|
+
extraHTTPHeaders: {
|
|
68
|
+
'x-test-automation': 'playwright',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// ── Projects (browser targets) ─────────────────────────────────
|
|
73
|
+
projects: [
|
|
74
|
+
{
|
|
75
|
+
name: 'chromium',
|
|
76
|
+
use: { ...devices['Desktop Chrome'] },
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'firefox',
|
|
80
|
+
use: { ...devices['Desktop Firefox'] },
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'webkit',
|
|
84
|
+
use: { ...devices['Desktop Safari'] },
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'mobile-chrome',
|
|
88
|
+
use: { ...devices['Pixel 7'] },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'mobile-safari',
|
|
92
|
+
use: { ...devices['iPhone 14'] },
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
|
|
96
|
+
// ── Dev server ──────────────────────────────────────────────────
|
|
97
|
+
webServer: {
|
|
98
|
+
command: 'npm run dev',
|
|
99
|
+
url: 'http://localhost:3000',
|
|
100
|
+
reuseExistingServer: !process.env.CI,
|
|
101
|
+
timeout: 120_000, // 2 min for cold builds
|
|
102
|
+
stdout: 'pipe',
|
|
103
|
+
stderr: 'pipe',
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### JavaScript
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
// playwright.config.js
|
|
112
|
+
const { defineConfig, devices } = require('@playwright/test');
|
|
113
|
+
const dotenv = require('dotenv');
|
|
114
|
+
const path = require('path');
|
|
115
|
+
|
|
116
|
+
dotenv.config({ path: path.resolve(__dirname, '.env') });
|
|
117
|
+
|
|
118
|
+
module.exports = defineConfig({
|
|
119
|
+
testDir: './tests',
|
|
120
|
+
testMatch: '**/*.spec.js',
|
|
121
|
+
|
|
122
|
+
fullyParallel: true,
|
|
123
|
+
forbidOnly: !!process.env.CI,
|
|
124
|
+
retries: process.env.CI ? 2 : 0,
|
|
125
|
+
workers: process.env.CI ? '50%' : undefined,
|
|
126
|
+
|
|
127
|
+
reporter: process.env.CI
|
|
128
|
+
? [['html', { open: 'never' }], ['github']]
|
|
129
|
+
: [['html', { open: 'on-failure' }]],
|
|
130
|
+
|
|
131
|
+
timeout: 30_000,
|
|
132
|
+
expect: {
|
|
133
|
+
timeout: 5_000,
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
use: {
|
|
137
|
+
baseURL: process.env.BASE_URL || 'http://localhost:3000',
|
|
138
|
+
actionTimeout: 10_000,
|
|
139
|
+
navigationTimeout: 15_000,
|
|
140
|
+
|
|
141
|
+
trace: 'on-first-retry',
|
|
142
|
+
screenshot: 'only-on-failure',
|
|
143
|
+
video: 'retain-on-failure',
|
|
144
|
+
|
|
145
|
+
locale: 'en-US',
|
|
146
|
+
timezoneId: 'America/New_York',
|
|
147
|
+
extraHTTPHeaders: {
|
|
148
|
+
'x-test-automation': 'playwright',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
projects: [
|
|
153
|
+
{
|
|
154
|
+
name: 'chromium',
|
|
155
|
+
use: { ...devices['Desktop Chrome'] },
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'firefox',
|
|
159
|
+
use: { ...devices['Desktop Firefox'] },
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'webkit',
|
|
163
|
+
use: { ...devices['Desktop Safari'] },
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'mobile-chrome',
|
|
167
|
+
use: { ...devices['Pixel 7'] },
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'mobile-safari',
|
|
171
|
+
use: { ...devices['iPhone 14'] },
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
|
|
175
|
+
webServer: {
|
|
176
|
+
command: 'npm run dev',
|
|
177
|
+
url: 'http://localhost:3000',
|
|
178
|
+
reuseExistingServer: !process.env.CI,
|
|
179
|
+
timeout: 120_000,
|
|
180
|
+
stdout: 'pipe',
|
|
181
|
+
stderr: 'pipe',
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Patterns
|
|
187
|
+
|
|
188
|
+
### Pattern 1: Environment-Specific Configuration
|
|
189
|
+
|
|
190
|
+
**Use when**: Tests run against dev, staging, and production environments.
|
|
191
|
+
**Avoid when**: Single-environment local-only projects.
|
|
192
|
+
|
|
193
|
+
#### TypeScript
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// playwright.config.ts
|
|
197
|
+
import { defineConfig } from '@playwright/test';
|
|
198
|
+
import dotenv from 'dotenv';
|
|
199
|
+
import path from 'path';
|
|
200
|
+
|
|
201
|
+
// Load environment-specific .env file: .env.staging, .env.production, etc.
|
|
202
|
+
const ENV = process.env.TEST_ENV || 'local';
|
|
203
|
+
dotenv.config({ path: path.resolve(__dirname, `.env.${ENV}`) });
|
|
204
|
+
|
|
205
|
+
const envConfig: Record<string, { baseURL: string; retries: number }> = {
|
|
206
|
+
local: { baseURL: 'http://localhost:3000', retries: 0 },
|
|
207
|
+
staging: { baseURL: 'https://staging.example.com', retries: 2 },
|
|
208
|
+
production: { baseURL: 'https://www.example.com', retries: 2 },
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const env = envConfig[ENV];
|
|
212
|
+
|
|
213
|
+
export default defineConfig({
|
|
214
|
+
testDir: './tests',
|
|
215
|
+
testMatch: '**/*.spec.ts',
|
|
216
|
+
retries: env.retries,
|
|
217
|
+
use: {
|
|
218
|
+
baseURL: env.baseURL,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# Run against staging
|
|
225
|
+
TEST_ENV=staging npx playwright test
|
|
226
|
+
|
|
227
|
+
# Run against production (subset of smoke tests)
|
|
228
|
+
TEST_ENV=production npx playwright test --grep @smoke
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### JavaScript
|
|
232
|
+
|
|
233
|
+
```js
|
|
234
|
+
// playwright.config.js
|
|
235
|
+
const { defineConfig } = require('@playwright/test');
|
|
236
|
+
const dotenv = require('dotenv');
|
|
237
|
+
const path = require('path');
|
|
238
|
+
|
|
239
|
+
const ENV = process.env.TEST_ENV || 'local';
|
|
240
|
+
dotenv.config({ path: path.resolve(__dirname, `.env.${ENV}`) });
|
|
241
|
+
|
|
242
|
+
const envConfig = {
|
|
243
|
+
local: { baseURL: 'http://localhost:3000', retries: 0 },
|
|
244
|
+
staging: { baseURL: 'https://staging.example.com', retries: 2 },
|
|
245
|
+
production: { baseURL: 'https://www.example.com', retries: 2 },
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const env = envConfig[ENV];
|
|
249
|
+
|
|
250
|
+
module.exports = defineConfig({
|
|
251
|
+
testDir: './tests',
|
|
252
|
+
testMatch: '**/*.spec.js',
|
|
253
|
+
retries: env.retries,
|
|
254
|
+
use: {
|
|
255
|
+
baseURL: env.baseURL,
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Pattern 2: Multi-Project with Setup Dependencies
|
|
261
|
+
|
|
262
|
+
**Use when**: Tests need shared authentication state or database seeding before running.
|
|
263
|
+
**Avoid when**: Tests are fully independent with no shared setup phase.
|
|
264
|
+
|
|
265
|
+
#### TypeScript
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
// playwright.config.ts
|
|
269
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
270
|
+
|
|
271
|
+
export default defineConfig({
|
|
272
|
+
testDir: './tests',
|
|
273
|
+
testMatch: '**/*.spec.ts',
|
|
274
|
+
|
|
275
|
+
projects: [
|
|
276
|
+
// Setup project runs first, saves auth state
|
|
277
|
+
{
|
|
278
|
+
name: 'setup',
|
|
279
|
+
testMatch: /global\.setup\.ts/,
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
// Browser projects depend on setup
|
|
283
|
+
{
|
|
284
|
+
name: 'chromium',
|
|
285
|
+
use: {
|
|
286
|
+
...devices['Desktop Chrome'],
|
|
287
|
+
storageState: 'playwright/.auth/user.json',
|
|
288
|
+
},
|
|
289
|
+
dependencies: ['setup'],
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'firefox',
|
|
293
|
+
use: {
|
|
294
|
+
...devices['Desktop Firefox'],
|
|
295
|
+
storageState: 'playwright/.auth/user.json',
|
|
296
|
+
},
|
|
297
|
+
dependencies: ['setup'],
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
// tests/global.setup.ts
|
|
305
|
+
import { test as setup, expect } from '@playwright/test';
|
|
306
|
+
|
|
307
|
+
const authFile = 'playwright/.auth/user.json';
|
|
308
|
+
|
|
309
|
+
setup('authenticate', async ({ page }) => {
|
|
310
|
+
await page.goto('/login');
|
|
311
|
+
await page.getByLabel('Email').fill('user@example.com');
|
|
312
|
+
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
|
|
313
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
314
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
315
|
+
await page.context().storageState({ path: authFile });
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### JavaScript
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
// playwright.config.js
|
|
323
|
+
const { defineConfig, devices } = require('@playwright/test');
|
|
324
|
+
|
|
325
|
+
module.exports = defineConfig({
|
|
326
|
+
testDir: './tests',
|
|
327
|
+
testMatch: '**/*.spec.js',
|
|
328
|
+
|
|
329
|
+
projects: [
|
|
330
|
+
{
|
|
331
|
+
name: 'setup',
|
|
332
|
+
testMatch: /global\.setup\.js/,
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: 'chromium',
|
|
336
|
+
use: {
|
|
337
|
+
...devices['Desktop Chrome'],
|
|
338
|
+
storageState: 'playwright/.auth/user.json',
|
|
339
|
+
},
|
|
340
|
+
dependencies: ['setup'],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: 'firefox',
|
|
344
|
+
use: {
|
|
345
|
+
...devices['Desktop Firefox'],
|
|
346
|
+
storageState: 'playwright/.auth/user.json',
|
|
347
|
+
},
|
|
348
|
+
dependencies: ['setup'],
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
// tests/global.setup.js
|
|
356
|
+
const { test: setup, expect } = require('@playwright/test');
|
|
357
|
+
|
|
358
|
+
const authFile = 'playwright/.auth/user.json';
|
|
359
|
+
|
|
360
|
+
setup('authenticate', async ({ page }) => {
|
|
361
|
+
await page.goto('/login');
|
|
362
|
+
await page.getByLabel('Email').fill('user@example.com');
|
|
363
|
+
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD);
|
|
364
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
365
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
366
|
+
await page.context().storageState({ path: authFile });
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Pattern 3: `webServer` with Build Step
|
|
371
|
+
|
|
372
|
+
**Use when**: Tests need a running application server. Let Playwright manage the server lifecycle.
|
|
373
|
+
**Avoid when**: Testing against an already-deployed environment (staging/prod).
|
|
374
|
+
|
|
375
|
+
#### TypeScript
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
// playwright.config.ts
|
|
379
|
+
import { defineConfig } from '@playwright/test';
|
|
380
|
+
|
|
381
|
+
export default defineConfig({
|
|
382
|
+
testDir: './tests',
|
|
383
|
+
testMatch: '**/*.spec.ts',
|
|
384
|
+
use: {
|
|
385
|
+
baseURL: 'http://localhost:3000',
|
|
386
|
+
},
|
|
387
|
+
webServer: {
|
|
388
|
+
command: process.env.CI
|
|
389
|
+
? 'npm run build && npm run start' // production build in CI
|
|
390
|
+
: 'npm run dev', // dev server locally
|
|
391
|
+
url: 'http://localhost:3000',
|
|
392
|
+
reuseExistingServer: !process.env.CI,
|
|
393
|
+
timeout: 120_000,
|
|
394
|
+
stdout: 'pipe',
|
|
395
|
+
stderr: 'pipe',
|
|
396
|
+
env: {
|
|
397
|
+
NODE_ENV: 'test',
|
|
398
|
+
DATABASE_URL: process.env.DATABASE_URL || 'postgresql://localhost:5432/test',
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### JavaScript
|
|
405
|
+
|
|
406
|
+
```js
|
|
407
|
+
// playwright.config.js
|
|
408
|
+
const { defineConfig } = require('@playwright/test');
|
|
409
|
+
|
|
410
|
+
module.exports = defineConfig({
|
|
411
|
+
testDir: './tests',
|
|
412
|
+
testMatch: '**/*.spec.js',
|
|
413
|
+
use: {
|
|
414
|
+
baseURL: 'http://localhost:3000',
|
|
415
|
+
},
|
|
416
|
+
webServer: {
|
|
417
|
+
command: process.env.CI
|
|
418
|
+
? 'npm run build && npm run start'
|
|
419
|
+
: 'npm run dev',
|
|
420
|
+
url: 'http://localhost:3000',
|
|
421
|
+
reuseExistingServer: !process.env.CI,
|
|
422
|
+
timeout: 120_000,
|
|
423
|
+
stdout: 'pipe',
|
|
424
|
+
stderr: 'pipe',
|
|
425
|
+
env: {
|
|
426
|
+
NODE_ENV: 'test',
|
|
427
|
+
DATABASE_URL: process.env.DATABASE_URL || 'postgresql://localhost:5432/test',
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Pattern 4: `globalSetup` / `globalTeardown`
|
|
434
|
+
|
|
435
|
+
**Use when**: One-time non-browser work: seeding a database, starting a service, setting env vars. Runs once per `npx playwright test` invocation.
|
|
436
|
+
**Avoid when**: You need browser context (use a setup project instead) or per-test isolation (use fixtures).
|
|
437
|
+
|
|
438
|
+
#### TypeScript
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
// playwright.config.ts
|
|
442
|
+
import { defineConfig } from '@playwright/test';
|
|
443
|
+
|
|
444
|
+
export default defineConfig({
|
|
445
|
+
testDir: './tests',
|
|
446
|
+
testMatch: '**/*.spec.ts',
|
|
447
|
+
globalSetup: './tests/global-setup.ts',
|
|
448
|
+
globalTeardown: './tests/global-teardown.ts',
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
// tests/global-setup.ts
|
|
454
|
+
import { FullConfig } from '@playwright/test';
|
|
455
|
+
|
|
456
|
+
async function globalSetup(config: FullConfig) {
|
|
457
|
+
// Seed test database
|
|
458
|
+
const { execSync } = await import('child_process');
|
|
459
|
+
execSync('npx prisma db seed', { stdio: 'inherit' });
|
|
460
|
+
|
|
461
|
+
// Store data for tests to use via environment variables
|
|
462
|
+
process.env.TEST_RUN_ID = `run-${Date.now()}`;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export default globalSetup;
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
```ts
|
|
469
|
+
// tests/global-teardown.ts
|
|
470
|
+
import { FullConfig } from '@playwright/test';
|
|
471
|
+
|
|
472
|
+
async function globalTeardown(config: FullConfig) {
|
|
473
|
+
const { execSync } = await import('child_process');
|
|
474
|
+
execSync('npx prisma db push --force-reset', { stdio: 'inherit' });
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export default globalTeardown;
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
#### JavaScript
|
|
481
|
+
|
|
482
|
+
```js
|
|
483
|
+
// playwright.config.js
|
|
484
|
+
const { defineConfig } = require('@playwright/test');
|
|
485
|
+
|
|
486
|
+
module.exports = defineConfig({
|
|
487
|
+
testDir: './tests',
|
|
488
|
+
testMatch: '**/*.spec.js',
|
|
489
|
+
globalSetup: './tests/global-setup.js',
|
|
490
|
+
globalTeardown: './tests/global-teardown.js',
|
|
491
|
+
});
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
```js
|
|
495
|
+
// tests/global-setup.js
|
|
496
|
+
const { execSync } = require('child_process');
|
|
497
|
+
|
|
498
|
+
async function globalSetup(config) {
|
|
499
|
+
execSync('npx prisma db seed', { stdio: 'inherit' });
|
|
500
|
+
process.env.TEST_RUN_ID = `run-${Date.now()}`;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
module.exports = globalSetup;
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
```js
|
|
507
|
+
// tests/global-teardown.js
|
|
508
|
+
const { execSync } = require('child_process');
|
|
509
|
+
|
|
510
|
+
async function globalTeardown(config) {
|
|
511
|
+
execSync('npx prisma db push --force-reset', { stdio: 'inherit' });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
module.exports = globalTeardown;
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### Pattern 5: `.env` File Setup
|
|
518
|
+
|
|
519
|
+
**Use when**: Managing secrets, URLs, or feature flags without hardcoding.
|
|
520
|
+
**Avoid when**: Never commit `.env` files with real secrets. Provide `.env.example` instead.
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
# .env.example (commit this)
|
|
524
|
+
BASE_URL=http://localhost:3000
|
|
525
|
+
TEST_PASSWORD=
|
|
526
|
+
API_KEY=
|
|
527
|
+
|
|
528
|
+
# .env.local (gitignored)
|
|
529
|
+
BASE_URL=http://localhost:3000
|
|
530
|
+
TEST_PASSWORD=s3cret
|
|
531
|
+
API_KEY=test-key-abc123
|
|
532
|
+
|
|
533
|
+
# .env.staging (gitignored)
|
|
534
|
+
BASE_URL=https://staging.example.com
|
|
535
|
+
TEST_PASSWORD=staging-password
|
|
536
|
+
API_KEY=staging-key-xyz789
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
```bash
|
|
540
|
+
# .gitignore
|
|
541
|
+
.env
|
|
542
|
+
.env.local
|
|
543
|
+
.env.staging
|
|
544
|
+
.env.production
|
|
545
|
+
playwright/.auth/
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Install dotenv:
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
npm install -D dotenv
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Pattern 6: Trace, Screenshot, and Video Settings
|
|
555
|
+
|
|
556
|
+
**Use when**: Deciding artifact collection strategy for local development vs CI.
|
|
557
|
+
|
|
558
|
+
| Setting | Local | CI | Why |
|
|
559
|
+
|---|---|---|---|
|
|
560
|
+
| `trace` | `'off'` or `'on-first-retry'` | `'on-first-retry'` | Traces are large; only collect on failure |
|
|
561
|
+
| `screenshot` | `'off'` | `'only-on-failure'` | Useful for CI debugging only |
|
|
562
|
+
| `video` | `'off'` | `'retain-on-failure'` | Video is slow to record; keep only failures |
|
|
563
|
+
|
|
564
|
+
#### TypeScript
|
|
565
|
+
|
|
566
|
+
```ts
|
|
567
|
+
// playwright.config.ts
|
|
568
|
+
import { defineConfig } from '@playwright/test';
|
|
569
|
+
|
|
570
|
+
export default defineConfig({
|
|
571
|
+
testDir: './tests',
|
|
572
|
+
testMatch: '**/*.spec.ts',
|
|
573
|
+
use: {
|
|
574
|
+
// CI: capture everything on failure; Local: minimal overhead
|
|
575
|
+
trace: process.env.CI ? 'on-first-retry' : 'off',
|
|
576
|
+
screenshot: process.env.CI ? 'only-on-failure' : 'off',
|
|
577
|
+
video: process.env.CI ? 'retain-on-failure' : 'off',
|
|
578
|
+
},
|
|
579
|
+
});
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### JavaScript
|
|
583
|
+
|
|
584
|
+
```js
|
|
585
|
+
// playwright.config.js
|
|
586
|
+
const { defineConfig } = require('@playwright/test');
|
|
587
|
+
|
|
588
|
+
module.exports = defineConfig({
|
|
589
|
+
testDir: './tests',
|
|
590
|
+
testMatch: '**/*.spec.js',
|
|
591
|
+
use: {
|
|
592
|
+
trace: process.env.CI ? 'on-first-retry' : 'off',
|
|
593
|
+
screenshot: process.env.CI ? 'only-on-failure' : 'off',
|
|
594
|
+
video: process.env.CI ? 'retain-on-failure' : 'off',
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
## Decision Guide
|
|
600
|
+
|
|
601
|
+
### Which Timeout to Adjust
|
|
602
|
+
|
|
603
|
+
| Symptom | Timeout to Change | Default | Recommended Range |
|
|
604
|
+
|---|---|---|---|
|
|
605
|
+
| Test takes too long overall | `timeout` | 30s | 30-60s (never above 120s) |
|
|
606
|
+
| Assertion `expect()` keeps retrying too long or not long enough | `expect.timeout` | 5s | 5-10s |
|
|
607
|
+
| `page.goto()` or `waitForURL()` times out | `navigationTimeout` | 30s | 10-30s |
|
|
608
|
+
| `click()`, `fill()`, `check()` time out | `actionTimeout` | 0 (no limit) | 10-15s |
|
|
609
|
+
| Dev server slow to start | `webServer.timeout` | 60s | 60-180s |
|
|
610
|
+
|
|
611
|
+
### Server Management
|
|
612
|
+
|
|
613
|
+
| Scenario | Approach | Why |
|
|
614
|
+
|---|---|---|
|
|
615
|
+
| Local dev + CI, app in same repo | `webServer` with `reuseExistingServer: !process.env.CI` | Playwright manages server; reuses yours locally |
|
|
616
|
+
| Separate frontend/backend repos | Manual start or Docker Compose | `webServer` can only run one command |
|
|
617
|
+
| Testing deployed staging/production | No `webServer`; set `baseURL` via env var | Server already running remotely |
|
|
618
|
+
| Multiple services needed (API + frontend) | Array of `webServer` entries | Each gets its own command and URL health check |
|
|
619
|
+
|
|
620
|
+
### Single vs Multi-Project Config
|
|
621
|
+
|
|
622
|
+
| Scenario | Approach | Why |
|
|
623
|
+
|---|---|---|
|
|
624
|
+
| Starting out, early development | Single project (chromium only) | Faster feedback, simpler config |
|
|
625
|
+
| Pre-release cross-browser validation | Multi-project: chromium + firefox + webkit | Catch rendering/API differences |
|
|
626
|
+
| Mobile-responsive app | Add mobile projects alongside desktop | Viewport + touch differences matter |
|
|
627
|
+
| Authenticated + unauthenticated tests | Setup project + dependent projects | Share auth state without re-login per test |
|
|
628
|
+
| CI pipeline with tight time budget | Chromium in PR checks; all browsers on merge to main | Balance speed vs coverage |
|
|
629
|
+
|
|
630
|
+
### globalSetup vs Setup Projects vs Fixtures
|
|
631
|
+
|
|
632
|
+
| Need | Use | Why |
|
|
633
|
+
|---|---|---|
|
|
634
|
+
| One-time DB seed or external service prep | `globalSetup` | Runs once, no browser needed |
|
|
635
|
+
| Shared browser auth (login once, reuse cookies) | Setup project with `dependencies` | Needs browser context; `globalSetup` has none |
|
|
636
|
+
| Per-test isolated state (unique user, fresh data) | Custom fixture via `test.extend()` | Each test gets its own instance with teardown |
|
|
637
|
+
| Cleanup after all tests | `globalTeardown` | Runs once at the end regardless of pass/fail |
|
|
638
|
+
|
|
639
|
+
## Anti-Patterns
|
|
640
|
+
|
|
641
|
+
| Don't Do This | Problem | Do This Instead |
|
|
642
|
+
|---|---|---|
|
|
643
|
+
| `timeout: 300_000` globally | Masks flaky tests; CI runs take forever | Fix the root cause; keep timeout at 30s; raise `navigationTimeout` only if warranted |
|
|
644
|
+
| Hardcoded URLs in tests: `page.goto('http://localhost:3000/login')` | Breaks in every non-local environment | Use `baseURL` in config, then `page.goto('/login')` |
|
|
645
|
+
| Running chromium + firefox + webkit on every PR | 3x CI time for marginal benefit on most PRs | Chromium on PRs; all browsers on main branch merges |
|
|
646
|
+
| `trace: 'on'` always in CI | Huge artifacts, slow uploads, disk full | `trace: 'on-first-retry'` -- only captures when a test fails and retries |
|
|
647
|
+
| `video: 'on'` always in CI | Massive CI storage; recording slows tests | `video: 'retain-on-failure'` -- records all but only keeps failures |
|
|
648
|
+
| Config values inline in test files: `test.use({ viewport: { width: 1280, height: 720 } })` in every file | Scattered, hard to maintain, inconsistent | Define once in project config; override per-file only when genuinely needed |
|
|
649
|
+
| `retries: 3` locally | Hides flakiness during development | `retries: 0` locally, `retries: 2` in CI |
|
|
650
|
+
| No `forbidOnly` in CI | Accidentally committed `test.only` runs a single test, everything else silently skipped | `forbidOnly: !!process.env.CI` |
|
|
651
|
+
| `globalSetup` for browser auth | No browser context available; complex workarounds needed | Use a setup project with `dependencies` |
|
|
652
|
+
| Committing `.env` files with real credentials | Security risk | Commit `.env.example` only; gitignore real `.env` files |
|
|
653
|
+
|
|
654
|
+
## Troubleshooting
|
|
655
|
+
|
|
656
|
+
### "baseURL" not working -- tests navigate to full URL
|
|
657
|
+
|
|
658
|
+
**Cause**: Using `page.goto('http://localhost:3000/path')` instead of `page.goto('/path')`. When `goto` receives an absolute URL, it ignores `baseURL`.
|
|
659
|
+
|
|
660
|
+
**Fix**: Always pass relative paths to `page.goto()`:
|
|
661
|
+
|
|
662
|
+
```ts
|
|
663
|
+
// Wrong -- ignores baseURL
|
|
664
|
+
await page.goto('http://localhost:3000/dashboard');
|
|
665
|
+
|
|
666
|
+
// Correct -- uses baseURL from config
|
|
667
|
+
await page.goto('/dashboard');
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### webServer starts but tests still fail with connection refused
|
|
671
|
+
|
|
672
|
+
**Cause**: The `url` in `webServer` does not match what the server actually serves, or the health check endpoint returns non-200.
|
|
673
|
+
|
|
674
|
+
**Fix**: Ensure `webServer.url` matches the actual server address. Add a health check route if needed:
|
|
675
|
+
|
|
676
|
+
```ts
|
|
677
|
+
// playwright.config.ts
|
|
678
|
+
import { defineConfig } from '@playwright/test';
|
|
679
|
+
|
|
680
|
+
export default defineConfig({
|
|
681
|
+
testDir: './tests',
|
|
682
|
+
testMatch: '**/*.spec.ts',
|
|
683
|
+
webServer: {
|
|
684
|
+
command: 'npm run dev',
|
|
685
|
+
url: 'http://localhost:3000/api/health', // use a real endpoint
|
|
686
|
+
reuseExistingServer: !process.env.CI,
|
|
687
|
+
timeout: 120_000,
|
|
688
|
+
},
|
|
689
|
+
});
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Tests pass locally but timeout in CI
|
|
693
|
+
|
|
694
|
+
**Cause**: CI machines are slower. Default timeouts too tight for CI hardware.
|
|
695
|
+
|
|
696
|
+
**Fix**: Increase `navigationTimeout` for CI, reduce `workers` to avoid resource contention:
|
|
697
|
+
|
|
698
|
+
```ts
|
|
699
|
+
// playwright.config.ts
|
|
700
|
+
import { defineConfig } from '@playwright/test';
|
|
701
|
+
|
|
702
|
+
export default defineConfig({
|
|
703
|
+
testDir: './tests',
|
|
704
|
+
testMatch: '**/*.spec.ts',
|
|
705
|
+
workers: process.env.CI ? '50%' : undefined,
|
|
706
|
+
use: {
|
|
707
|
+
navigationTimeout: process.env.CI ? 30_000 : 15_000,
|
|
708
|
+
actionTimeout: process.env.CI ? 15_000 : 10_000,
|
|
709
|
+
},
|
|
710
|
+
});
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### "Error: page.goto: Target page, context or browser has been closed"
|
|
714
|
+
|
|
715
|
+
**Cause**: Test exceeded its `timeout` and Playwright tore down the browser while an action was still running.
|
|
716
|
+
|
|
717
|
+
**Fix**: Do not increase the global timeout. Instead, find the slow step using `--trace on` and fix it. Common causes: waiting for a slow API, unresolved network request, or missing `await`.
|
|
718
|
+
|
|
719
|
+
```bash
|
|
720
|
+
# Record a trace for debugging
|
|
721
|
+
npx playwright test --trace on
|
|
722
|
+
npx playwright show-report
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
## Related
|
|
726
|
+
|
|
727
|
+
- [core/fixtures-and-hooks.md](fixtures-and-hooks.md) -- custom fixtures that replace `globalSetup` for per-test state
|
|
728
|
+
- [core/test-organization.md](test-organization.md) -- file structure, naming conventions, test grouping
|
|
729
|
+
- [core/authentication.md](authentication.md) -- setup projects for shared auth state
|
|
730
|
+
- [ci/ci-github-actions.md](../ci/ci-github-actions.md) -- CI-specific config and caching
|
|
731
|
+
- [ci/projects-and-dependencies.md](../ci/projects-and-dependencies.md) -- advanced multi-project patterns
|