Config Walkthrough
When to Use
Use this as a reference when configuring
playwright.config.tsfor a VR project.
Pattern
Annotated config
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: 'e2e', // where tests live
fullyParallel: true, // all tests parallel by default
forbidOnly: !!process.env.CI, // CI fails if `test.only` slipped in
retries: process.env.CI ? 2 : 0, // 2 retries in CI; 0 locally
workers: process.env.CI ? 1 : 2, // cap: one web container can't serve 50%-of-cores
reporter: 'html', // required for VR diff viewer
outputDir: 'test-results', // diff PNGs land here on failure
use: {
baseURL: 'https://mysite.ddev.site',
ignoreHTTPSErrors: true, // for DDEV self-signed
trace: 'on-first-retry', // record trace on retry
colorScheme: 'light', // pin to avoid OS dark mode
locale: 'en-US',
timezoneId: 'UTC',
},
expect: {
timeout: 5000, // expect() web-first wait
toHaveScreenshot: {
threshold: 0.15, // per-pixel YIQ tolerance
stylePath: './screenshot.css',
animations: 'disabled',
caret: 'hide',
// no maxDiffPixels / maxDiffPixelRatio — unset already means a zero budget
},
},
snapshotPathTemplate: '{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}',
projects: [
{ name: 'chromium-1440',
use: { ...devices['Desktop Chrome'], viewport: { width: 1440, height: 900 } } },
{ name: 'chromium-768',
use: { ...devices['Desktop Chrome'], viewport: { width: 768, height: 1024 } } },
{ name: 'chromium-375',
use: { ...devices['Desktop Chrome'], viewport: { width: 375, height: 667 } } },
],
});
Key Reference (VR-relevant)
| Key | Purpose |
|---|---|
testDir |
Where tests live; relative to config file |
fullyParallel |
Parallelize all tests across files |
forbidOnly |
Fail CI if test.only is committed |
retries |
Per-test retry count |
workers |
Parallel worker count; defaults to "50%" of logical CPU cores |
outputDir |
Where actual/diff PNGs land (default test-results/) |
reporter |
'html' is required for the visual diff viewer |
use.baseURL |
Default URL for page.goto('/') |
use.ignoreHTTPSErrors |
Trust self-signed TLS (DDEV) |
use.colorScheme / locale / timezoneId |
Pin for reproducibility |
expect.timeout |
expect() web-first wait time |
expect.toHaveScreenshot |
Defaults for every screenshot assertion |
snapshotPathTemplate |
Where baselines are stored |
updateSnapshots |
Default 'missing'; usually overridden via CLI |
ignoreSnapshots |
Skip snapshot assertions entirely (e.g. dev-only) |
projects[] |
Browser × viewport matrix |
webServer |
Auto-start a dev server before tests; rarely used for Drupal |
Common Mistakes
- Wrong:
reporter: 'list'without'html'→ Right: no visual diff viewer; triage becomes painful - Wrong:
workersunset in CI → Right: random parallelism; tests touching shared Drupal state collide - Wrong:
workersleft at the default locally → Right: the default is 50% of your logical cores, and a single-container Drupal backend cannot serve that many; captures start failing in batches that read as flake - Wrong: Missing
ignoreHTTPSErrors→ Right: DDEV TLS errors fail every navigation