From atum-stack-web
Visual regression testing pattern library for modern web apps — Percy (BrowserStack, Git-integrated, cross-browser), Chromatic (Storybook-native, built by Storybook maintainers), Applitools Eyes (AI-powered visual diff with Ultrafast Grid), BackstopJS (open-source Puppeteer-based local runner), Playwright built-in visual comparisons (toHaveScreenshot / toMatchSnapshot), Cypress visual testing via plugins. Covers baseline management, visual diff algorithms (pixel-perfect vs perceptual), dynamic content handling (animations, timestamps, random IDs), viewport matrices, browser matrices, CI integration (approve/reject diffs in PR), Storybook component snapshot workflows, and strategies for avoiding flaky tests (fixed dates, font loading, disable animations). Use when adding visual regression to a new project, choosing between tools, setting up Storybook with Chromatic, handling dynamic content, or debugging flaky visual tests. Differentiates from e2e-testing by focusing exclusively on pixel-level rendering correctness, not functional behavior.
npx claudepluginhub arnwaldn/atum-plugins-collection --plugin atum-stack-webThis skill uses the workspace's default tool permissions.
Le visual regression capture des screenshots et les compare à un baseline pour détecter les changements UI **non intentionnels**. Complémente (sans remplacer) les tests unitaires et E2E.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides implementation of event-driven hooks in Claude Code plugins using prompt-based validation and bash commands for PreToolUse, Stop, and session events.
Le visual regression capture des screenshots et les compare à un baseline pour détecter les changements UI non intentionnels. Complémente (sans remplacer) les tests unitaires et E2E.
Projet avec Storybook ?
├── OUI → Chromatic (intégration native Storybook, cloud managed)
└── NON →
├── Budget + cross-browser critique → Percy ou Applitools
├── Budget zéro + local only → BackstopJS
└── Already using Playwright → Playwright visual comparisons built-in
Besoin de tests AI-powered (smart diff) ?
└── Applitools Eyes (plus cher mais détecte les changements "meaningful")
npm install -D chromatic
# CI run
npx chromatic --project-token=xxx --exit-zero-on-changes
// .storybook/main.ts
export default {
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: ['@storybook/addon-essentials'],
framework: { name: '@storybook/react-vite' },
}
Chromatic capture un snapshot de chaque story × chaque viewport × chaque browser configuré. Les diffs sont review-able dans une UI dédiée, avec approve/reject qui met à jour le baseline.
// tests/visual.spec.ts
import { test, expect } from '@playwright/test'
test('homepage visual', async ({ page }) => {
await page.goto('https://example.com')
await expect(page).toHaveScreenshot('homepage.png', {
maxDiffPixelRatio: 0.01,
fullPage: true,
animations: 'disabled',
})
})
test('hero component', async ({ page }) => {
await page.goto('https://example.com')
const hero = page.locator('.hero')
await expect(hero).toHaveScreenshot('hero.png')
})
// playwright.config.ts
export default defineConfig({
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
expect: {
toHaveScreenshot: {
maxDiffPixelRatio: 0.01,
threshold: 0.2,
animations: 'disabled',
},
},
})
Première run → crée les baselines. Runs suivants → compare.
Update baseline : npx playwright test --update-snapshots.
npm install -D backstopjs
npx backstop init
// backstop.json
{
"id": "my_project",
"viewports": [
{ "label": "phone", "width": 375, "height": 667 },
{ "label": "tablet", "width": 768, "height": 1024 },
{ "label": "desktop", "width": 1440, "height": 900 }
],
"scenarios": [
{
"label": "homepage",
"url": "https://example.com",
"selectors": ["document"],
"delay": 500,
"misMatchThreshold": 0.1
},
{
"label": "product page",
"url": "https://example.com/products/p1",
"selectors": [".product-hero", ".product-description"],
"hideSelectors": [".advertisement", ".cookie-banner"]
}
],
"engine": "puppeteer",
"report": ["browser"]
}
npx backstop reference # créer les baselines
npx backstop test # comparer
npx backstop approve # accepter les changements
Problème : dates, animations, random IDs, user avatars → causent des faux positifs.
// Playwright
await expect(page).toHaveScreenshot({
mask: [page.locator('.timestamp'), page.locator('.user-avatar')],
})
// Chromatic — via CSS `visibility: hidden` dans une story dédiée
// Playwright
test.beforeEach(async ({ page }) => {
// Freeze Date.now()
await page.addInitScript(() => {
const fixedDate = new Date('2026-01-01T00:00:00Z')
Date.now = () => fixedDate.getTime()
})
// Disable CSS animations
await page.addStyleTag({
content: `
*, *::before, *::after {
animation-duration: 0s !important;
transition-duration: 0s !important;
}
`,
})
})
await page.evaluate(() => document.fonts.ready)
name: Visual regression
on: [pull_request]
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
- run: npm ci
- run: npm run build-storybook
- uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
exitOnceUploaded: true
Chromatic commente la PR avec un lien vers les diffs review.
main — chaque merge met à jour la baselinemainnpx playwright test --update-snapshots + commitLes rendus varient entre macOS / Linux / Windows → toujours générer les baselines dans le même environnement que la CI (typiquement Linux).
# En local sur macOS
docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/playwright:v1.41.0 \
npx playwright test --update-snapshots
Un bon setup capture 3-4 viewports :
Au-delà de 4, le coût CI explose.
Chromatic et Percy supportent Chrome + Firefox + Safari + Edge en parallèle. Playwright built-in aussi.
Règle : couvrir au minimum Chrome (le standard) + Safari (iOS users) + Firefox (rendering different engine).
isMobile: true → pas de touch events, pas de viewport metapage.addInitScripte2e-testingfrontend-designaccessibility-auditorfrontend-patterns