playwright-visual-regression
Playwright browser automation patterns for E2E testing, visual regression, screenshot capture, and accessibility validation. Use when implementing or debugging browser-based tests.
Best use case
playwright-visual-regression is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Playwright browser automation patterns for E2E testing, visual regression, screenshot capture, and accessibility validation. Use when implementing or debugging browser-based tests.
Teams using playwright-visual-regression should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/playwright-visual-regression/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How playwright-visual-regression Compares
| Feature / Agent | playwright-visual-regression | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Playwright browser automation patterns for E2E testing, visual regression, screenshot capture, and accessibility validation. Use when implementing or debugging browser-based tests.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Playwright Visual Regression Testing
This skill documents patterns for E2E testing with Playwright, including visual regression, cross-browser testing, and accessibility automation for OSCAR Export Analyzer.
## Setup and Configuration
### Installation
```bash
npm install -D @playwright/test
npx playwright install # Install browsers
```
### Configuration (`playwright.config.js`)
```javascript
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:5173',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
},
});
```
## Basic E2E Test
```javascript
import { test, expect } from '@playwright/test';
test.describe('CSV Upload Flow', () => {
test('uploads CSV and displays charts', async ({ page }) => {
await page.goto('/');
// Upload CSV file
const fileInput = page.locator('input[type="file"]');
await fileInput.setInputFiles('tests/fixtures/sample-cpap-data.csv');
// Wait for parsing to complete
await expect(page.locator('text=Parsing complete')).toBeVisible();
// Verify charts are rendered
await expect(page.locator('[data-testid="usage-chart"]')).toBeVisible();
await expect(page.locator('[data-testid="ahi-chart"]')).toBeVisible();
});
});
```
## Visual Regression Testing
### Capture Visual Baseline
```javascript
import { test, expect } from '@playwright/test';
test.describe('Visual Regression', () => {
test('chart layout matches baseline', async ({ page }) => {
// Load test data
await page.goto('/');
await page
.locator('input[type="file"]')
.setInputFiles('tests/fixtures/test-data.csv');
await page.waitForLoadState('networkidle');
// Take screenshot and compare with baseline
await expect(page).toHaveScreenshot('usage-chart.png', {
maxDiffPixels: 100, // Allow small differences
});
});
test('dark mode appearance', async ({ page }) => {
await page.goto('/');
// Enable dark mode
await page.locator('[aria-label="Toggle dark mode"]').click();
// Wait for theme transition
await page.waitForTimeout(300);
// Compare with dark mode baseline
await expect(page).toHaveScreenshot('dark-mode.png');
});
});
```
### Update Baselines
```bash
# Update all baselines
npx playwright test --update-snapshots
# Update specific test baselines
npx playwright test visual-regression --update-snapshots
```
### Baseline Management
- **Store baselines in git**: `tests/e2e/*.spec.js-snapshots/`
- **Review visual diffs**: Check Playwright HTML report
- **Approve changes**: Commit updated baselines after verifying correctness
## Responsive Testing
```javascript
test.describe('Responsive Design', () => {
test('mobile layout', async ({ page }) => {
// Set mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/');
// Verify mobile layout
await expect(page.locator('[data-testid="mobile-menu"]')).toBeVisible();
await expect(page).toHaveScreenshot('mobile-layout.png');
});
test('tablet layout', async ({ page }) => {
await page.setViewportSize({ width: 768, height: 1024 });
await page.goto('/');
await expect(page).toHaveScreenshot('tablet-layout.png');
});
test('desktop layout', async ({ page }) => {
await page.setViewportSize({ width: 1920, height: 1080 });
await page.goto('/');
await expect(page).toHaveScreenshot('desktop-layout.png');
});
});
```
## Chart Interaction Testing
```javascript
test.describe('Chart Interactions', () => {
test('zoom chart with mouse wheel', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
const chart = page.locator('[data-testid="ahi-chart"]');
// Get initial axis range
const initialRange = await page.evaluate(() => {
const plotly = window.Plotly;
const chartDiv = document.querySelector('[data-testid="ahi-chart"]');
return chartDiv.layout.xaxis.range;
});
// Zoom in with mouse wheel
await chart.hover();
await page.mouse.wheel(0, -100);
// Wait for zoom animation
await page.waitForTimeout(500);
// Verify axis range changed
const newRange = await page.evaluate(() => {
const chartDiv = document.querySelector('[data-testid="ahi-chart"]');
return chartDiv.layout.xaxis.range;
});
expect(newRange).not.toEqual(initialRange);
});
test('toggle legend series', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
// Click legend item to hide series
await page.locator('.legend .traces:has-text("AHI")').click();
// Verify series hidden
const isVisible = await page.evaluate(() => {
const trace = document.querySelector('.trace.scatter');
return trace.style.opacity === '0.5';
});
expect(isVisible).toBe(true);
});
});
```
## Accessibility Testing
### Built-in Accessibility Checks
```javascript
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility', () => {
test('page has no accessibility violations', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
// Run axe accessibility scan
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
});
```
### Keyboard Navigation
```javascript
test('keyboard navigation works', async ({ page }) => {
await page.goto('/');
// Tab to file input
await page.keyboard.press('Tab');
await expect(page.locator('input[type="file"]')).toBeFocused();
// Tab to upload button
await page.keyboard.press('Tab');
await expect(page.locator('button:has-text("Upload")')).toBeFocused();
// Activate with Enter
await page.keyboard.press('Enter');
});
```
### Screen Reader Testing
```javascript
test('has proper ARIA labels', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
// Check chart has accessible name
const chartLabel = await page
.locator('[data-testid="ahi-chart"]')
.getAttribute('aria-label');
expect(chartLabel).toContain('AHI Trends');
// Check form labels
await expect(page.locator('label[for="start-date"]')).toHaveText(
'Start Date',
);
});
```
## Print Layout Testing
```javascript
test.describe('Print Layout', () => {
test('print stylesheet applied', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
// Emulate print media
await page.emulateMedia({ media: 'print' });
// Verify print-specific styles
await expect(page).toHaveScreenshot('print-layout.png');
// Check elements hidden in print
const navVisible = await page.locator('nav').isVisible();
expect(navVisible).toBe(false);
});
test('PDF export quality', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
// Trigger print dialog
await page.locator('button:has-text("Export PDF")').click();
// Generate PDF
const pdf = await page.pdf({
format: 'A4',
printBackground: true,
});
// Verify PDF generated
expect(pdf.length).toBeGreaterThan(0);
});
});
```
## Cross-Browser Testing
```javascript
// Run same test across browsers
test.describe('Cross-Browser Compatibility', () => {
test('works in all browsers', async ({ page, browserName }) => {
test.skip(browserName === 'webkit', 'Known webkit issue #123');
await page.goto('/');
await loadTestData(page);
// Verify core functionality works
await expect(page.locator('[data-testid="usage-chart"]')).toBeVisible();
});
});
```
## Large File Stress Testing
```javascript
test.describe('Performance', () => {
test('handles large CSV upload', async ({ page }) => {
// Increase timeout for large file
test.setTimeout(60000);
await page.goto('/');
// Upload 30MB CSV
await page
.locator('input[type="file"]')
.setInputFiles('tests/fixtures/large-cpap-data.csv');
// Wait for parsing (with progress updates)
await expect(page.locator('text=Parsing')).toBeVisible();
await expect(page.locator('text=Parsing complete')).toBeVisible({
timeout: 30000,
});
// Verify app still responsive
await page.locator('button:has-text("Filter")').click();
await expect(page.locator('[data-testid="date-filter"]')).toBeVisible();
});
test('memory usage stays reasonable', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
// Measure memory usage
const metrics = await page.evaluate(() => {
return (performance as any).memory
? {
usedJSHeapSize: (performance as any).memory.usedJSHeapSize,
totalJSHeapSize: (performance as any).memory.totalJSHeapSize,
}
: null;
});
if (metrics) {
// Ensure memory usage under 200MB
expect(metrics.usedJSHeapSize).toBeLessThan(200 * 1024 * 1024);
}
});
});
```
## Fixture Management
```javascript
// tests/e2e/fixtures/index.ts
import { test as base } from '@playwright/test';
type Fixtures = {
loadTestData: () => Promise<void>;
};
export const test = base.extend<Fixtures>({
loadTestData: async ({ page }, use) => {
const loader = async () => {
await page.goto('/');
await page
.locator('input[type="file"]')
.setInputFiles('tests/fixtures/sample-cpap-data.csv');
await page.waitForLoadState('networkidle');
};
await use(loader);
},
});
```
**Usage:**
```javascript
import { test, expect } from './fixtures';
test('uses fixture', async ({ page, loadTestData }) => {
await loadTestData();
// Test with data already loaded
await expect(page.locator('[data-testid="usage-chart"]')).toBeVisible();
});
```
## Resilient Selectors
### Good Selectors (Stable)
```javascript
// ✅ Data test IDs (most stable)
page.locator('[data-testid="usage-chart"]');
// ✅ Accessible roles and names
page.getByRole('button', { name: 'Upload' });
page.getByRole('textbox', { name: 'Start Date' });
// ✅ Text content (user-facing)
page.locator('text=Usage Patterns');
```
### Bad Selectors (Brittle)
```javascript
// ❌ CSS classes (change frequently)
page.locator('.chart-container-wrapper-inner');
// ❌ Deeply nested selectors
page.locator('div > div > div:nth-child(3) > span.label');
// ❌ Positional selectors
page.locator('.chart').nth(2);
```
## Documentation Automation
### Screenshot Generation for README
```javascript
test.describe('Documentation Screenshots', () => {
test('generate README screenshots', async ({ page }) => {
await page.goto('/');
await loadTestData(page);
// Full page screenshot for README hero
await page.screenshot({
path: 'screenshots/app-overview.png',
fullPage: true,
});
// Specific chart screenshots
await page.locator('[data-testid="usage-chart"]').screenshot({
path: 'screenshots/usage-chart.png',
});
await page.locator('[data-testid="ahi-chart"]').screenshot({
path: 'screenshots/ahi-chart.png',
});
// Dark mode screenshot
await page.locator('[aria-label="Toggle dark mode"]').click();
await page.waitForTimeout(300);
await page.screenshot({
path: 'screenshots/dark-mode.png',
});
});
});
```
## CI Integration
### GitHub Actions
```yaml
# .github/workflows/playwright.yml
name: Playwright Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
```
## Debugging Tests
### Interactive Mode
```bash
# Open Playwright Inspector
npx playwright test --debug
# Run in headed mode (see browser)
npx playwright test --headed
# Run specific test
npx playwright test tests/e2e/upload.spec.js --headed
```
### Trace Viewer
```bash
# Generate trace on failure (configured in playwright.config.js)
# View trace after test failure:
npx playwright show-trace trace.zip
```
### Pause Execution
```javascript
test('debug test', async ({ page }) => {
await page.goto('/');
// Pause execution, open inspector
await page.pause();
// Continue manually in inspector
});
```
## Resources
- **Playwright docs**: https://playwright.dev/
- **Axe accessibility**: https://github.com/dequelabs/axe-core-npm
- **Test fixtures**: `tests/e2e/fixtures/`
- **Config**: `playwright.config.js`
- **CI workflow**: `.github/workflows/playwright.yml`Related Skills
qa-testing-playwright
End-to-end web application testing with Playwright: scope control, stable selectors, parallelization/sharding, flake control, network mocking vs real services, visual testing tradeoffs, and CI/CD integration.
playwright
Playwright E2E testing, page objects, fixtures, visual regression, accessibility testing, and CI integration patterns.
playwright-test
Generate robust, zero-flakiness Playwright E2E tests following OpenMetadata patterns. Creates comprehensive test files with proper waits, API validation, multi-role permissions, and complete entity lifecycle management.
playwright-reviewing
Review Playwright E2E tests for best practices violations. Detects mocked app data, explicit timeouts, CSS selectors, skipped tests, and assertion anti-patterns. Use when reviewing Playwright PRs or auditing test quality.
Playwright E2E Testing
Comprehensive Playwright end-to-end testing patterns with Page Object Model, fixtures, and best practices
playwright-e2e-tester
Expert in end-to-end testing with Playwright, the modern cross-browser testing framework. Specializes in test generation, page object patterns, visual regression testing, and CI/CD integration. Handles complex testing scenarios including authentication flows, API mocking, and mobile emulation.
playwright-best-practices
Review code for Playwright Guidelines compliance. Use when asked to "review my playwright tests", "check accessibility", or "check my tests against best practices".
divek-bi-visual-audit
Visual compliance auditing for DiveK brand identity. Use when reviewing UI screens, component libraries, landing pages, design handoff specs, CSS tokens, or visual QA reports for alignment with DiveK color palette, typography, and cinematic-minimal style direction.
azure-resource-manager-playwright-dotnet
Azure Resource Manager SDK for Microsoft Playwright Testing in .NET.
u0577-observability-regression-sentinel
Operate the "Observability Regression Sentinel" capability in production for workflows. Use when mission execution explicitly requires this capability and outcomes must be reproducible, policy-gated, and handoff-ready.
Analyze Regressions
Grade component health based on regression triage metrics for OpenShift releases
playwright-expert
Use when writing E2E tests with Playwright, setting up test infrastructure, or debugging flaky browser tests. Invoke for browser automation, E2E tests, Page Object Model, test flakiness, visual testing, CI/CD pipeline optimization.