Loading...
Loading...
Visual verification workflow for UI changes to accelerate code review and catch ...
npx skill4agent add bobmatnyc/claude-mpm-skills screenshot// screenshot.js
const { chromium } = require('playwright');
async function captureScreenshots(url) {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(url);
// Desktop
await page.setViewportSize({ width: 1920, height: 1080 });
await page.screenshot({
path: 'screenshots/desktop.png',
fullPage: true
});
// Tablet
await page.setViewportSize({ width: 768, height: 1024 });
await page.screenshot({
path: 'screenshots/tablet.png',
fullPage: true
});
// Mobile
await page.setViewportSize({ width: 375, height: 667 });
await page.screenshot({
path: 'screenshots/mobile.png',
fullPage: true
});
await browser.close();
}
captureScreenshots('http://localhost:3000');node screenshot.js// screenshot.js
const puppeteer = require('puppeteer');
async function captureScreenshots(url) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
// Desktop
await page.setViewport({ width: 1920, height: 1080 });
await page.screenshot({
path: 'screenshots/desktop.png',
fullPage: true
});
// Tablet
await page.setViewport({ width: 768, height: 1024 });
await page.screenshot({
path: 'screenshots/tablet.png',
fullPage: true
});
// Mobile
await page.setViewport({ width: 375, height: 667 });
await page.screenshot({
path: 'screenshots/mobile.png',
fullPage: true
});
await browser.close();
}
captureScreenshots('http://localhost:3000');## Visual Changes
### Desktop (1920x1080)

**Key changes**:
- Updated header navigation layout
- Improved spacing between sections
- Added hover states to buttons
### Tablet (768x1024)

**Key changes**:
- Stacked layout for sidebar
- Touch-friendly button sizes (48x48px)
- Adjusted typography for readability
### Mobile (375x667)

**Key changes**:
- Hamburger menu replaces horizontal nav
- Single column layout
- Bottom sticky CTA button
### Before/After Comparison
#### Before (Bug)

**Issue**: Text overflowing container on mobile
#### After (Fixed)

**Fix**: Applied word-wrap and max-width constraints
### Interaction States
#### Default State

#### Hover State

#### Active/Selected State

#### Error State

#### Loading State

## Responsive Design Notes
- Breakpoints: 768px (tablet), 375px (mobile)
- All touch targets > 44x44px
- Text remains readable at all sizes (min 16px body)
- No horizontal scrolling on any viewport
- Images scale proportionally
## Accessibility Checks
- [ ] Keyboard navigation works
- [ ] Focus states visible
- [ ] Color contrast meets WCAG AA (4.5:1)
- [ ] Alt text on images
- [ ] ARIA labels where needed// tests/visual.spec.js
const { test, expect } = require('@playwright/test');
test.describe('Visual Regression', () => {
test('homepage looks correct on desktop', async ({ page }) => {
await page.goto('http://localhost:3000');
await page.setViewportSize({ width: 1920, height: 1080 });
await expect(page).toHaveScreenshot('homepage-desktop.png');
});
test('homepage looks correct on mobile', async ({ page }) => {
await page.goto('http://localhost:3000');
await page.setViewportSize({ width: 375, height: 667 });
await expect(page).toHaveScreenshot('homepage-mobile.png');
});
});npx playwright test --update-snapshots// Button.stories.jsx
export default {
title: 'Components/Button',
component: Button,
};
export const Primary = () => <Button variant="primary">Click me</Button>;
export const Secondary = () => <Button variant="secondary">Click me</Button>;npm install --save-dev chromatic
npx chromatic --project-token=<token># .github/workflows/chromatic.yml
name: Chromatic
on: push
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx chromatic --project-token=${{ secrets.CHROMATIC_TOKEN }}desktop-homepage.png# Using ImageMagick
convert input.png -quality 85 output.png
# Using pngquant
pngquant input.png --output output.png// Mock Date
const mockDate = new Date('2025-01-01T00:00:00Z');
jest.useFakeTimers();
jest.setSystemTime(mockDate);
// Or in Playwright
await page.addInitScript(() => {
Date.now = () => 1704067200000; // Fixed timestamp
});FROM mcr.microsoft.com/playwright:v1.40.0-jammy
WORKDIR /app
COPY . .
RUN npm ci
CMD ["npm", "run", "screenshot"]screenshots/
├── desktop/
│ ├── homepage.png
│ ├── product-list.png
│ └── checkout.png
├── tablet/
│ ├── homepage.png
│ ├── product-list.png
│ └── checkout.png
└── mobile/
├── homepage.png
├── product-list.png
└── checkout.pngdesktop-homepage-logged-in.pngmobile-form-error-state.pngbefore-header-fix.pngafter-header-fix.pngname: Visual Testing
on: pull_request
jobs:
screenshots:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build app
run: npm run build
- name: Start app
run: npm start &
- name: Wait for app
run: npx wait-on http://localhost:3000
- name: Capture screenshots
run: node scripts/screenshot.js
- name: Upload screenshots
uses: actions/upload-artifact@v4
with:
name: screenshots
path: screenshots/universal-verification-pre-mergeuniversal-testing-webapp-testingtoolchains-javascript-testing-playwrightuniversal-debugging-verification-before-completion