Frontend Testing
Testing React components, hooks, and user interactions
๐ Overview
This guide covers frontend-specific testing with Vitest, Testing Library, and MSW.
๐ ๏ธ Setup
Install Dependencies
cd apps/web
pnpm add -D vitest @testing-library/react @testing-library/jest-dom @testing-library/user-event @vitejs/plugin-react jsdom msw
Vitest Config
File: apps/web/vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [react(), tsconfigPaths()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './test/setup.ts',
},
})
Test Setup
File: apps/web/test/setup.ts
import '@testing-library/jest-dom/vitest'
import { cleanup } from '@testing-library/react'
import { afterEach } from 'vitest'
afterEach(() => {
cleanup()
})
๐งช Component Testing
Basic Component Test
import { render, screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import { Button } from '@/components/ui/button'
describe('Button', () => {
it('renders with text', () => {
render(<Button>Click me</Button>)
expect(screen.getByText('Click me')).toBeInTheDocument()
})
it('calls onClick when clicked', async () => {
const handleClick = vi.fn()
const user = userEvent.setup()
render(<Button onClick={handleClick}>Click</Button>)
await user.click(screen.getByText('Click'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
})
๐ Testing with tRPC
Mock tRPC Responses
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'
const server = setupServer(
http.post('http://localhost:4000/trpc/projects.list', () => {
return HttpResponse.json({
result: { data: [{ id: '1', name: 'Project 1' }] },
})
})
)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
Test Component with tRPC
import { render, screen, waitFor } from '@testing-library/react'
import { ProjectList } from '@/components/projects/project-list'
import { createWrapper } from '@/test/utils'
test('renders projects', async () => {
render(<ProjectList />, { wrapper: createWrapper() })
await waitFor(() => {
expect(screen.getByText('Project 1')).toBeInTheDocument()
})
})
๐ฃ Testing Hooks
import { renderHook, waitFor } from '@testing-library/react'
import { useProjects } from '@/lib/hooks/use-projects'
test('useProjects fetches projects', async () => {
const { result } = renderHook(() => useProjects(), {
wrapper: createWrapper(),
})
await waitFor(() => {
expect(result.current.projects).toHaveLength(2)
})
})
๐ Run Tests
# Run all tests
pnpm test
# Watch mode
pnpm test:watch
# Coverage
pnpm test:coverage
# UI mode
pnpm test:ui
๐ Related Documentation
- Testing Guide - General testing
- Components - Component patterns
- tRPC Client - tRPC usage
Section 04 (Frontend) complete! Next: Backend documentation