diff --git a/README.md b/README.md index ecb9971..2ed5786 100644 --- a/README.md +++ b/README.md @@ -65,3 +65,32 @@ Start the app - Tailwind CSS - Icons from [Lucide](https://lucide.dev) - Tailwind CSS class sorting, merging and linting. + +## Testing + +Quick commands: + +```bash +# Run all tests (CI mode) +yarn test:run + +# Watch mode (dev) +yarn test:watch + +# UI runner +yarn test:ui + +# Coverage report +yarn test:coverage + +# Validate setup (sanity checks) +yarn test:validation +``` + +Notes: + +- Tests live in `tests/` with utilities in `tests/test-utils.tsx`. +- Mocks are under `tests/mocks/` (Next components, browser APIs, external libs). +- Use the custom render from `@/tests/test-utils` to get providers. +- Path alias `@/` points to project root. +- jsdom environment is preconfigured. diff --git a/docs/TESTING.md b/docs/TESTING.md deleted file mode 100644 index 9ad64cc..0000000 --- a/docs/TESTING.md +++ /dev/null @@ -1,473 +0,0 @@ -# Testing Guide - -This document provides comprehensive guidance for testing in this Next.js 14 project. - -## ๐Ÿš€ Quick Start - -### Running Tests - -```bash -# Run all tests -yarn test - -# Run tests in watch mode (recommended for development) -yarn test:watch - -# Run tests with UI (visual test runner) -yarn test:ui - -# Run tests once (CI mode) -yarn test:run - -# Run tests with coverage report -yarn test:coverage - -# Validate test setup -yarn test:validation -``` - -### Writing Your First Test - -```typescript -import { render, screen } from '@/tests/test-utils' -import { MyComponent } from '@/components/MyComponent' - -describe('MyComponent', () => { - it('renders correctly', () => { - render() - - expect(screen.getByText('Hello World')).toBeInTheDocument() - }) -}) -``` - -## ๐Ÿ—๏ธ Test Structure - -### File Organization - -``` -tests/ -โ”œโ”€โ”€ setup.ts # Global test setup -โ”œโ”€โ”€ test-utils.tsx # Custom render functions & utilities -โ”œโ”€โ”€ mocks/ # Mock implementations -โ”‚ โ”œโ”€โ”€ index.ts -โ”‚ โ”œโ”€โ”€ next-components.ts # Next.js component mocks -โ”‚ โ”œโ”€โ”€ external-libraries.ts # Third-party library mocks -โ”‚ โ””โ”€โ”€ browser-apis.ts # Browser API mocks -โ”œโ”€โ”€ examples/ # Example test files -โ”‚ โ”œโ”€โ”€ Button.test.tsx -โ”‚ โ”œโ”€โ”€ Input.test.tsx -โ”‚ โ”œโ”€โ”€ AppLink.test.tsx -โ”‚ โ”œโ”€โ”€ GlobalProvider.test.tsx -โ”‚ โ”œโ”€โ”€ api-route.test.ts -โ”‚ โ””โ”€โ”€ hooks.test.tsx -โ””โ”€โ”€ validation.test.ts # Setup validation tests -``` - -### Naming Conventions - -- **Test files**: `ComponentName.test.tsx` or `functionName.test.ts` -- **Test descriptions**: Use descriptive names that explain the behavior -- **Test IDs**: Use `data-testid` for elements that need specific targeting - -## ๐Ÿงฐ Testing Utilities - -### Custom Render Function - -Always use the custom render function from `@/tests/test-utils`: - -```typescript -import { render, screen } from '@/tests/test-utils' - -// This automatically wraps your component with all necessary providers: -// - GlobalProvider (with QueryClient) -// - ProjectsProvider -// - ThemeProvider -``` - -### Provider-Specific Rendering - -For more control over which providers to include: - -```typescript -import { renderWithProviders } from '@/tests/test-utils' - -renderWithProviders(, { - withGlobal: true, // Include GlobalProvider - withProjects: false, // Exclude ProjectsProvider - withTheme: true, // Include ThemeProvider -}) -``` - -### Helper Functions - -```typescript -import { - waitForLoadingToFinish, - mockMatchMedia, - mockLocalStorage, - resetAllMocks -} from '@/tests/test-utils' - -// Wait for async operations -await waitForLoadingToFinish() - -// Mock media queries -mockMatchMedia(true) // true = dark mode preference - -// Mock localStorage with methods -const storage = mockLocalStorage() -storage.getItem.mockReturnValue('stored-value') - -// Reset all mocks between tests -resetAllMocks() -``` - -## ๐ŸŽฏ Testing Patterns - -### Component Testing - -#### Basic Component -```typescript -describe('Button', () => { - it('renders with text', () => { - render() - expect(screen.getByRole('button')).toHaveTextContent('Click me') - }) - - it('handles click events', () => { - const handleClick = vi.fn() - render() - - fireEvent.click(screen.getByRole('button')) - expect(handleClick).toHaveBeenCalledTimes(1) - }) -}) -``` - -#### Component with Props -```typescript -describe('Card', () => { - const defaultProps = { - title: 'Test Title', - content: 'Test content', - variant: 'default' as const, - } - - it('renders with required props', () => { - render() - - expect(screen.getByText('Test Title')).toBeInTheDocument() - expect(screen.getByText('Test content')).toBeInTheDocument() - }) - - it('applies variant styles', () => { - render() - - const card = screen.getByRole('article') // or appropriate role - expect(card).toHaveClass('highlighted-variant-class') - }) -}) -``` - -#### Client Components -```typescript -describe('InteractiveComponent', () => { - it('updates state on user interaction', () => { - render() - - const input = screen.getByRole('textbox') - fireEvent.change(input, { target: { value: 'new value' } }) - - expect(input).toHaveValue('new value') - }) -}) -``` - -### Provider Testing - -```typescript -describe('ThemeProvider', () => { - it('provides theme context to children', () => { - const TestComponent = () => { - const { theme } = useTheme() - return
{theme}
- } - - render( - - - - ) - - expect(screen.getByTestId('theme')).toHaveTextContent('light') - }) -}) -``` - -### Hook Testing - -```typescript -describe('useCounter', () => { - it('increments count', () => { - const { result } = renderHook(() => useCounter()) - - act(() => { - result.current.increment() - }) - - expect(result.current.count).toBe(1) - }) -}) -``` - -### API Route Testing - -```typescript -describe('API Routes', () => { - it('returns valid response', async () => { - const request = new NextRequest('http://localhost/api/test?param=value') - const response = await GET(request) - const data = await response.json() - - expect(response.status).toBe(200) - expect(data).toHaveProperty('result') - }) -}) -``` - -## ๐Ÿ”ง Configuration - -### Vitest Config Highlights - -- **Environment**: jsdom for DOM testing -- **Setup files**: Automatic mock setup -- **Coverage**: v8 provider with HTML reports -- **Path aliases**: `@/` mapped to project root -- **CSS support**: Enabled for styling tests - -### Mocks Included - -#### Next.js Components -- โœ… `next/image` โ†’ Simple `` tag -- โœ… `next/link` โ†’ Simple `` tag -- โœ… `next/script` โ†’ Simple `