Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Playwright testing guide covering E2E, component, API, visual, accessibility, and security tests.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
core/configuration.md
1# Playwright Configuration23## Table of Contents451. [CLI Quick Reference](#cli-quick-reference)62. [Decision Guide](#decision-guide)73. [Production-Ready Config](#production-ready-config)84. [Patterns](#patterns)95. [Anti-Patterns](#anti-patterns)106. [Troubleshooting](#troubleshooting)117. [Related](#related)1213> **When to use**: Setting up a new project, adjusting timeouts, adding browser targets, configuring CI behavior, or managing environment-specific settings.1415## CLI Quick Reference1617```bash18npx playwright init # scaffold config + first test19npx playwright test --config=custom.config.ts # use alternate config20npx playwright test --project=chromium # run single project21npx playwright test --reporter=html # override reporter22npx playwright test --grep @smoke # run tests tagged @smoke23npx playwright test --grep-invert @slow # exclude @slow tests24npx playwright show-report # open last HTML report25DEBUG=pw:api npx playwright test # verbose logging26```2728## Decision Guide2930### Timeout Selection3132| Symptom | Setting | Default | Recommended |33|---------|---------|---------|-------------|34| Test takes too long overall | `timeout` | 30s | 30-60s (max 120s) |35| Assertion retries too long/short | `expect.timeout` | 5s | 5-10s |36| `page.goto()` or `waitForURL()` times out | `navigationTimeout` | 30s | 10-30s |37| `click()`, `fill()` time out | `actionTimeout` | 0 (unlimited) | 10-15s |38| Dev server slow to start | `webServer.timeout` | 60s | 60-180s |3940### Server Management4142| Scenario | Approach |43|----------|----------|44| App in same repo | `webServer` with `reuseExistingServer: !process.env.CI` |45| Separate repos | Manual start or Docker Compose |46| Testing deployed environment | No `webServer`; set `baseURL` via env |47| Multiple services | Array of `webServer` entries |4849### Single vs Multi-Project5051| Scenario | Approach |52|----------|----------|53| Early development | Single project (chromium only) |54| Pre-release validation | Multi-project: chromium + firefox + webkit |55| Mobile-responsive app | Add mobile projects alongside desktop |56| Auth + non-auth tests | Setup project with dependencies |57| Tight CI budget | Chromium on PRs; all browsers on main |5859### globalSetup vs Setup Projects vs Fixtures6061| Need | Use |62|------|-----|63| One-time DB seed | `globalSetup` |64| Shared browser auth | Setup project with `dependencies` |65| Per-test isolated state | Custom fixture via `test.extend()` |66| Cleanup after all tests | `globalTeardown` |6768## Production-Ready Config6970```ts71// playwright.config.ts72import { defineConfig, devices } from '@playwright/test';73import dotenv from 'dotenv';74import path from 'path';7576dotenv.config({ path: path.resolve(__dirname, '.env') });7778export default defineConfig({79testDir: './e2e',80testMatch: '**/*.spec.ts',8182fullyParallel: true,83forbidOnly: !!process.env.CI,84retries: process.env.CI ? 2 : 0,85workers: process.env.CI ? '50%' : undefined,8687reporter: process.env.CI88? [['html', { open: 'never' }], ['github']]89: [['html', { open: 'on-failure' }]],9091timeout: 30_000,92expect: { timeout: 5_000 },9394use: {95baseURL: process.env.BASE_URL || 'http://localhost:4000',96actionTimeout: 10_000,97navigationTimeout: 15_000,98trace: 'on-first-retry',99screenshot: 'only-on-failure',100video: 'retain-on-failure',101locale: 'en-US',102timezoneId: 'America/Los_Angeles',103},104105projects: [106{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },107{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },108{ name: 'webkit', use: { ...devices['Desktop Safari'] } },109{ name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },110{ name: 'mobile-safari', use: { ...devices['iPhone 14'] } },111],112113webServer: {114command: 'npm run start',115url: 'http://localhost:4000',116reuseExistingServer: !process.env.CI,117timeout: 120_000,118stdout: 'pipe',119stderr: 'pipe',120},121});122```123124## Patterns125126### Environment-Specific Configuration127128**Use when**: Tests run against dev, staging, and production environments.129130```ts131// playwright.config.ts132import { defineConfig } from '@playwright/test';133import dotenv from 'dotenv';134import path from 'path';135136const ENV = process.env.TEST_ENV || 'local';137dotenv.config({ path: path.resolve(__dirname, `.env.${ENV}`) });138139const envConfig: Record<string, { baseURL: string; retries: number }> = {140local: { baseURL: 'http://localhost:4000', retries: 0 },141staging: { baseURL: 'https://staging.myapp.com', retries: 2 },142prod: { baseURL: 'https://myapp.com', retries: 2 },143};144145export default defineConfig({146testDir: './e2e',147retries: envConfig[ENV].retries,148use: { baseURL: envConfig[ENV].baseURL },149});150```151152```bash153TEST_ENV=staging npx playwright test154TEST_ENV=prod npx playwright test --grep @smoke155```156157### Setup Project with Dependencies158159**Use when**: Tests need shared authentication state before running.160161```ts162// playwright.config.ts163import { defineConfig, devices } from '@playwright/test';164165export default defineConfig({166testDir: './e2e',167projects: [168{169name: 'setup',170testMatch: /auth\.setup\.ts/,171},172{173name: 'chromium',174use: {175...devices['Desktop Chrome'],176storageState: 'playwright/.auth/session.json',177},178dependencies: ['setup'],179},180{181name: 'firefox',182use: {183...devices['Desktop Firefox'],184storageState: 'playwright/.auth/session.json',185},186dependencies: ['setup'],187},188],189});190```191192```ts193// e2e/auth.setup.ts194import { test as setup, expect } from '@playwright/test';195196const authFile = 'playwright/.auth/session.json';197198setup('authenticate', async ({ page }) => {199await page.goto('/login');200await page.getByLabel('Username').fill('[email protected]');201await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);202await page.getByRole('button', { name: 'Log in' }).click();203await expect(page.getByRole('heading', { name: 'Home' })).toBeVisible();204await page.context().storageState({ path: authFile });205});206```207208### webServer with Build Step209210**Use when**: Tests need a running application server managed by Playwright.211212```ts213// playwright.config.ts214import { defineConfig } from '@playwright/test';215216export default defineConfig({217testDir: './e2e',218use: { baseURL: 'http://localhost:4000' },219webServer: {220command: process.env.CI221? 'npm run build && npm run preview'222: 'npm run dev',223url: 'http://localhost:4000',224reuseExistingServer: !process.env.CI,225timeout: 120_000,226env: {227NODE_ENV: 'test',228DB_URL: process.env.DB_URL || 'postgresql://localhost:5432/testdb',229},230},231});232```233234### globalSetup / globalTeardown235236**Use when**: One-time non-browser work like seeding a database. Runs once per test run.237238```ts239// playwright.config.ts240import { defineConfig } from '@playwright/test';241242export default defineConfig({243testDir: './e2e',244globalSetup: './e2e/setup.ts',245globalTeardown: './e2e/teardown.ts',246});247```248249```ts250// e2e/setup.ts251import { FullConfig } from '@playwright/test';252253export default async function globalSetup(config: FullConfig) {254const { execSync } = await import('child_process');255execSync('npx prisma db seed', { stdio: 'inherit' });256process.env.TEST_RUN_ID = `run-${Date.now()}`;257}258```259260```ts261// e2e/teardown.ts262import { FullConfig } from '@playwright/test';263264export default async function globalTeardown(config: FullConfig) {265const { execSync } = await import('child_process');266execSync('npx prisma db push --force-reset', { stdio: 'inherit' });267}268```269270### Environment Variables with .env271272**Use when**: Managing secrets, URLs, or feature flags without hardcoding.273274```bash275# .env.example (commit this)276BASE_URL=http://localhost:4000277TEST_PASSWORD=278API_KEY=279280# .env.local (gitignored)281BASE_URL=http://localhost:4000282TEST_PASSWORD=secret123283API_KEY=dev-key-abc284285# .env.staging (gitignored)286BASE_URL=https://staging.myapp.com287TEST_PASSWORD=staging-pass288API_KEY=staging-key-xyz289```290291```bash292# .gitignore293.env294.env.local295.env.staging296.env.production297playwright/.auth/298```299300Install dotenv:301302```bash303npm install -D dotenv304```305306### Tag-Based Test Filtering307308**Use when**: Running subsets of tests in different CI stages (PR vs nightly).309310```ts311// playwright.config.ts312import { defineConfig } from '@playwright/test';313314export default defineConfig({315testDir: './e2e',316317// Filter by tags in CI318grep: process.env.CI ? /@smoke|@critical/ : undefined,319grepInvert: process.env.CI ? /@flaky/ : undefined,320});321```322323**Project-specific filtering:**324325```ts326// playwright.config.ts327import { defineConfig, devices } from '@playwright/test';328329export default defineConfig({330testDir: './e2e',331projects: [332{333name: 'smoke',334grep: /@smoke/,335use: { ...devices['Desktop Chrome'] },336},337{338name: 'regression',339grepInvert: /@smoke/,340use: { ...devices['Desktop Chrome'] },341},342{343name: 'critical-only',344grep: /@critical/,345use: { ...devices['Desktop Chrome'] },346},347],348});349```350351```bash352# Run specific project353npx playwright test --project=smoke354npx playwright test --project=regression355```356357### Artifact Collection Strategy358359| Setting | Local | CI | Reason |360|---------|-------|-----|--------|361| `trace` | `'off'` | `'on-first-retry'` | Traces are large; collect on failure only |362| `screenshot` | `'off'` | `'only-on-failure'` | Useful for CI debugging |363| `video` | `'off'` | `'retain-on-failure'` | Recording slows tests |364365```ts366// playwright.config.ts367import { defineConfig } from '@playwright/test';368369export default defineConfig({370testDir: './e2e',371use: {372trace: process.env.CI ? 'on-first-retry' : 'off',373screenshot: process.env.CI ? 'only-on-failure' : 'off',374video: process.env.CI ? 'retain-on-failure' : 'off',375},376});377```378379## Anti-Patterns380381| Don't | Problem | Do Instead |382|-------|---------|------------|383| `timeout: 300_000` globally | Masks flaky tests; slow CI | Fix root cause; keep 30s default |384| Hardcoded URLs: `page.goto('http://localhost:4000/login')` | Breaks in other environments | Use `baseURL` + relative paths |385| All browsers on every PR | 3x CI time | Chromium on PRs; all on main |386| `trace: 'on'` always | Huge artifacts, slow uploads | `trace: 'on-first-retry'` |387| `video: 'on'` always | Massive storage; slow tests | `video: 'retain-on-failure'` |388| Config in test files: `test.use({ viewport: {...} })` everywhere | Scattered, inconsistent | Define once in project config |389| `retries: 3` locally | Hides flakiness | `retries: 0` local, `retries: 2` CI |390| No `forbidOnly` in CI | Committed `test.only` runs single test | `forbidOnly: !!process.env.CI` |391| `globalSetup` for browser auth | No browser context available | Use setup project with dependencies |392| Committing `.env` with credentials | Security risk | Commit `.env.example` only |393394## Troubleshooting395396### baseURL Not Working397398**Cause**: Using absolute URL in `page.goto()` ignores `baseURL`.399400```ts401// Wrong - ignores baseURL402await page.goto('http://localhost:4000/dashboard');403404// Correct - uses baseURL405await page.goto('/dashboard');406```407408### webServer Starts But Tests Get Connection Refused409410**Cause**: `webServer.url` doesn't match actual server address or health check returns non-200.411412```ts413webServer: {414command: 'npm run dev',415url: 'http://localhost:4000/api/health', // use real endpoint416reuseExistingServer: !process.env.CI,417timeout: 120_000,418},419```420421### Tests Pass Locally But Timeout in CI422423**Cause**: CI machines are slower. Increase timeouts and reduce workers:424425```ts426export default defineConfig({427workers: process.env.CI ? '50%' : undefined,428use: {429navigationTimeout: process.env.CI ? 30_000 : 15_000,430actionTimeout: process.env.CI ? 15_000 : 10_000,431},432});433```434435### "Target page, context or browser has been closed"436437**Cause**: Test exceeded `timeout` and Playwright tore down browser during action.438439**Fix**: Don't increase global timeout. Find slow step using trace:440441```bash442npx playwright test --trace on443npx playwright show-report444```445446## Related447448- [test-tags.md](./test-tags.md) - tagging and filtering tests with `--grep`449- [fixtures-hooks.md](./fixtures-hooks.md) - custom fixtures for per-test state450- [test-suite-structure.md](test-suite-structure.md) - file structure and naming451- [authentication.md](../advanced/authentication.md) - setup projects for shared auth452- [projects-dependencies.md](./projects-dependencies.md) - advanced multi-project patterns453