QA Visual Regression Writer
Purpose
Write visual regression tests to detect unintended UI changes. Transform test cases and visual checkpoints into executable visual tests using Playwright built-in screenshots, Percy (cloud-based), or BackstopJS (Docker-based).
Trigger Phrases
- "Write visual regression tests for [page/component]"
- "Generate screenshot comparison tests"
- "Create visual tests with Playwright toHaveScreenshot"
- "Add Percy visual tests for [flow]"
- "BackstopJS visual regression for [viewports]"
- "Visual tests for layout drift detection"
- "Screenshot baseline tests for [feature]"
- "Mask dynamic content in visual tests"
- "Visual regression with viewport variations"
Tools
| Tool |
Approach |
Best For |
| Playwright |
Built-in toHaveScreenshot() |
Local baselines, CI, no extra services |
| Percy |
Cloud-based, BrowserStack integration |
Cross-browser, team review, CI integration |
| BackstopJS |
Docker, configurable viewports |
Multi-viewport, config-driven scenarios |
Workflow
- Read test cases — From qa-testcase-from-docs, qa-testcase-from-ui, or manual specs
- Define visual checkpoints — Identify pages/elements to capture
- Set baselines — Initial capture; store reference images
- Generate visual tests — Produce test scripts with assertions
- Configure thresholds — maxDiffPixels, maxDiffPixelRatio, or tool-specific settings
Key Patterns
- Full page screenshots —
expect(page).toHaveScreenshot()
- Element screenshots —
expect(locator).toHaveScreenshot()
- Viewport variations — Desktop, tablet, mobile (1280x720, 768x1024, 375x667)
- Dynamic content masking — Mask timestamps, avatars, ads, animations
- Animation disabling —
page.addStyleTag or prefers-reduced-motion for stable captures
- Threshold configuration — Tune sensitivity to avoid false positives
See references/patterns.md for screenshot strategies, masking, viewports, thresholds.
Baseline Management
| Phase |
Action |
| Initial capture |
Run tests with --update-snapshots (Playwright) or backstop approve |
| Update workflow |
Review diffs → approve intentional changes → commit baselines |
| Review process |
Percy: dashboard review; BackstopJS: HTML report; Playwright: diff artifacts |
See references/best-practices.md for baseline management and CI integration.
Playwright Integration
// Full page
await expect(page).toHaveScreenshot('homepage.png');
// Element
await expect(page.getByTestId('header')).toHaveScreenshot('header.png');
// With options
await expect(page).toHaveScreenshot('dashboard.png', {
mask: [page.locator('.timestamp'), page.locator('.avatar')],
maxDiffPixels: 100,
maxDiffPixelRatio: 0.01,
fullPage: true,
});
- expect(page).toHaveScreenshot(name) — Full page or viewport
- expect(locator).toHaveScreenshot(name) — Element-only
- maxDiffPixels / maxDiffPixelRatio — Tolerance for minor rendering differences
- mask — Hide dynamic regions from comparison
See references/config.md for Playwright screenshot config.
Percy Integration
import percySnapshot from '@percy/playwright';
await percySnapshot(page, 'Homepage');
await percySnapshot(page, 'Dashboard', { widths: [1280, 768, 375] });
- percySnapshot(page, name) — Capture and upload to Percy
- Percy CLI —
npx percy exec -- npx playwright test
- BrowserStack Percy — Cloud review, cross-browser, parallel execution
See references/config.md for Percy setup.
BackstopJS Integration
// backstop.json
{
"scenarios": [
{
"label": "Homepage",
"url": "http://localhost:3000",
"viewports": [{ "width": 1280, "height": 720 }]
}
]
}
- backstop.json — Scenarios, viewports, selectors, delay
- backstop test — Run comparisons
- backstop approve — Update baselines
See references/config.md for BackstopJS configuration.
Output
- Visual test scripts — TypeScript/JavaScript with screenshot assertions
- Baseline images — Stored in
test-results/, backstop_data/, or Percy cloud
- Comparison reports — HTML diff reports, Percy dashboard, Playwright artifacts
Scope
Can do (autonomous):
- Generate visual regression tests from test case specs
- Use Playwright toHaveScreenshot, Percy, or BackstopJS
- Apply masking for dynamic content (timestamps, avatars, ads)
- Configure viewport variations (desktop/tablet/mobile)
- Set thresholds (maxDiffPixels, maxDiffPixelRatio)
- Disable animations for stable captures
- Create backstop.json scenarios
Cannot do (requires confirmation):
- Add Percy/BackstopJS dependencies without approval
- Configure Percy project token (user must provide)
- Override existing baseline images without approval
Will not do (out of scope):
- Execute tests (user runs
npx playwright test, backstop test)
- Modify production UI code
- Set up Percy/BrowserStack accounts
References
references/patterns.md — Screenshot strategies, masking, viewports, thresholds
references/config.md — Playwright, Percy, BackstopJS configuration
references/best-practices.md — Baseline management, CI integration, dynamic content
Quality Checklist
Troubleshooting
| Symptom |
Likely Cause |
Fix |
| Flaky visual diffs |
Animations, timestamps, fonts |
Mask dynamic regions; disable animations |
| Too many false positives |
Threshold too strict |
Increase maxDiffPixels or maxDiffPixelRatio |
| Baseline mismatch |
Different OS/fonts/DPI |
Run baselines in CI; use consistent environment |
| Percy upload fails |
Missing token, network |
Set PERCY_TOKEN; check Percy dashboard |
| BackstopJS no match |
Wrong reference path |
Run backstop approve to create baselines |
| Element screenshot empty |
Element not visible |
Ensure element in viewport; add wait |
1---2name: qa-visual-regression-writer3description: Generate visual regression tests using Playwright screenshots, Percy, and BackstopJS for screenshot comparison, layout drift detection, and baseline management.4---56# QA Visual Regression Writer78## Purpose910Write visual regression tests to detect unintended UI changes. Transform test cases and visual checkpoints into executable visual tests using Playwright built-in screenshots, Percy (cloud-based), or BackstopJS (Docker-based).1112## Trigger Phrases1314- "Write visual regression tests for [page/component]"15- "Generate screenshot comparison tests"16- "Create visual tests with Playwright toHaveScreenshot"17- "Add Percy visual tests for [flow]"18- "BackstopJS visual regression for [viewports]"19- "Visual tests for layout drift detection"20- "Screenshot baseline tests for [feature]"21- "Mask dynamic content in visual tests"22- "Visual regression with viewport variations"2324## Tools2526| Tool | Approach | Best For |27|------|----------|----------|28| **Playwright** | Built-in `toHaveScreenshot()` | Local baselines, CI, no extra services |29| **Percy** | Cloud-based, BrowserStack integration | Cross-browser, team review, CI integration |30| **BackstopJS** | Docker, configurable viewports | Multi-viewport, config-driven scenarios |3132## Workflow33341. **Read test cases** — From qa-testcase-from-docs, qa-testcase-from-ui, or manual specs352. **Define visual checkpoints** — Identify pages/elements to capture363. **Set baselines** — Initial capture; store reference images374. **Generate visual tests** — Produce test scripts with assertions385. **Configure thresholds** — maxDiffPixels, maxDiffPixelRatio, or tool-specific settings3940## Key Patterns4142- **Full page screenshots** — `expect(page).toHaveScreenshot()`43- **Element screenshots** — `expect(locator).toHaveScreenshot()`44- **Viewport variations** — Desktop, tablet, mobile (1280x720, 768x1024, 375x667)45- **Dynamic content masking** — Mask timestamps, avatars, ads, animations46- **Animation disabling** — `page.addStyleTag` or `prefers-reduced-motion` for stable captures47- **Threshold configuration** — Tune sensitivity to avoid false positives4849See `references/patterns.md` for screenshot strategies, masking, viewports, thresholds.5051## Baseline Management5253| Phase | Action |54|-------|--------|55| **Initial capture** | Run tests with `--update-snapshots` (Playwright) or `backstop approve` |56| **Update workflow** | Review diffs → approve intentional changes → commit baselines |57| **Review process** | Percy: dashboard review; BackstopJS: HTML report; Playwright: diff artifacts |5859See `references/best-practices.md` for baseline management and CI integration.6061## Playwright Integration6263```typescript64// Full page65await expect(page).toHaveScreenshot('homepage.png');6667// Element68await expect(page.getByTestId('header')).toHaveScreenshot('header.png');6970// With options71await expect(page).toHaveScreenshot('dashboard.png', {72 mask: [page.locator('.timestamp'), page.locator('.avatar')],73 maxDiffPixels: 100,74 maxDiffPixelRatio: 0.01,75 fullPage: true,76});77```7879- **expect(page).toHaveScreenshot(name)** — Full page or viewport80- **expect(locator).toHaveScreenshot(name)** — Element-only81- **maxDiffPixels / maxDiffPixelRatio** — Tolerance for minor rendering differences82- **mask** — Hide dynamic regions from comparison8384See `references/config.md` for Playwright screenshot config.8586## Percy Integration8788```typescript89import percySnapshot from '@percy/playwright';9091await percySnapshot(page, 'Homepage');92await percySnapshot(page, 'Dashboard', { widths: [1280, 768, 375] });93```9495- **percySnapshot(page, name)** — Capture and upload to Percy96- **Percy CLI** — `npx percy exec -- npx playwright test`97- **BrowserStack Percy** — Cloud review, cross-browser, parallel execution9899See `references/config.md` for Percy setup.100101## BackstopJS Integration102103```javascript104// backstop.json105{106 "scenarios": [107 {108 "label": "Homepage",109 "url": "http://localhost:3000",110 "viewports": [{ "width": 1280, "height": 720 }]111 }112 ]113}114```115116- **backstop.json** — Scenarios, viewports, selectors, delay117- **backstop test** — Run comparisons118- **backstop approve** — Update baselines119120See `references/config.md` for BackstopJS configuration.121122## Output123124- **Visual test scripts** — TypeScript/JavaScript with screenshot assertions125- **Baseline images** — Stored in `test-results/`, `backstop_data/`, or Percy cloud126- **Comparison reports** — HTML diff reports, Percy dashboard, Playwright artifacts127128## Scope129130**Can do (autonomous):**131- Generate visual regression tests from test case specs132- Use Playwright toHaveScreenshot, Percy, or BackstopJS133- Apply masking for dynamic content (timestamps, avatars, ads)134- Configure viewport variations (desktop/tablet/mobile)135- Set thresholds (maxDiffPixels, maxDiffPixelRatio)136- Disable animations for stable captures137- Create backstop.json scenarios138139**Cannot do (requires confirmation):**140- Add Percy/BackstopJS dependencies without approval141- Configure Percy project token (user must provide)142- Override existing baseline images without approval143144**Will not do (out of scope):**145- Execute tests (user runs `npx playwright test`, `backstop test`)146- Modify production UI code147- Set up Percy/BrowserStack accounts148149## References150151- `references/patterns.md` — Screenshot strategies, masking, viewports, thresholds152- `references/config.md` — Playwright, Percy, BackstopJS configuration153- `references/best-practices.md` — Baseline management, CI integration, dynamic content154155## Quality Checklist156157- [ ] Dynamic content masked (timestamps, avatars, ads)158- [ ] Animations disabled or reduced for stable captures159- [ ] Viewport variations defined where needed160- [ ] Thresholds configured to avoid false positives161- [ ] Baselines stored in version control (Playwright/BackstopJS)162- [ ] Traceability to test case IDs where applicable163- [ ] No hardcoded secrets (Percy token via env)164165## Troubleshooting166167| Symptom | Likely Cause | Fix |168|---------|--------------|-----|169| Flaky visual diffs | Animations, timestamps, fonts | Mask dynamic regions; disable animations |170| Too many false positives | Threshold too strict | Increase maxDiffPixels or maxDiffPixelRatio |171| Baseline mismatch | Different OS/fonts/DPI | Run baselines in CI; use consistent environment |172| Percy upload fails | Missing token, network | Set PERCY_TOKEN; check Percy dashboard |173| BackstopJS no match | Wrong reference path | Run `backstop approve` to create baselines |174| Element screenshot empty | Element not visible | Ensure element in viewport; add wait |