From playwright-visual
Patterns for `toHaveScreenshot` and `toMatchSnapshot` assertions: full-page vs element captures, all supported options, masking dynamic regions, and DO / DON'T examples.
How this skill is triggered — by the user, by Claude, or both
Slash command
/playwright-visual:visual-assertionsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Patterns for `toHaveScreenshot` and `toMatchSnapshot` assertions: full-page vs element captures, all supported options, masking dynamic regions, and DO / DON'T examples.
Patterns for toHaveScreenshot and toMatchSnapshot assertions: full-page vs element captures, all supported options, masking dynamic regions, and DO / DON'T examples.
toHaveScreenshot call signatures with all supported options.toMatchSnapshot usage for non-image artifacts (JSON fixtures, text snapshots).mask: [page.locator()].toHaveScreenshot — Full Option Referenceawait expect(target).toHaveScreenshot(name?, options?);
| Option | Type | Default | Purpose |
|---|---|---|---|
animations | 'disabled' | 'allow' | 'disabled' | Disable CSS/JS animations before capture |
clip | { x, y, width, height } | — | Capture a sub-region of the element or page |
fullPage | boolean | false | Capture the full scrollable page |
mask | Locator[] | [] | Render these regions as magenta rectangles |
maskColor | string | '#FF00FF' | Override mask fill color |
maxDiffPixels | number | — | Absolute pixel count tolerance |
maxDiffPixelRatio | number | — | Ratio of differing pixels (0–1) |
threshold | number | 0.2 | Per-pixel color distance tolerance (0–1) |
timeout | number | config default | Max ms to wait for the assertion to pass |
test('marketing page — full scroll', async ({ page }) => {
await page.goto('/marketing');
await page.waitForLoadState('networkidle');
await expect(page).toHaveScreenshot('marketing-full.png', {
fullPage: true,
animations: 'disabled',
maxDiffPixelRatio: 0.05,
});
});
test('subscription card component', async ({ page }) => {
await page.goto('/pricing');
const card = page.getByTestId('pricing-card-pro');
await card.waitFor({ state: 'visible' });
await expect(card).toHaveScreenshot('pricing-card-pro.png', {
animations: 'disabled',
maxDiffPixelRatio: 0.02,
});
});
test('navigation bar', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('navbar.png', {
clip: { x: 0, y: 0, width: 1280, height: 64 },
animations: 'disabled',
maxDiffPixelRatio: 0.01,
});
});
test('user profile page', async ({ page }) => {
await page.goto('/profile/123');
await expect(page).toHaveScreenshot('profile.png', {
fullPage: true,
animations: 'disabled',
maxDiffPixelRatio: 0.03,
mask: [
page.getByTestId('avatar-image'), // user-uploaded photo
page.getByTestId('joined-date'), // relative timestamp
page.locator('[data-dynamic-count]'), // activity counter
],
});
});
toMatchSnapshot for Non-Image Artifactstest('API response shape', async ({ request }) => {
const response = await request.get('/api/v1/config');
const body = await response.json();
// Snapshot the JSON shape; useful for contract-style tests
expect(JSON.stringify(body, null, 2)).toMatchSnapshot('api-config.json');
});
// Good: animations disabled globally via config AND locally for belt-and-suspenders
await expect(page.getByTestId('hero')).toHaveScreenshot('hero.png', {
animations: 'disabled',
});
// Bad: no animation control; capture may catch mid-animation frames
await expect(page.getByTestId('hero')).toHaveScreenshot('hero.png');
// Good: timestamp is masked; diff is deterministic
await expect(page).toHaveScreenshot('dashboard.png', {
animations: 'disabled',
mask: [page.getByTestId('last-sync-time')],
});
// Bad: threshold inflated to absorb a changing timestamp — masks real regressions
await expect(page).toHaveScreenshot('dashboard.png', {
animations: 'disabled',
maxDiffPixelRatio: 0.4, // hiding a timestamp diff is not the same as tolerating sub-pixel variance
});
// Good: captures only the component; unrelated layout changes don't affect this test
await expect(page.getByTestId('alert-banner')).toHaveScreenshot('alert-banner.png', {
animations: 'disabled',
maxDiffPixelRatio: 0.02,
});
// Bad: any layout shift anywhere on the page breaks this component test
await expect(page).toHaveScreenshot('alert-banner.png', {
fullPage: true,
});
animations: 'disabled' on every toHaveScreenshot call, even if configured globally — it is cheap and explicit.mask for every region whose content is non-deterministic; do not tune thresholds as a substitute.toHaveScreenshot for component-scoped tests; use fullPage: true only for layout tests.toMatchSnapshot for non-image artifacts (JSON, text) where pixel comparison is not appropriate.Guides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
npx claudepluginhub gagandeepp/software-agent-teams --plugin playwright-visual