This document outlines the comprehensive testing strategy for the MIE UI component library.
- Vitest - Fast unit test runner with Jest compatibility
- React Testing Library - Component testing utilities
- Jest DOM - Additional DOM testing matchers
- User Events - Realistic user interaction simulation
- Playwright - Browser automation for visual testing
- Chromatic - Visual regression testing service integrated with Storybook
- Storybook - Component documentation and testing environment
- ESLint - Code linting and best practices
- TypeScript - Type checking
- Prettier - Code formatting
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.stories.tsx # Storybook stories
│ │ └── Button.test.tsx # Unit tests
│ └── ...
├── test/
│ ├── setup.ts # Test environment setup
│ └── test-utils.tsx # Custom render utilities
tests/
├── visual/
│ ├── components.spec.ts # Visual regression tests
│ └── comprehensive.spec.ts # Comprehensive visual tests
└── tsconfig.json # TypeScript config for tests
# Run all unit tests
npm run test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage report
npm run test:coverage# Install Playwright browsers (one-time setup)
npm run playwright:install
# Run all visual tests
npm run test:visual
# Run visual tests with UI mode
npm run test:visual:ui
# Run specific test file
npx playwright test tests/visual/components.spec.ts
# Update visual baselines (when intentional changes are made)
npx playwright test --update-snapshots# Start Storybook development server
npm run storybook
# Build static Storybook
npm run build-storybookimport { describe, it, expect, vi } from 'vitest';
import { screen } from '@testing-library/react';
import { renderWithTheme } from '../../test/test-utils';
import { Button } from './Button';
describe('Button', () => {
it('renders children correctly', () => {
renderWithTheme(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
});
it('handles click events', () => {
const handleClick = vi.fn();
renderWithTheme(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});import userEvent from '@testing-library/user-event';
it('handles user input', async () => {
const user = userEvent.setup();
const handleChange = vi.fn();
renderWithTheme(<Input onChange={handleChange} />);
const input = screen.getByRole('textbox');
await user.type(input, 'Hello World');
expect(input).toHaveValue('Hello World');
expect(handleChange).toHaveBeenCalled();
});import { test, expect } from '@playwright/test';
test('Button - Default state', async ({ page }) => {
await page.goto('/iframe.html?id=button--default&viewMode=story');
await page.waitForLoadState('networkidle');
await expect(page).toHaveScreenshot('button-default.png');
});test('Button - Hover state', async ({ page }) => {
await page.goto('/iframe.html?id=button--default&viewMode=story');
const button = page.getByRole('button').first();
await button.hover();
await expect(page).toHaveScreenshot('button-hover.png');
});test('Button - Dark theme', async ({ page }) => {
await page.goto('/iframe.html?id=button--default&viewMode=story&globals=theme:dark');
await page.waitForLoadState('networkidle');
await expect(page).toHaveScreenshot('button-dark.png');
});- Test behavior, not implementation - Focus on what the component does, not how it does it
- Use descriptive test names - Make it clear what is being tested
- Test accessibility - Ensure components work with screen readers and keyboard navigation
- Mock external dependencies - Use vi.fn() for callbacks and external services
- Test error states - Verify components handle errors gracefully
- Wait for animations - Use
waitForLoadState('networkidle')or specific waits - Test multiple states - Default, hover, focus, disabled, etc.
- Test responsive design - Different viewport sizes
- Test themes - Light/dark modes and different brand themes
- Use meaningful names - Screenshot names should be descriptive
- Cover all variants - Every prop combination should have a story
- Include interactive examples - Show real usage patterns
- Document accessibility - Use the a11y addon
- Add controls - Allow users to interact with components
The project uses a custom Vitest configuration with:
- JSdom environment for DOM testing
- Jest DOM matchers for enhanced assertions
- Coverage reporting with thresholds
- Path aliases for clean imports
The visual tests are configured to:
- Run against multiple browsers (Chrome, Firefox, Safari)
- Test desktop and mobile viewports
- Start Storybook automatically
- Generate HTML reports with screenshots
- Branches: 80%
- Functions: 80%
- Lines: 80%
- Statements: 80%
The CI pipeline runs:
- Linting and type checking
- Unit tests with coverage
- Visual regression tests
- Build verification
- Chromatic visual reviews
- Accessibility testing
- Security auditing
- Automated tests catch obvious regressions
- Chromatic reviews for detailed visual changes
- Manual review for complex interactions
- Approval process for design system changes
- Fonts not loading: Add font loading waits
- Animations: Add specific wait times
- Browser differences: Check if it's browser-specific
- Timing issues: Use
waitForLoadState('networkidle')
- Missing mocks: Ensure external dependencies are mocked
- Async operations: Use proper async/await patterns
- DOM cleanup: Tests should clean up after themselves
- Theme context: Use
renderWithThemefor themed components
When components intentionally change:
# Update all snapshots
npx playwright test --update-snapshots
# Update specific test snapshots
npx playwright test components.spec.ts --update-snapshots