From vizzly
Provides philosophy and best practices for visual testing: when/where to add screenshots in functional tests, naming conventions, stability tips with Playwright.
npx claudepluginhub vizzly-testing/cli --plugin vizzlyThis skill uses the workspace's default tool permissions.
When helping users add or improve visual tests, follow these principles and best practices.
Expert approach to visual-regression-testing in test automation. Use when working with .
Implements visual regression testing with screenshot comparison, diff detection, and baseline management for UI components and pages to catch CSS regressions and layout shifts.
Sets up visual regression testing for UI: generates capture scripts, comparison scripts, and baseline images to diff dev builds against prototype screenshots during feature development.
Share bugs, ideas, or general feedback.
When helping users add or improve visual tests, follow these principles and best practices.
Visual tests capture the REAL thing.
Unlike component libraries that render in isolation, Vizzly captures screenshots from your actual functional tests - the same tests that verify your app works. This means:
High-value screenshot opportunities:
goto() or routing changesLower value (consider skipping):
Good names are descriptive, consistent, and sortable.
Pattern: <feature>-<state>-<variant>
Good examples:
homepage-desktop
homepage-mobile
login-form-empty
login-form-filled
login-form-error
checkout-cart-with-items
checkout-cart-empty
user-profile-settings-tab
Avoid:
test1 # Not descriptive
page # Too generic
myComponent # CamelCase inconsistent
home_page_after_login # Underscores (use hyphens)
Include viewport when testing responsive:
homepage-1920x1080
homepage-768x1024
homepage-375x667
Capture screenshots after the page is fully loaded:
// Wait for network idle
await page.waitForLoadState('networkidle');
// Or wait for specific element
await page.waitForSelector('.main-content');
// Then screenshot
await vizzlyScreenshot('homepage', await page.screenshot());
Mock or stabilize dynamic data:
// Mock dates
await page.evaluate(() => {
Date.now = () => new Date('2024-01-01').getTime();
});
// Hide dynamic elements
await page.evaluate(() => {
document.querySelector('.timestamp')?.remove();
});
Prevent animation-related flakiness:
await page.addStyleTag({
content: `
*, *::before, *::after {
animation-duration: 0s !important;
transition-duration: 0s !important;
}
`
});
Configure thresholds based on your needs:
// In vizzly.config.js
export default defineConfig({
threshold: 0.1, // 0.1% - strict, catches minor changes
// threshold: 1.0, // 1% - lenient, ignores anti-aliasing
// threshold: 0, // 0% - pixel-perfect (may be flaky)
});
Threshold guidelines:
0.05% - 0.1% - Most projects, catches real changes, ignores rendering noise0.5% - 1% - If you have font rendering issues across environments2%+ - Only for very dynamic UIs or when stability is a problemA good visual test:
A bad visual test:
When adding visual tests to an existing test suite:
Example integration:
test('user can log in', async ({ page }) => {
await page.goto('/login');
await vizzlyScreenshot('login-form-empty', await page.screenshot());
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'password');
await vizzlyScreenshot('login-form-filled', await page.screenshot());
await page.click('button[type="submit"]');
await page.waitForURL('/dashboard');
await vizzlyScreenshot('dashboard-after-login', await page.screenshot());
});
The visual TDD workflow:
vizzly tdd start--watchhttp://localhost:47392Flaky tests:
Too many screenshots:
Brittle baselines:
Ignoring failures: