testing-patterns
الگوهای تست انیمیشن برای بررسی ظاهر، زمانبندی، دسترسپذیری و عملکرد
این Skill روشهای منظم تست انیمیشنهای UI را با Playwright، Jest و ابزارهای مرورگر ارائه میکند؛ از visual regression و بررسی timing تا تست prefers-reduced-motion و نرخ فریم.
با استفاده از این الگوها، خطاهای بصری، زمانبندی ناپایدار، ناسازگاری با دسترسپذیری و افت عملکرد را پیش از انتشار پیدا میکنید و تستها را در CI قابلاعتمادتر اجرا میکنید.
چه چیزی میگیرید
- نمونه تست visual regression برای حالتهای مختلف انیمیشن با Playwright
- الگوهای بررسی duration، delay و easing با Jest fake timers
- تست سازگاری با prefers-reduced-motion و الزامات دسترسپذیری
- روش سنجش frame rate، زمان paint و عملکرد انیمیشن
- پیشنهادهایی برای کاهش flaky test و اجرای تستها در CI
برای چه کاری خوب است
- تیمهای frontend که برای کامپوننتهای متحرک تست خودکار مینویسند
- پروژههایی که به visual regression و کنترل دقیق timing نیاز دارند
- محصولاتی که باید reduced motion و عملکرد روان انیمیشن را تضمین کنند
برای چه کاری نیست
- پروژههایی که هنوز انیمیشن یا رابط کاربری قابلتستی ندارند
- تحلیل طراحی و ساخت انیمیشن؛ این Skill بر اعتبارسنجی و تست تمرکز دارد
دربارهٔ این اسکیل
Animation Testing Patterns
Systematic approaches to testing animations for correctness, accessibility, and performance.
Testing Categories
| Category | What to Test | Tools |
|---|---|---|
| Visual regression | Animation appearance | Playwright, Percy |
| Timing | Duration, delays, easing | Jest timers, Playwright |
| Accessibility | Reduced motion, focus | Playwright, axe |
| Performance | Frame rate, paint times | Chrome DevTools, Lighthouse |
Visual Regression Testing
Playwright Setup
import { test, expect } from '@playwright/test';
test.describe('Button animations', () => {
test('hover state animation', async ({ page }) => {
await page.goto('/components/button');
const button = page.locator('.animated-button');
await expect(button).toHaveScreenshot('button-idle.png');
await button.hover();
await page.waitForTimeout(300);
await expect(button).toHaveScreenshot('button-hover.png');
});
});
Timing Verification
Jest Timer Tests
import { render, screen, act } from '@testing-library/react';
describe('Animation timing', () => {
beforeEach(() => jest.useFakeTimers());
afterEach(() => jest.useRealTimers());
test('fade-in completes in 300ms', () => {
render(<AnimatedComponent />);
const element = screen.getByTestId('animated-element');
expect(element).toHaveStyle({ opacity: 0 });
act(() => jest.advanceTimersByTime(300));
expect(element).toHaveStyle({ opacity: 1 });
});
});
Reduced Motion Testing
Playwright Reduced Motion
test.describe('Reduced motion compliance', () => {
test('respects prefers-reduced-motion', async ({ page }) => {
await page.emulateMedia({ reducedMotion: 'reduce' });
await page.goto('/components/animated-list');
await page.click('[data-add-item]');
const item = page.locator('.list-item').last();
const animationDuration = await item.evaluate(
el => getComputedStyle(el).animationDuration
);
expect(animationDuration).toBe('0s');
});
});
Jest Reduced Motion
describe('Reduced motion', () => {
beforeEach(() => {
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: query === '(prefers-reduced-motion: reduce)',
media: query,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
})),
});
});
test('uses instant transition when reduced motion preferred', () => {
render(<AnimatedComponent />);
const element = screen.getByTestId('animated');
expect(element).toHaveClass('reduced-motion');
});
});
Performance Testing
Frame Rate Testing
test('maintains 60fps during animation', async ({ page }) => {
await page.goto('/components/heavy-animation');
await page.tracing.start({ categories: ['devtools.timeline'] });
await page.click('[data-start-animation]');
await page.waitForTimeout(1000);
const traceBuffer = await page.tracing.stop();
const trace = JSON.parse(traceBuffer.toString());
// Analyze frame times - 60fps = 16.67ms per frame
const frames = trace.traceEvents
.filter(e => e.name === 'FrameCommittedInBrowser')
.map(e => e.ts);
const frameTimes = frames.slice(1).map((ts, i) => ts - frames[i]);
const avgFrameTime = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
expect(avgFrameTime).toBeLessThan(20000); // 20ms average
});
CI Integration
name: Animation Tests
on: [push, pull_request]
jobs:
visual-regression:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:animations
- uses: actions/upload-artifact@v3
if: failure()
with:
name: animation-test-results
path: test-results/
Common Testing Mistakes
- Not waiting for animation completion - Use proper timeouts
- Ignoring reduced motion - Always test both preferences
- Screenshot timing issues - Capture at consistent animation points
- Missing performance baseline - Establish fps expectations
- Flaky visual tests - Allow reasonable pixel tolerance
Related Skills
motion-audit/- Comprehensive audit checklistsaccessibility/- WCAG compliance requirementsremotion/- Testing Remotion compositions
See references/ for complete Playwright and Jest test examples.