| Install Playwright in a Node or Drupal project |
Setup |
Use npm init playwright@latest for new Node projects; isolate under tests/playwright/ for Drupal repos. After any package upgrade, always re-run npx playwright install --with-deps or browser binaries will be stale and fail to launch. |
| Choose between screenshot APIs |
Screenshot APIs |
Use expect(page).toHaveScreenshot() for all VR assertions — it auto-retries for stability and diffs against a baseline. page.screenshot() captures only; toMatchSnapshot() diffs but skips the stability retry. |
| Configure browser projects (Chromium / Firefox / WebKit) |
Browser Projects |
Define one project per browser in playwright.config.ts using devices presets; the project name flows into baseline filenames. Start with Chromium-only for VR — adding browsers multiplies baselines 2-3x and should only be done when your suite is stable. |
| Capture at multiple viewport sizes |
Viewport & Device Matrix |
Use one project per (browser × viewport) combination — the project name flows into the baseline filename, giving each tuple its own image. Never call page.setViewportSize() mid-test; it causes baseline filename collisions. |
| Reference all options for toHaveScreenshot() |
Screenshot Options |
Set animations: 'disabled', caret: 'hide', threshold: 0.15, and maxDiffPixelRatio: 0.005 globally in expect.toHaveScreenshot. Never override animations: 'allow' — it reintroduces flake. |
| Understand baseline file naming and storage |
Baseline Files |
Baselines follow <test-name>-<ordinal>-<projectName>-<platform>.png stored in <spec>.ts-snapshots/ next to the spec file. Commit *-snapshots/; gitignore test-results/. |
| Make captures deterministic (animations, fonts, masks) |
Stability Controls |
Add document.fonts.ready, animations: 'disabled', and a stylePath injecting screenshot.css to eliminate font swap, CSS animation frames, and dynamic content regions. |
| Ensure local and CI produce the same screenshots |
Determinism |
Always capture baselines inside the same Docker image that runs in CI. Pin @playwright/test and the Docker tag to the same version number. Never capture on macOS and compare in Linux. |
| Organize tests with fixtures and parallelism |
Test Organization |
Use test.extend fixtures for shared auth and page state instead of globals or beforeEach chains. Set fullyParallel: true globally but apply mode: 'serial' only to tests that share Drupal editorial state. |
| Configure every key in playwright.config.ts |
Config Walkthrough |
Set reporter: 'html', outputDir: 'test-results', forbidOnly: !!process.env.CI, and pin colorScheme, locale, timezoneId in use. Define the browser × viewport matrix under projects. |
| Run the right CLI command |
CLI Cheatsheet |
Use npx playwright test --project=<name> to scope runs, npx playwright test -u to update only changed baselines, and npx playwright show-report to open the HTML diff viewer. |
| Identify and fix common anti-patterns |
Anti-Patterns |
The two most damaging mistakes are capturing baselines on macOS and comparing in Linux CI, and committing baselines from a flaky state. Pin a Docker image, add stability controls first, then capture. |
| Use Playwright outside the test runner |
Programmatic API |
Use the playwright package (not @playwright/test) for standalone scripts. toHaveScreenshot() only works inside the test runner — for diffing in custom scripts, call pixelmatch directly. |
| Run Playwright against Drupal/DDEV |
Drupal & DDEV |
Set baseURL to DDEV_PRIMARY_URL, ignoreHTTPSErrors: true, and run via ddev exec npx playwright test. Use storage-state auth with a setup project; gitignore .auth/. |