원클릭으로
angular-visual-regression
Capture baseline screenshots of all app pages and pixel-diff on subsequent runs to detect unintended UI changes
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Capture baseline screenshots of all app pages and pixel-diff on subsequent runs to detect unintended UI changes
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Reusable MCP Playwright code snippets for testing this Angular + DaisyUI + Supabase app — auth helpers, form filling, toast checking, table extraction
Simulate full user journeys (signup -> login -> dashboard -> purchase) via MCP Playwright with screenshots and timing at each step
Guide for exposing services via ASD tunnels — asd expose, asd.yaml network config, with/without Caddy, auth rules
| name | angular-visual-regression |
| description | Capture baseline screenshots of all app pages and pixel-diff on subsequent runs to detect unintended UI changes |
| user_invocable | true |
| arg_description | Action: baseline | diff | single <page> (default: baseline) |
Capture baseline screenshots of every page in the ASD Angular Supabase app, then diff against baselines on subsequent runs to catch unintended visual changes.
http://localhost:4200
This skill file must be kept up to date by Claude. When you encounter selectors or page structures that no longer match, or new pages are added to the app, update this SKILL.md immediately. Outdated skills waste time — fix them as you find them.
~/tmp/visual-regression/asd-angular-supabase/
baseline/
home.png
pricing.png
auth-login.png
auth-signup.png
dashboard.png
dashboard-orders.png
dashboard-orders-empty.png
dashboard-settings.png
payment-callback.png
current/
(same structure, captured on diff runs)
diffs/
(pixel-diff images, only for pages with changes)
| Page | URL | Render Mode | Auth Required | Key Elements to Wait For |
|---|---|---|---|---|
| Home | / | Prerender | No | h1.text-5xl, .btn.btn-primary |
| Pricing | /pricing | Prerender | No | h1:has-text("Pricing"), .card (3x) |
| Auth Login | /auth/login | Prerender | No | h2:has-text("Sign In"), input#email |
| Auth Signup | /auth/signup | Prerender | No | h2:has-text("Create Account"), input#email |
| Dashboard | /dashboard | Client | Yes | h1:has-text("Dashboard"), .stat-card |
| Dashboard Orders | /dashboard/orders | Client | Yes | h2:has-text("Order History"), table.table OR :text("No orders yet.") |
| Dashboard Settings | /dashboard/settings | Client | Yes | h2:has-text("Account Settings"), .btn.btn-error |
| Payment Callback | /payment/callback | Client | No | h1:has-text("Payment Submitted") |
baseline (default)Capture fresh baseline screenshots for all pages.
baseline/ directorydiffCompare current state against baselines.
current/ directorybaseline/ versiondiffs/ directory (only for changed pages)single <page>Capture or diff a single page. Page names: home, pricing, auth-login, auth-signup, dashboard, dashboard-orders, dashboard-settings, payment-callback.
// 1. Navigate
await page.goto(url)
await page.waitForLoadState('networkidle')
// 2. Wait for key elements (ensures page is fully rendered)
for (const selector of keyElements) {
await page.waitForSelector(selector, { timeout: 10000 })
}
// 3. Wait for animations/transitions to settle
await page.waitForTimeout(300)
// 4. Hide dynamic content that causes false positives
await page.evaluate(() => {
// Hide toast notifications
document.querySelectorAll('.toast').forEach((el) => (el.style.display = 'none'))
})
// 5. Capture screenshot
await page.screenshot({
path: screenshotPath,
fullPage: true,
})
// Login before capturing dashboard pages
await page.goto('http://localhost:4200/auth/login')
await page.waitForSelector('input#email')
await page.fill('input#email', testEmail)
await page.fill('input#password', testPassword)
await page.click('button.btn.btn-primary[type="submit"]')
await page.waitForURL('**/dashboard', { timeout: 10000 })
// Using page.screenshot comparison
// Threshold: 0.1% pixel difference = PASS (accounts for anti-aliasing)
// Above threshold = FAIL, generate diff image
function compareScreenshots(baseline, current) {
// Use Playwright's built-in screenshot comparison if available
// Or use pixelmatch library for pixel-level comparison
// Return: { match: boolean, diffPercent: number, diffImagePath: string }
}
Before capturing any screenshots, verify the theme is consistent:
const theme = await page.evaluate(() => document.documentElement.getAttribute('data-theme'))
console.log('Current theme:', theme || 'default')
// Store theme name with baseline metadata for comparison
Visual Regression Baselines Captured
=====================================
Theme: [theme-name]
Viewport: 1400x900
Date: [timestamp]
| Page | File | Size |
| ------------------- | -------------------------- | ------ |
| Home | baseline/home.png | 245 KB |
| Pricing | baseline/pricing.png | 312 KB |
| ... | ... | ... |
Total: 9 pages captured
Visual Regression Diff Results
===============================
Baseline date: [date]
Current date: [date]
Theme: [theme-name]
Viewport: 1400x900
| Page | Status | Diff % | Diff Image |
| ------------------- | ------ | ------- | ---------------------------- |
| Home | PASS | 0.00% | - |
| Pricing | FAIL | 2.34% | diffs/pricing-diff.png |
| ... | ... | ... | ... |
Result: 1 of 9 pages changed
DaisyUI button classes repeat across navbar and page content. When clicking elements during capture flows, scope selectors with parent containers:
.hero-content a.btn-primary (not a.btn-primary[href="/auth/login"]).navbar a.btn-sm or .navbar button.btn-sm.card-body a.btn-ghost:has-text("Back to Home")pnpm dev on port 4200)data-theme attribute is checked and recorded to catch unintentional theme changeshttp://localhost:54324): Supabase local email capture — useful for verifying auth emails after signup in visual tests