component-tester
π―Skillfrom deve1993/quickfy-website
Writes and runs comprehensive React component tests using Vitest 4, React Testing Library, with coverage, visual regression, and accessibility validation.
Installation
npx skills add https://github.com/deve1993/quickfy-website --skill component-testerSkill Details
Write, run, and analyze component tests using Vitest 4 and React Testing Library with coverage analysis, visual regression, and accessibility validation
Overview
# Component Tester
Expert skill for testing UI component libraries with Vitest 4 and React Testing Library. Specializes in Browser Mode, visual regression testing, comprehensive coverage analysis, and accessibility validation.
Technology Stack (2025)
Testing Framework
- Vitest 4.0 - Browser Mode stable, visual regression built-in
- @testing-library/react 16 - React 19 support
- @testing-library/user-event 14 - Realistic user interactions
- @vitest/browser-playwright - Browser provider for visual testing
- @axe-core/react 5 - Accessibility testing
Coverage & Reporting
- @vitest/coverage-v8 - Fast V8-based coverage
- Playwright Traces - Debug failed tests with traces
- HTML Reporter - Visual test reports
Core Capabilities
1. Test Writing
- Unit tests for individual components
- Integration tests for component interactions
- Visual regression tests (Vitest 4 Browser Mode)
- Accessibility tests (a11y)
- User interaction tests
- Async behavior testing
- Server Component testing
2. Vitest 4.0 Features
- Browser Mode Stable - Real browser testing
- Visual Regression - Built-in screenshot comparison
- Playwright Traces - Debugging with traces
- Improved TypeScript - Better type inference
3. Test Patterns
- Arrange-Act-Assert (AAA) pattern
- Test fixtures and factories
- Custom render functions
- Mock management
- React 19
useAPI testing
Configuration
Vitest 4 Config
```typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
css: true,
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
exclude: [
'node_modules/',
'src/test/',
'*/.d.ts',
'*/.config.*',
'dist/',
],
thresholds: {
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
// Browser Mode (Vitest 4)
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
headless: true,
},
},
})
```
Browser Mode Setup
```typescript
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config'
export default defineWorkspace([
{
extends: './vitest.config.ts',
test: {
name: 'unit',
environment: 'jsdom',
include: ['src/*/.test.{ts,tsx}'],
},
},
{
extends: './vitest.config.ts',
test: {
name: 'browser',
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
},
include: ['src/*/.browser.test.{ts,tsx}'],
},
},
])
```
Setup File
```typescript
// src/test/setup.ts
import '@testing-library/jest-dom/vitest'
import { cleanup } from '@testing-library/react'
import { afterEach, vi } from 'vitest'
afterEach(() => {
cleanup()
})
// Mock matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
})
// Mock IntersectionObserver
global.IntersectionObserver = class IntersectionObserver {
constructor() {}
disconnect() {}
observe() {}
takeRecords() { return [] }
unobserve() {}
} as any
```
Test Examples
Basic Component Test
```typescript
// Button.test.tsx
import { render, screen } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { describe, it, expect, vi } from 'vitest'
import { Button } from './Button'
describe('Button', () => {
it('renders with text', () => {
render()
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument()
})
it('calls onClick when clicked', async () => {
const handleClick = vi.fn()
const user = userEvent.setup()
render()
await user.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('is disabled when disabled prop is true', () => {
render()
expect(screen.getByRole('button')).toBeDisabled()
})
it('applies variant styles', () => {
const { rerender } = render()
expect(screen.getByRole('button')).toHaveClass('bg-primary')
rerender()
expect(screen.getByRole('button')).toHaveClass('bg-secondary')
})
})
```
Visual Regression Test (Vitest 4)
```typescript
// Button.visual.test.tsx
import { page } from '@vitest/browser/context'
import { describe, it, expect } from 'vitest'
import { render } from '@testing-library/react'
import { Button } from './Button'
describe('Button visual', () => {
it('matches default screenshot', async () => {
render()
await expect(page.screenshot()).toMatchFileSnapshot(
'./__snapshots__/button-default.png'
)
})
it('matches hover state screenshot', async () => {
render()
const button = page.getByRole('button')
await button.hover()
await expect(page.screenshot()).toMatchFileSnapshot(
'./__snapshots__/button-hover.png'
)
})
it('matches all variants', async () => {
const variants = ['primary', 'secondary', 'outline', 'ghost'] as const
for (const variant of variants) {
render()
await expect(page.screenshot()).toMatchFileSnapshot(
./__snapshots__/button-${variant}.png
)
}
})
})
```
Accessibility Test
```typescript
// Button.a11y.test.tsx
import { render } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'
import { describe, it, expect } from 'vitest'
import { Button } from './Button'
expect.extend(toHaveNoViolations)
describe('Button accessibility', () => {
it('has no accessibility violations', async () => {
const { container } = render()
const results = await axe(container)
expect(results).toHaveNoViolations()
})
it('has correct ARIA label when icon-only', async () => {
const { container } = render(
)
const results = await axe(container)
expect(results).toHaveNoViolations()
})
})
```
Keyboard Navigation Test
```typescript
// Menu.test.tsx
import { render, screen } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import { describe, it, expect } from 'vitest'
import { Menu } from './Menu'
describe('Menu keyboard navigation', () => {
it('opens menu with Enter key', async () => {
const user = userEvent.setup()
render()
const trigger = screen.getByRole('button', { name: /open menu/i })
await user.tab()
await user.keyboard('{Enter}')
expect(screen.getByRole('menu')).toBeInTheDocument()
})
it('navigates items with arrow keys', async () => {
const user = userEvent.setup()
render()
const items = screen.getAllByRole('menuitem')
await user.tab()
expect(items[0]).toHaveFocus()
await user.keyboard('{ArrowDown}')
expect(items[1]).toHaveFocus()
await user.keyboard('{ArrowUp}')
expect(items[0]).toHaveFocus()
})
it('closes menu with Escape key', async () => {
const user = userEvent.setup()
render()
expect(screen.getByRole('menu')).toBeInTheDocument()
await user.keyboard('{Escape}')
expect(screen.queryByRole('menu')).not.toBeInTheDocument()
})
})
```
React 19 Server Component Test
```typescript
// ProductList.test.tsx
import { render, screen } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
// Mock the database
vi.mock('@/lib/db', () => ({
db: {
products: {
findMany: vi.fn().mockResolvedValue([
{ id: '1', name: 'Product 1', price: 100 },
{ id: '2', name: 'Product 2', price: 200 },
]),
},
},
}))
describe('ProductList Server Component', () => {
it('renders products from database', async () => {
const ProductList = (await import('./ProductList')).default
render(await ProductList())
expect(screen.getByText('Product 1')).toBeInTheDocument()
expect(screen.getByText('Product 2')).toBeInTheDocument()
})
})
```
Custom Hooks Test
```typescript
// useCounter.test.ts
import { renderHook, act } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import { useCounter } from './useCounter'
describe('useCounter', () => {
it('increments counter', () => {
const { result } = renderHook(() => useCounter())
expect(result.current.count).toBe(0)
act(() => {
result.current.increment()
})
expect(result.current.count).toBe(1)
})
it('decrements counter', () => {
const { result } = renderHook(() => useCounter(10))
act(() => {
result.current.decrement()
})
expect(result.current.count).toBe(9)
})
it('resets to initial value', () => {
const { result } = renderHook(() => useCounter(5))
act(() => {
result.current.increment()
result.current.increment()
result.current.reset()
})
expect(result.current.count).toBe(5)
})
})
```
Testing Best Practices
What to Test
- User-visible behavior
- Accessibility features
- User interactions
- Different prop combinations
- Edge cases and error states
- Loading and async states
What NOT to Test
- Implementation details
- Internal state directly
- Styling (use visual regression)
- Third-party libraries
- Framework internals
Query Priority Order
```typescript
// 1. Accessible to all (best)
getByRole('button', { name: /submit/i })
getByLabelText(/username/i)
getByPlaceholderText(/enter email/i)
getByText(/welcome/i)
// 2. Semantic (good)
getByAltText(/profile picture/i)
getByTitle(/tooltip/i)
// 3. Test IDs (last resort)
getByTestId('submit-button')
```
Running Tests
Commands
```bash
# Run all tests
npm test
# Watch mode
npm test -- --watch
# Run with coverage
npm test -- --coverage
# Run visual tests
npm test -- --project=browser
# Update visual snapshots
npm test -- --update-snapshots
# Run in UI mode
npm test -- --ui
# Run specific file
npm test Button.test.tsx
```
CI/CD Integration
```yaml
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm ci
- run: npx playwright install chromium
- run: npm test -- --coverage
- uses: codecov/codecov-action@v4
with:
file: ./coverage/lcov.info
```
When to Use This Skill
Activate when you need to:
- Write unit tests for components
- Create visual regression tests
- Set up test infrastructure
- Analyze test coverage
- Add accessibility tests
- Test user interactions
- Mock API calls
- Configure Vitest 4
- Debug test issues
Output Format
Provide:
- Complete Test Suite: All test cases
- Coverage Report: What's tested
- Setup Instructions: Configuration needed
- Accessibility Notes: A11y test results
- Visual Tests: Screenshot comparison setup
More from this repository5
Generates, converts, and optimizes React/Vue/Svelte UI components with advanced TypeScript patterns, animations, and accessibility.
Optimizes and publishes TypeScript libraries with zero-config bundling, multi-format exports, and streamlined NPM publishing workflows.
design-system-manager skill from deve1993/quickfy-website
Generates comprehensive documentation for component libraries, including Storybook stories, README files, API docs, and migration guides with professional precision.
Scans and analyzes project structure, dependencies, code complexity, and potential architectural issues in web development repositories