Visual Regression Test Agent. Storybook + Chromatic/Percy를 사용한 Visual Regression 테스트를 담당합니다. UI 변경 감지 및 시각적 일관성을 검증합니다.
Install with Tessl CLI
npx tessl i github:shaul1991/shaul-agents-plugin --skill visual-regression63
Does it follow best practices?
If you maintain this skill, you can automatically optimize it using the tessl CLI to improve its score:
npx tessl skill review --optimize ./path/to/skillValidation for skill structure
UI의 시각적 변경을 감지하고, 의도하지 않은 변경을 방지합니다.
┌─────────────────────────────────────────────────────────────────┐
│ Visual Regression 테스트 흐름 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Baseline 캡처 │
│ └── 현재 UI 상태를 스크린샷으로 저장 │
│ │
│ 2. 코드 변경 │
│ └── UI 관련 코드 수정 │
│ │
│ 3. 새 스크린샷 캡처 │
│ └── 변경된 UI 상태 캡처 │
│ │
│ 4. 비교 (Diff) │
│ └── Baseline vs 새 스크린샷 픽셀 단위 비교 │
│ │
│ 5. 결과 │
│ ├── 변경 없음 → ✅ Pass │
│ ├── 의도된 변경 → 🔄 Baseline 업데이트 │
│ └── 의도치 않은 변경 → ❌ Fail (수정 필요) │
│ │
└─────────────────────────────────────────────────────────────────┘# Storybook 초기화
npx storybook@latest init
# 필요한 애드온
npm install -D @storybook/addon-a11y @storybook/addon-viewport// Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary', 'danger'],
},
size: {
control: { type: 'select' },
options: ['sm', 'md', 'lg'],
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
// 기본 상태
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Button',
},
};
// 비활성화 상태
export const Disabled: Story = {
args: {
variant: 'primary',
disabled: true,
children: 'Disabled',
},
};
// 로딩 상태
export const Loading: Story = {
args: {
variant: 'primary',
loading: true,
children: 'Loading',
},
};
// 다양한 크기
export const Sizes: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
),
};
// 다양한 변형
export const Variants: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem' }}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
</div>
),
};// ResponsiveComponent.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Header } from './Header';
const meta: Meta<typeof Header> = {
title: 'Layout/Header',
component: Header,
parameters: {
viewport: {
defaultViewport: 'responsive',
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Desktop: Story = {
parameters: {
viewport: {
defaultViewport: 'desktop',
},
},
};
export const Tablet: Story = {
parameters: {
viewport: {
defaultViewport: 'tablet',
},
},
};
export const Mobile: Story = {
parameters: {
viewport: {
defaultViewport: 'mobile1',
},
},
};# Chromatic 설치
npm install -D chromatic
# 프로젝트 설정 (처음 한 번)
npx chromatic --project-token=<your-token># .github/workflows/chromatic.yml
name: Chromatic
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run Chromatic
uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
exitZeroOnChanges: true # PR에서 변경 감지 시 실패하지 않음// chromatic.config.js
module.exports = {
projectToken: process.env.CHROMATIC_PROJECT_TOKEN,
// 스냅샷 옵션
delay: 300, // 캡처 전 대기 시간
diffThreshold: 0.063, // 픽셀 차이 임계값
// 제외할 스토리
onlyChanged: true, // 변경된 스토리만 테스트
externals: ['public/**'], // 외부 파일 변경 감지
// 브라우저
browsers: ['chrome', 'firefox'],
// 뷰포트
viewports: [320, 768, 1200],
};npm install -D @percy/cli @percy/storybook# Percy 실행
npx percy storybook http://localhost:6006# .github/workflows/percy.yml
name: Percy
on: [push, pull_request]
jobs:
percy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run build-storybook
- name: Percy Test
run: npx percy storybook ./storybook-static
env:
PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }}# Storybook 실행
npm run storybook
# Storybook 빌드
npm run build-storybook
# Chromatic 실행
npm run chromatic
# Visual Regression 로컬 테스트 (Loki)
npm run loki test
# Baseline 업데이트
npm run loki update// 모든 상태를 별도 Story로 분리
export const Default: Story = { args: { ... } };
export const Hover: Story = {
parameters: { pseudo: { hover: true } }
};
export const Focus: Story = {
parameters: { pseudo: { focus: true } }
};
export const Error: Story = { args: { error: true } };
export const Loading: Story = { args: { loading: true } };// 고정된 테스트 데이터 사용
export const WithData: Story = {
args: {
data: [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
],
},
};
// 날짜는 고정값 사용
export const WithDate: Story = {
args: {
date: new Date('2024-01-01'),
},
};// 글로벌 설정
// .storybook/preview.ts
export const parameters = {
chromatic: {
pauseAnimationAtEnd: true,
delay: 300,
},
};
// 특정 Story에서
export const Animated: Story = {
parameters: {
chromatic: { disableSnapshot: true }, // 스냅샷 제외
},
};## Visual Regression 리뷰
### 변경된 컴포넌트
- Button (3 변경)
- Header (1 변경)
- Card (0 변경)
### 리뷰 필요 항목
#### Button - Primary
- 변경 유형: 색상 변경
- 의도된 변경: ✅
- 승인: @reviewer
#### Button - Hover
- 변경 유형: 그림자 추가
- 의도된 변경: ✅
- 승인: @reviewer
### 최종 결과
- [ ] 모든 변경 승인됨
- [ ] Baseline 업데이트 완료src/components/**/*.stories.tsxstorybook-static/docs/features/<기능명>/test-results/visual-regression-report.md9242c58
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.