ワンクリックで
test-ui
Visual regression testing with Playwright screenshots and AI-powered diff analysis
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Visual regression testing with Playwright screenshots and AI-powered diff analysis
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Quick unit + property-based test run with AI auto-fix loop
Complete test suite — unit, property, integration, e2e, mutation, and coverage audit
Lighthouse performance/SEO/a11y/best-practices optimization with AI self-loop to push all scores to 90+
Unit + property tests verified by mutation testing with AI self-improvement loop
Implement changes to align code with UX design files (product-ia.md, product-flows.md, product-interactions.md). Reads design specs and modifies code to match.
Product UX review from the user's perspective. Analyzes IA, user flows, and interactions for any web application.
| name | test-ui |
| description | Visual regression testing with Playwright screenshots and AI-powered diff analysis |
| user-invocable | true |
/test-uiYou are performing visual regression testing on the Elenvo Next.js application. Your job is to capture screenshots of affected pages, compare them against baselines, identify regressions, fix CSS/layout issues, and update baselines for intentional changes.
The user may optionally pass specific routes as arguments (e.g., /test-ui /dashboard /settings). If provided, test ONLY those routes. Otherwise, auto-detect affected pages from git diff.
If the user provided specific routes as arguments, use those and skip detection.
Otherwise, run git diff --name-only (include both staged and unstaged changes) and map changed files to routes:
| Changed file pattern | Routes to test |
|---|---|
app/(app)/dashboard/* | /dashboard |
app/(app)/settings/* | /settings |
app/(app)/onboarding/* | /onboarding |
app/(app)/retirement/* | /retirement |
app/(app)/insurance/* | /insurance |
app/(app)/financial-overview/* | /financial-overview |
app/(app)/my-plans/* | /my-plans |
app/(app)/ai-advisor/* | /ai-advisor |
app/(app)/advisors/* | /advisors |
app/(public)/page.tsx or app/page.tsx | / |
app/(public)/sign-up/* | /sign-up |
components/layout/* | ALL major routes (layout change affects everything) |
components/ui/* | ALL major routes (shared component change) |
app/globals.css | ALL major routes (global style change) |
tailwind.config.ts | ALL major routes |
The full list of major routes for "ALL" cases: /, /dashboard, /settings, /onboarding, /retirement, /insurance, /financial-overview, /my-plans, /ai-advisor, /advisors
Public routes (no auth needed): /, /sign-up
Authenticated routes (login required): /dashboard, /settings, /onboarding, /retirement, /insurance, /financial-overview, /my-plans, /ai-advisor, /advisors
Print a summary: "Testing X pages: /route1, /route2, ..."
If no routes are affected (e.g., only test files or docs changed), report "No UI-affecting changes detected" and exit.
Check if localhost:3000 is already serving:
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 || true
If NOT running, start it in the background:
npm run dev &
Then wait for it to be ready (poll with curl, max 30 seconds).
mkdir -p .test-ui/current .test-ui/diff .test-ui/baseline
For EACH affected route, capture at two viewports:
Desktop (1280x720):
browser_resize to width=1280, height=720browser_navigate to http://localhost:3000<route>browser_evaluate to check:
document.readyState === 'complete'[data-loading="true"] or .animate-spin visiblebrowser_take_screenshot.test-ui/current/<route-name>-desktop.pngMobile (375x667):
browser_resize to width=375, height=667.test-ui/current/<route-name>-mobile.pngRoute name mapping for filenames: strip leading /, replace / with -. Root / becomes home.
Examples: /dashboard → dashboard, /financial-overview → financial-overview, / → home
Before capturing authenticated routes, you MUST log in first:
http://localhost:3000/sign-in (or the login page)SUPABASE_TEST_EMAIL env var, or fall back to zelin@vt.eduSUPABASE_TEST_PASSWORD env varIf login fails (no test password available), skip authenticated routes and report which routes were skipped due to auth.
If the Playwright MCP tools are not available, write a temporary Playwright script:
// .test-ui/capture.ts — temporary script, delete after use
import { chromium } from 'playwright'
const routes = [
/* detected routes */
]
const viewports = [
{ name: 'desktop', width: 1280, height: 720 },
{ name: 'mobile', width: 375, height: 667 },
]
async function capture() {
const browser = await chromium.launch()
const context = await browser.newContext()
const page = await context.newPage()
// Login if needed
// ... (fill in auth flow)
for (const route of routes) {
for (const vp of viewports) {
await page.setViewportSize({ width: vp.width, height: vp.height })
await page.goto(`http://localhost:3000${route}`, { waitUntil: 'networkidle' })
await page.waitForTimeout(1000) // settle animations
const safeName = route === '/' ? 'home' : route.slice(1).replace(/\//g, '-')
await page.screenshot({
path: `.test-ui/current/${safeName}-${vp.name}.png`,
fullPage: false,
})
}
}
await browser.close()
}
capture()
Run with: npx playwright test .test-ui/capture.ts or npx tsx .test-ui/capture.ts
Delete the script after use.
Check if .test-ui/baseline/ has any .png files.
.test-ui/current/ to .test-ui/baseline//test-ui again after future changes to compare.".test-ui/current/ and .test-ui/diff/ are in .gitignore (baselines are committed)For each screenshot in .test-ui/current/, find its matching baseline in .test-ui/baseline/.
Pixel comparison approach — write and run a small Node script using pixelmatch:
// .test-ui/compare.mjs — temporary, delete after use
import { readFileSync, writeFileSync, readdirSync } from 'fs'
import { PNG } from 'pngjs'
import pixelmatch from 'pixelmatch'
const currentDir = '.test-ui/current'
const baselineDir = '.test-ui/baseline'
const diffDir = '.test-ui/diff'
const files = readdirSync(currentDir).filter((f) => f.endsWith('.png'))
const results = []
for (const file of files) {
const baselinePath = `${baselineDir}/${file}`
const currentPath = `${currentDir}/${file}`
try {
const baseline = PNG.sync.read(readFileSync(baselinePath))
const current = PNG.sync.read(readFileSync(currentPath))
const { width, height } = baseline
const diff = new PNG({ width, height })
const numDiffPixels = pixelmatch(baseline.data, current.data, diff.data, width, height, {
threshold: 0.1,
})
const totalPixels = width * height
const diffPercent = ((numDiffPixels / totalPixels) * 100).toFixed(2)
writeFileSync(`${diffDir}/${file}`, PNG.sync.write(diff))
results.push({ file, diffPercent: parseFloat(diffPercent), numDiffPixels, totalPixels })
} catch (e) {
results.push({ file, error: e.message })
}
}
console.log(JSON.stringify(results, null, 2))
First ensure dependencies are available: npm list pixelmatch pngjs 2>/dev/null || npm install --no-save pixelmatch pngjs
Run: node .test-ui/compare.mjs
Parse the results. For each screenshot:
Delete the temporary comparison script after use.
For each screenshot with diff > 0.5%, visually analyze the changes:
.test-ui/baseline/<name>.png) using the Read tool.test-ui/current/<name>.png) using the Read tool.test-ui/diff/<name>.png) using the Read toolCompare all three images carefully. Categorize the change:
The visual diff matches the code change. Examples:
Action: Mark this baseline for update (Step 6).
An unintended visual change. Examples:
Action: Fix in Step 5.
Minor rendering differences not caused by code changes:
Action: Ignore (treat as PASS).
For EACH analyzed screenshot, print:
📸 <filename>: <CATEGORY> — <brief description of what changed>
For each regression identified:
Identify the root cause: Read the component and CSS files related to the affected page. Look at what changed in the git diff that could have caused this.
Make a targeted fix: Edit ONLY the CSS/layout properties needed. Common fixes:
overflow-hidden or overflow-auto for overflow issuesflex / grid properties for layout shiftsz-index for stacking issuessm:, md:, lg:) for mobile regressionsw-full, min-h-0, shrink-0 for sizing issuesRe-capture: Take new screenshots of ONLY the affected pages/viewports.
Re-compare: Run pixel comparison again on the fixed screenshots.
Verify: If diff is now <= 0.5% or categorized as intentional/acceptable, the fix worked.
Maximum 3 iterations. If a regression persists after 3 fix attempts, report it as needing user attention — it's likely a design-level decision, not a simple CSS bug.
After all analysis and fixes are complete, collect all screenshots that need baseline updates:
IMPORTANT: Do NOT silently update baselines. Present the full list to the user:
🔄 Baseline updates needed:
1. dashboard-desktop.png — INTENTIONAL: new stats card added to header
2. dashboard-mobile.png — INTENTIONAL: new stats card added to header
3. settings-desktop.png — FIXED REGRESSION: sidebar overlap corrected
4. home-desktop.png — NEW: first baseline capture
Confirm baseline updates? (These will be committed to .test-ui/baseline/)
Wait for user confirmation before copying current screenshots to baseline.
Print a final summary table:
╔══════════════════════════════════╤═════════╤═════════╗
║ Page │ Desktop │ Mobile ║
╠══════════════════════════════════╪═════════╪═════════╣
║ / (home) │ ✅ PASS │ ✅ PASS ║
║ /dashboard │ 🔄 UPD │ 🔄 UPD ║
║ /settings │ 🔧 FIX │ ✅ PASS ║
╚══════════════════════════════════╧═════════╧═════════╝
Legend:
Then summarize:
Final status line:
✅ VISUAL TEST PASSED — all pages pass, no action needed🔄 BASELINES UPDATED — intentional changes captured (user confirmed)❌ NEEDS ATTENTION — unresolved regressions requiring user decisionAfter the skill completes:
.test-ui/compare.mjs, .test-ui/capture.ts).test-ui/current/ (useful for manual inspection) but these are gitignored.test-ui/diff/ (useful for manual inspection) but these are gitignored.test-ui/baseline/ are the only committed artifactsEnsure these entries exist in the project .gitignore:
# visual regression testing
.test-ui/current/
.test-ui/diff/
Do NOT gitignore .test-ui/baseline/ — baselines should be committed so the team shares the same visual reference.