> ## Documentation Index
> Fetch the complete documentation index at: https://docs.weventures.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing & Quality

> Comprehensive testing strategies with Playwright, Vitest, and modern QA practices

# 🧪 Comprehensive Testing Strategy

<Card title="Testing Pyramid from Discovery Requirements" icon="flask">
  **E2E (10%):** Critical user journeys from discovery phase

  **Integration (30%):** API and service integration tests

  **Unit (60%):** Business logic and utility functions
</Card>

## Modern Testing Stack

<CardGroup cols={3}>
  <Card title="Playwright" icon="browser" color="#45BA4B">
    E2E testing with multi-browser support
  </Card>

  <Card title="Vitest" icon="zap" color="#FCC72C">
    Lightning-fast unit testing
  </Card>

  <Card title="MSW" icon="network-wired" color="#FF6B6B">
    API mocking for integration tests
  </Card>
</CardGroup>

## E2E Testing with Playwright

<Tabs>
  <Tab title="Critical Path Tests">
    <Steps>
      <Step title="Setup Playwright">
        ```bash theme={null}
        # Install Playwright with all browsers
        pnpm add -D @playwright/test
        pnpm exec playwright install

        # Configure playwright.config.ts
        ```

        ```typescript theme={null}
        // playwright.config.ts
        import { defineConfig, devices } from '@playwright/test'

        export default defineConfig({
          testDir: './e2e',
          fullyParallel: true,
          forbidOnly: !!process.env.CI,
          retries: process.env.CI ? 2 : 0,
          workers: process.env.CI ? 1 : undefined,
          reporter: 'html',

          use: {
            baseURL: 'http://localhost:3000',
            trace: 'on-first-retry',
            screenshot: 'only-on-failure',
            video: 'retain-on-failure',
          },

          projects: [
            {
              name: 'chromium',
              use: { ...devices['Desktop Chrome'] },
            },
            {
              name: 'firefox',
              use: { ...devices['Desktop Firefox'] },
            },
            {
              name: 'webkit',
              use: { ...devices['Desktop Safari'] },
            },
            {
              name: 'Mobile Chrome',
              use: { ...devices['Pixel 5'] },
            },
            {
              name: 'Mobile Safari',
              use: { ...devices['iPhone 12'] },
            },
          ],

          webServer: {
            command: 'pnpm dev',
            url: 'http://localhost:3000',
            reuseExistingServer: !process.env.CI,
          },
        })
        ```
      </Step>

      <Step title="User Journey Tests">
        ```typescript theme={null}
        // e2e/critical-paths.spec.ts
        import { test, expect } from '@playwright/test'

        // From user journey mapping in discovery
        test.describe('User Onboarding Flow', () => {
          test('new user can complete full onboarding', async ({ page }) => {
            // 1. Landing page
            await page.goto('/')
            await expect(page).toHaveTitle(/Welcome/)

            // Visual regression test
            await expect(page).toHaveScreenshot('landing-page.png')

            // 2. Sign up
            await page.click('[data-testid="signup-button"]')
            await page.fill('[name="email"]', 'test@example.com')
            await page.fill('[name="password"]', 'SecurePass123!')
            await page.click('[type="submit"]')

            // 3. Email verification (mock in test env)
            await page.goto('/verify-email?token=test-token')

            // 4. Profile setup
            await page.fill('[name="fullName"]', 'Test User')
            await page.fill('[name="company"]', 'Test Corp')
            await page.click('[data-testid="continue"]')

            // 5. Onboarding tour
            await expect(page.locator('[data-tour="step-1"]')).toBeVisible()
            await page.click('[data-testid="skip-tour"]')

            // 6. Dashboard
            await expect(page).toHaveURL('/dashboard')
            await expect(page.locator('h1')).toContainText('Welcome, Test User')

            // Performance metrics
            const metrics = await page.evaluate(() => performance.getEntriesByType('navigation'))
            expect(metrics[0].loadEventEnd).toBeLessThan(3000)
          })

          test('existing user can sign in', async ({ page }) => {
            await page.goto('/signin')
            await page.fill('[name="email"]', 'existing@example.com')
            await page.fill('[name="password"]', 'password')
            await page.click('[type="submit"]')
            await expect(page).toHaveURL('/dashboard')
          })
        })
        ```
      </Step>

      <Step title="Accessibility Testing">
        ```typescript theme={null}
        // e2e/accessibility.spec.ts
        import { test, expect } from '@playwright/test'
        import AxeBuilder from '@axe-core/playwright'

        test.describe('Accessibility', () => {
          test('homepage should have no violations', async ({ page }) => {
            await page.goto('/')

            const accessibilityScanResults = await new AxeBuilder({ page })
              .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
              .analyze()

            expect(accessibilityScanResults.violations).toEqual([])
          })

          test('keyboard navigation works', async ({ page }) => {
            await page.goto('/')

            // Tab through interactive elements
            await page.keyboard.press('Tab')
            const firstFocused = await page.evaluate(() => document.activeElement?.tagName)
            expect(firstFocused).toBeTruthy()

            // Check skip to content link
            await page.keyboard.press('Enter')
            const mainContent = await page.locator('main')
            await expect(mainContent).toBeFocused()
          })

          test('screen reader compatibility', async ({ page }) => {
            await page.goto('/')

            // Check ARIA labels
            const buttons = await page.getByRole('button').all()
            for (const button of buttons) {
              const ariaLabel = await button.getAttribute('aria-label')
              const text = await button.textContent()
              expect(ariaLabel || text).toBeTruthy()
            }

            // Check heading hierarchy
            const headings = await page.evaluate(() => {
              return Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6'))
                .map(h => ({ level: parseInt(h.tagName[1]), text: h.textContent }))
            })

            // Verify proper heading structure
            let previousLevel = 0
            for (const heading of headings) {
              expect(heading.level - previousLevel).toBeLessThanOrEqual(1)
              previousLevel = heading.level
            }
          })
        })
        ```
      </Step>

      <Step title="Performance Testing">
        ```typescript theme={null}
        // e2e/performance.spec.ts
        import { test, expect } from '@playwright/test'

        test.describe('Performance Metrics', () => {
          test('meets Core Web Vitals', async ({ page }) => {
            await page.goto('/')

            // Measure Core Web Vitals
            const metrics = await page.evaluate(() => {
              return new Promise((resolve) => {
                let lcp, fid, cls

                // Largest Contentful Paint
                new PerformanceObserver((list) => {
                  const entries = list.getEntries()
                  lcp = entries[entries.length - 1].renderTime || entries[entries.length - 1].loadTime
                }).observe({ entryTypes: ['largest-contentful-paint'] })

                // First Input Delay (simulated)
                addEventListener('click', (e) => {
                  fid = performance.now() - e.timeStamp
                }, { once: true })

                // Cumulative Layout Shift
                let clsValue = 0
                new PerformanceObserver((list) => {
                  for (const entry of list.getEntries()) {
                    if (!entry.hadRecentInput) {
                      clsValue += entry.value
                    }
                  }
                  cls = clsValue
                }).observe({ entryTypes: ['layout-shift'] })

                setTimeout(() => {
                  resolve({ lcp, fid, cls })
                }, 5000)
              })
            })

            // Assert against targets from discovery
            expect(metrics.lcp).toBeLessThan(2500) // Good LCP
            expect(metrics.cls).toBeLessThan(0.1) // Good CLS
          })

          test('bundle size check', async ({ page }) => {
            const response = await page.goto('/')
            const resources = await page.evaluate(() =>
              performance.getEntriesByType('resource')
                .filter(r => r.name.includes('.js'))
                .reduce((acc, r) => acc + r.transferSize, 0)
            )

            // JavaScript budget from discovery
            expect(resources).toBeLessThan(200 * 1024) // 200KB
          })
        })
        ```
      </Step>
    </Steps>
  </Tab>

  <Tab title="Mobile Testing">
    ```typescript theme={null}
    // e2e/mobile.spec.ts
    import { test, expect, devices } from '@playwright/test'

    // Test on devices from discovery audience analysis
    const devicesToTest = [
      { name: 'iPhone 12', device: devices['iPhone 12'] },
      { name: 'iPhone SE', device: devices['iPhone SE'] },
      { name: 'Pixel 5', device: devices['Pixel 5'] },
      { name: 'Galaxy S9+', device: devices['Galaxy S9+'] },
      { name: 'iPad (gen 7)', device: devices['iPad (gen 7)'] },
      { name: 'iPad Pro 11', device: devices['iPad Pro 11'] },
    ]

    devicesToTest.forEach(({ name, device }) => {
      test.describe(`Mobile: ${name}`, () => {
        test.use({ ...device })

        test('responsive navigation works', async ({ page }) => {
          await page.goto('/')

          // Mobile menu should be visible
          await expect(page.locator('[data-testid="mobile-menu"]')).toBeVisible()

          // Desktop menu should be hidden
          await expect(page.locator('[data-testid="desktop-menu"]')).toBeHidden()

          // Test mobile interactions
          await page.click('[data-testid="mobile-menu"]')
          await expect(page.locator('[data-testid="mobile-nav"]')).toBeVisible()

          // Test touch gestures
          await page.locator('[data-testid="carousel"]').swipe({
            direction: 'left',
            distance: 100
          })
        })

        test('viewport and orientation', async ({ page }) => {
          await page.goto('/')

          // Test portrait mode
          await page.setViewportSize({ width: 375, height: 812 })
          await expect(page.locator('.mobile-layout')).toBeVisible()

          // Test landscape mode
          await page.setViewportSize({ width: 812, height: 375 })
          await expect(page.locator('.landscape-layout')).toBeVisible()
        })

        test('touch interactions', async ({ page }) => {
          await page.goto('/interactive')

          // Test tap
          await page.tap('[data-testid="touch-button"]')

          // Test long press
          await page.locator('[data-testid="long-press"]').press('Enter', { delay: 1000 })

          // Test pinch zoom
          await page.evaluate(() => {
            const event = new TouchEvent('gesturestart', {
              scale: 2
            })
            document.dispatchEvent(event)
          })
        })
      })
    })
    ```
  </Tab>

  <Tab title="Cross-Browser Testing">
    ```typescript theme={null}
    // e2e/cross-browser.spec.ts
    import { test, expect, Browser, chromium, firefox, webkit } from '@playwright/test'

    const browsers = [
      { name: 'Chrome', launch: chromium },
      { name: 'Firefox', launch: firefox },
      { name: 'Safari', launch: webkit },
    ]

    browsers.forEach(({ name, launch }) => {
      test.describe(`Cross-browser: ${name}`, () => {
        let browser: Browser

        test.beforeAll(async () => {
          browser = await launch()
        })

        test.afterAll(async () => {
          await browser.close()
        })

        test('CSS features support', async () => {
          const page = await browser.newPage()
          await page.goto('/')

          // Test CSS Grid support
          const gridSupport = await page.evaluate(() => {
            return CSS.supports('display', 'grid')
          })
          expect(gridSupport).toBe(true)

          // Test CSS custom properties
          const customPropsSupport = await page.evaluate(() => {
            return CSS.supports('--custom', 'value')
          })
          expect(customPropsSupport).toBe(true)

          // Test modern CSS features
          const features = [
            'aspect-ratio: 16 / 9',
            'container-type: inline-size',
            'color: oklch(0.5 0.5 0)',
          ]

          for (const feature of features) {
            const [property, value] = feature.split(':')
            const supported = await page.evaluate(([p, v]) => {
              return CSS.supports(p, v.trim())
            }, [property, value])

            if (!supported) {
              console.warn(`${name} doesn't support ${feature}`)
            }
          }
        })

        test('JavaScript API compatibility', async () => {
          const page = await browser.newPage()
          await page.goto('/')

          // Test modern JavaScript APIs
          const apis = await page.evaluate(() => {
            return {
              intersectionObserver: 'IntersectionObserver' in window,
              resizeObserver: 'ResizeObserver' in window,
              webComponents: 'customElements' in window,
              webWorkers: 'Worker' in window,
              serviceWorker: 'serviceWorker' in navigator,
              webGL: 'WebGLRenderingContext' in window,
              webRTC: 'RTCPeerConnection' in window,
            }
          })

          // All modern browsers should support these
          expect(apis.intersectionObserver).toBe(true)
          expect(apis.resizeObserver).toBe(true)
          expect(apis.webComponents).toBe(true)
        })
      })
    })
    ```
  </Tab>
</Tabs>

## Integration Testing

<Tabs>
  <Tab title="API Testing">
    <CodeGroup>
      ```typescript API Testing with MSW theme={null}
      // __tests__/api/users.test.ts
      import { setupServer } from 'msw/node'
      import { http, HttpResponse } from 'msw'
      import { describe, it, expect, beforeAll, afterAll } from 'vitest'
      import { createTestClient } from '@/tests/utils'

      const server = setupServer(
        http.post('/api/users', async ({ request }) => {
          const body = await request.json()

          // Validate request
          if (!body.email || !body.password) {
            return HttpResponse.json(
              { error: 'Missing required fields' },
              { status: 400 }
            )
          }

          return HttpResponse.json({
            id: '123',
            email: body.email,
            createdAt: new Date().toISOString()
          }, { status: 201 })
        })
      )

      beforeAll(() => server.listen())
      afterAll(() => server.close())

      describe('User API', () => {
        const client = createTestClient()

        describe('POST /api/users', () => {
          it('creates user with valid data', async () => {
            const userData = {
              email: 'new@example.com',
              password: 'SecurePass123!',
              fullName: 'New User'
            }

            const response = await client.post('/api/users', userData)

            expect(response.status).toBe(201)
            expect(response.data).toHaveProperty('id')
            expect(response.data.email).toBe(userData.email)
          })

          it('validates email format', async () => {
            const response = await client.post('/api/users', {
              email: 'invalid-email',
              password: 'password'
            })

            expect(response.status).toBe(400)
            expect(response.data.error).toContain('email')
          })

          it('enforces password requirements from discovery', async () => {
            const response = await client.post('/api/users', {
              email: 'test@example.com',
              password: '123' // Too short
            })

            expect(response.status).toBe(400)
            expect(response.data.error).toContain('password')
          })
        })

        describe('Rate limiting', () => {
          it('enforces rate limits', async () => {
            const requests = Array(100).fill(null).map(() =>
              client.get('/api/users')
            )

            const responses = await Promise.all(requests)
            const rateLimited = responses.filter(r => r.status === 429)

            expect(rateLimited.length).toBeGreaterThan(0)
          })
        })
      })
      ```

      ```typescript WebSocket Testing theme={null}
      // __tests__/websocket/realtime.test.ts
      import { describe, it, expect, beforeAll, afterAll } from 'vitest'
      import { WebSocket, Server } from 'mock-socket'
      import { RealtimeClient } from '@/lib/realtime'

      describe('Realtime WebSocket', () => {
        let mockServer: Server
        let client: RealtimeClient

        beforeAll(() => {
          mockServer = new Server('ws://localhost:8080')
          mockServer.on('connection', socket => {
            socket.on('message', data => {
              // Echo back for testing
              socket.send(data)
            })
          })
        })

        afterAll(() => {
          mockServer.close()
        })

        it('connects successfully', async () => {
          client = new RealtimeClient('ws://localhost:8080')
          await client.connect()

          expect(client.isConnected()).toBe(true)
        })

        it('handles reconnection', async () => {
          client = new RealtimeClient('ws://localhost:8080')
          await client.connect()

          // Simulate disconnect
          mockServer.close()
          await new Promise(r => setTimeout(r, 100))

          // Should attempt reconnect
          mockServer = new Server('ws://localhost:8080')
          await new Promise(r => setTimeout(r, 1000))

          expect(client.isConnected()).toBe(true)
        })

        it('receives real-time updates', async () => {
          client = new RealtimeClient('ws://localhost:8080')
          await client.connect()

          const received: any[] = []
          client.on('message', (data) => {
            received.push(data)
          })

          client.send({ type: 'test', payload: 'data' })

          await new Promise(r => setTimeout(r, 100))

          expect(received).toHaveLength(1)
          expect(received[0]).toMatchObject({
            type: 'test',
            payload: 'data'
          })
        })
      })
      ```

      ```typescript GraphQL Testing theme={null}
      // __tests__/graphql/queries.test.ts
      import { graphql } from 'msw'
      import { setupServer } from 'msw/node'
      import { describe, it, expect } from 'vitest'
      import { client } from '@/lib/graphql'
      import { GET_USER, CREATE_USER } from '@/graphql/queries'

      const server = setupServer(
        graphql.query('GetUser', (req, res, ctx) => {
          return res(
            ctx.data({
              user: {
                id: req.variables.id,
                name: 'Test User',
                email: 'test@example.com'
              }
            })
          )
        }),

        graphql.mutation('CreateUser', (req, res, ctx) => {
          return res(
            ctx.data({
              createUser: {
                id: '123',
                ...req.variables.input
              }
            })
          )
        })
      )

      describe('GraphQL Operations', () => {
        it('fetches user data', async () => {
          const result = await client.query({
            query: GET_USER,
            variables: { id: '123' }
          })

          expect(result.data.user).toMatchObject({
            id: '123',
            name: 'Test User'
          })
        })

        it('creates new user', async () => {
          const result = await client.mutate({
            mutation: CREATE_USER,
            variables: {
              input: {
                name: 'New User',
                email: 'new@example.com'
              }
            }
          })

          expect(result.data.createUser).toMatchObject({
            id: '123',
            name: 'New User'
          })
        })
      })
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Database Testing">
    ```typescript theme={null}
    // __tests__/db/repository.test.ts
    import { describe, it, expect, beforeEach, afterEach } from 'vitest'
    import { PrismaClient } from '@prisma/client'
    import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended'
    import { UserRepository } from '@/repositories/user'

    // Mock Prisma for unit tests
    export const prismaMock = mockDeep<PrismaClient>() as DeepMockProxy<PrismaClient>

    describe('UserRepository', () => {
      let repo: UserRepository

      beforeEach(() => {
        mockReset(prismaMock)
        repo = new UserRepository(prismaMock)
      })

      describe('Unit Tests with Mocked DB', () => {
        it('creates user with profile', async () => {
          const userData = {
            email: 'test@example.com',
            password: 'hashed',
            profile: {
              fullName: 'Test User',
              role: 'user'
            }
          }

          prismaMock.user.create.mockResolvedValue({
            id: '123',
            email: userData.email,
            createdAt: new Date(),
            updatedAt: new Date(),
            profile: {
              id: '456',
              userId: '123',
              fullName: userData.profile.fullName,
              role: userData.profile.role
            }
          })

          const user = await repo.createWithProfile(userData)

          expect(user).toBeDefined()
          expect(user.profile).toBeDefined()
          expect(user.profile.fullName).toBe('Test User')
        })

        it('handles unique constraint violations', async () => {
          prismaMock.user.create.mockRejectedValue(
            new Error('Unique constraint failed on the fields: (`email`)')
          )

          await expect(
            repo.create({ email: 'existing@example.com' })
          ).rejects.toThrow('unique constraint')
        })
      })

      describe('Integration Tests with Real DB', () => {
        // Use test database for integration tests
        const testPrisma = new PrismaClient({
          datasources: {
            db: {
              url: process.env.TEST_DATABASE_URL
            }
          }
        })

        beforeEach(async () => {
          // Clean database before each test
          await testPrisma.$executeRaw`TRUNCATE TABLE users CASCADE`
        })

        afterEach(async () => {
          await testPrisma.$disconnect()
        })

        it('performs transactions correctly', async () => {
          const repo = new UserRepository(testPrisma)

          const result = await repo.createWithProfileTransaction({
            email: 'transaction@test.com',
            profile: {
              fullName: 'Transaction Test'
            }
          })

          // Verify both user and profile were created
          const user = await testPrisma.user.findUnique({
            where: { email: 'transaction@test.com' },
            include: { profile: true }
          })

          expect(user).toBeDefined()
          expect(user?.profile).toBeDefined()
          expect(user?.profile?.fullName).toBe('Transaction Test')
        })

        it('rolls back on transaction failure', async () => {
          const repo = new UserRepository(testPrisma)

          // This should fail and rollback
          await expect(
            repo.createWithProfileTransaction({
              email: null, // Invalid
              profile: { fullName: 'Test' }
            })
          ).rejects.toThrow()

          // Verify nothing was created
          const count = await testPrisma.user.count()
          expect(count).toBe(0)
        })
      })
    })
    ```
  </Tab>

  <Tab title="Service Testing">
    ```typescript theme={null}
    // __tests__/services/auth.test.ts
    import { describe, it, expect, vi, beforeEach } from 'vitest'
    import { AuthService } from '@/services/auth'
    import { EmailService } from '@/services/email'
    import { TokenService } from '@/services/token'
    import { UserRepository } from '@/repositories/user'

    // Mock dependencies
    vi.mock('@/services/email')
    vi.mock('@/services/token')
    vi.mock('@/repositories/user')

    describe('AuthService', () => {
      let authService: AuthService
      let emailService: EmailService
      let tokenService: TokenService
      let userRepo: UserRepository

      beforeEach(() => {
        emailService = new EmailService()
        tokenService = new TokenService()
        userRepo = new UserRepository()
        authService = new AuthService(userRepo, emailService, tokenService)

        vi.clearAllMocks()
      })

      describe('signup', () => {
        it('creates user and sends verification email', async () => {
          const signupData = {
            email: 'new@example.com',
            password: 'SecurePass123!',
            name: 'New User'
          }

          vi.mocked(userRepo.findByEmail).mockResolvedValue(null)
          vi.mocked(userRepo.create).mockResolvedValue({
            id: '123',
            email: signupData.email
          })
          vi.mocked(tokenService.generateVerificationToken).mockReturnValue('token123')
          vi.mocked(emailService.sendVerificationEmail).mockResolvedValue(true)

          const result = await authService.signup(signupData)

          expect(result.user).toBeDefined()
          expect(result.user.email).toBe(signupData.email)
          expect(emailService.sendVerificationEmail).toHaveBeenCalledWith(
            signupData.email,
            'token123'
          )
        })

        it('prevents duplicate signups', async () => {
          vi.mocked(userRepo.findByEmail).mockResolvedValue({
            id: '123',
            email: 'existing@example.com'
          })

          await expect(
            authService.signup({
              email: 'existing@example.com',
              password: 'password'
            })
          ).rejects.toThrow('already exists')
        })

        it('validates password strength', async () => {
          await expect(
            authService.signup({
              email: 'test@example.com',
              password: 'weak'
            })
          ).rejects.toThrow('password requirements')
        })
      })

      describe('login', () => {
        it('authenticates valid credentials', async () => {
          const user = {
            id: '123',
            email: 'user@example.com',
            password: '$2b$10$hashedpassword'
          }

          vi.mocked(userRepo.findByEmail).mockResolvedValue(user)
          vi.mocked(tokenService.generateAccessToken).mockReturnValue('access123')
          vi.mocked(tokenService.generateRefreshToken).mockReturnValue('refresh123')

          const result = await authService.login(
            'user@example.com',
            'correctpassword'
          )

          expect(result.accessToken).toBe('access123')
          expect(result.refreshToken).toBe('refresh123')
          expect(result.user).toBeDefined()
        })

        it('rejects invalid credentials', async () => {
          vi.mocked(userRepo.findByEmail).mockResolvedValue(null)

          await expect(
            authService.login('wrong@example.com', 'password')
          ).rejects.toThrow('Invalid credentials')
        })

        it('handles rate limiting', async () => {
          // Simulate multiple failed attempts
          for (let i = 0; i < 5; i++) {
            await expect(
              authService.login('user@example.com', 'wrong')
            ).rejects.toThrow()
          }

          // Next attempt should be rate limited
          await expect(
            authService.login('user@example.com', 'correct')
          ).rejects.toThrow('Too many attempts')
        })
      })
    })
    ```
  </Tab>
</Tabs>

## Unit Testing with Vitest

<Tabs>
  <Tab title="Component Testing">
    ```typescript theme={null}
    // __tests__/components/Button.test.tsx
    import { describe, it, expect, vi } from 'vitest'
    import { render, screen, fireEvent } from '@testing-library/react'
    import { Button } from '@/components/ui/button'

    describe('Button Component', () => {
      it('renders with correct text', () => {
        render(<Button>Click me</Button>)
        expect(screen.getByText('Click me')).toBeInTheDocument()
      })

      it('handles click events', () => {
        const handleClick = vi.fn()
        render(<Button onClick={handleClick}>Click me</Button>)

        fireEvent.click(screen.getByText('Click me'))
        expect(handleClick).toHaveBeenCalledTimes(1)
      })

      it('shows loading state', () => {
        render(<Button loading>Submit</Button>)
        expect(screen.getByTestId('spinner')).toBeInTheDocument()
        expect(screen.getByText('Submit')).toHaveAttribute('disabled')
      })

      it('applies variant styles', () => {
        const { rerender } = render(<Button variant="primary">Button</Button>)
        expect(screen.getByText('Button')).toHaveClass('bg-primary')

        rerender(<Button variant="secondary">Button</Button>)
        expect(screen.getByText('Button')).toHaveClass('bg-secondary')

        rerender(<Button variant="ghost">Button</Button>)
        expect(screen.getByText('Button')).toHaveClass('hover:bg-accent')
      })

      it('handles disabled state', () => {
        const handleClick = vi.fn()
        render(<Button disabled onClick={handleClick}>Disabled</Button>)

        const button = screen.getByText('Disabled')
        expect(button).toBeDisabled()

        fireEvent.click(button)
        expect(handleClick).not.toHaveBeenCalled()
      })
    })
    ```
  </Tab>

  <Tab title="Hook Testing">
    ```typescript theme={null}
    // __tests__/hooks/useAuth.test.tsx
    import { describe, it, expect, vi, beforeEach } from 'vitest'
    import { renderHook, act, waitFor } from '@testing-library/react'
    import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
    import { useAuth } from '@/hooks/useAuth'

    const createWrapper = () => {
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: { retry: false },
        },
      })

      return ({ children }) => (
        <QueryClientProvider client={queryClient}>
          {children}
        </QueryClientProvider>
      )
    }

    describe('useAuth Hook', () => {
      beforeEach(() => {
        vi.clearAllMocks()
        localStorage.clear()
      })

      it('initializes with unauthenticated state', () => {
        const { result } = renderHook(() => useAuth(), {
          wrapper: createWrapper()
        })

        expect(result.current.isAuthenticated).toBe(false)
        expect(result.current.user).toBeNull()
        expect(result.current.isLoading).toBe(true)
      })

      it('handles successful login', async () => {
        const { result } = renderHook(() => useAuth(), {
          wrapper: createWrapper()
        })

        await act(async () => {
          await result.current.login('user@example.com', 'password')
        })

        await waitFor(() => {
          expect(result.current.isAuthenticated).toBe(true)
          expect(result.current.user).toMatchObject({
            email: 'user@example.com'
          })
          expect(localStorage.getItem('token')).toBeTruthy()
        })
      })

      it('handles login failure', async () => {
        const { result } = renderHook(() => useAuth(), {
          wrapper: createWrapper()
        })

        await act(async () => {
          await expect(
            result.current.login('invalid@example.com', 'wrong')
          ).rejects.toThrow('Invalid credentials')
        })

        expect(result.current.isAuthenticated).toBe(false)
        expect(result.current.user).toBeNull()
        expect(localStorage.getItem('token')).toBeNull()
      })

      it('persists authentication across renders', async () => {
        localStorage.setItem('token', 'valid-token')

        const { result } = renderHook(() => useAuth(), {
          wrapper: createWrapper()
        })

        await waitFor(() => {
          expect(result.current.isAuthenticated).toBe(true)
          expect(result.current.user).toBeTruthy()
        })
      })

      it('handles logout', async () => {
        const { result } = renderHook(() => useAuth(), {
          wrapper: createWrapper()
        })

        // Login first
        await act(async () => {
          await result.current.login('user@example.com', 'password')
        })

        // Then logout
        await act(async () => {
          await result.current.logout()
        })

        expect(result.current.isAuthenticated).toBe(false)
        expect(result.current.user).toBeNull()
        expect(localStorage.getItem('token')).toBeNull()
      })

      it('refreshes token automatically', async () => {
        vi.useFakeTimers()

        const { result } = renderHook(() => useAuth(), {
          wrapper: createWrapper()
        })

        await act(async () => {
          await result.current.login('user@example.com', 'password')
        })

        // Fast forward to token refresh time
        await act(async () => {
          vi.advanceTimersByTime(1000 * 60 * 55) // 55 minutes
        })

        await waitFor(() => {
          // Token should be refreshed
          expect(result.current.isAuthenticated).toBe(true)
        })

        vi.useRealTimers()
      })
    })
    ```
  </Tab>

  <Tab title="Utility Testing">
    ```typescript theme={null}
    // __tests__/utils/validation.test.ts
    import { describe, it, expect } from 'vitest'
    import {
      validateEmail,
      validatePassword,
      validatePhone,
      validateCreditCard,
      validatePostalCode
    } from '@/utils/validation'

    describe('Validation Utilities', () => {
      describe('validateEmail', () => {
        it('accepts valid emails', () => {
          const validEmails = [
            'user@example.com',
            'user+tag@example.co.uk',
            'user.name@subdomain.example.com',
            'user_123@test-domain.org'
          ]

          validEmails.forEach(email => {
            expect(validateEmail(email)).toBe(true)
          })
        })

        it('rejects invalid emails', () => {
          const invalidEmails = [
            'notanemail',
            '@example.com',
            'user@',
            'user @example.com',
            'user@.com',
            'user@example'
          ]

          invalidEmails.forEach(email => {
            expect(validateEmail(email)).toBe(false)
          })
        })
      })

      describe('validatePassword', () => {
        it('enforces requirements from security discovery', () => {
          // Requirements: 8+ chars, uppercase, lowercase, number, special
          expect(validatePassword('ValidPass123!')).toBe(true)
          expect(validatePassword('password')).toBe(false) // no uppercase
          expect(validatePassword('PASSWORD')).toBe(false) // no lowercase
          expect(validatePassword('Password')).toBe(false) // no number
          expect(validatePassword('Password1')).toBe(false) // no special
          expect(validatePassword('Pass1!')).toBe(false) // too short
        })

        it('checks for common passwords', () => {
          const commonPasswords = [
            'password123!',
            'Admin123!',
            'Qwerty123!',
            'Password123!'
          ]

          commonPasswords.forEach(password => {
            expect(validatePassword(password, { checkCommon: true })).toBe(false)
          })
        })
      })

      describe('validatePhone', () => {
        it('validates international phone numbers', () => {
          const validPhones = [
            '+1-555-555-5555',
            '+44 20 7123 1234',
            '+33 1 42 68 53 00',
            '+49 30 12345678',
            '+81 3-1234-5678',
            '+86 10 1234 5678'
          ]

          validPhones.forEach(phone => {
            expect(validatePhone(phone)).toBe(true)
          })
        })

        it('validates US phone numbers', () => {
          const validUSPhones = [
            '(555) 555-5555',
            '555-555-5555',
            '555.555.5555',
            '5555555555'
          ]

          validUSPhones.forEach(phone => {
            expect(validatePhone(phone, { country: 'US' })).toBe(true)
          })
        })
      })

      describe('validateCreditCard', () => {
        it('validates card numbers using Luhn algorithm', () => {
          const validCards = [
            '4532015112830366', // Visa
            '5425233430109903', // MasterCard
            '374245455400126',  // Amex
            '6011000991300009'  // Discover
          ]

          validCards.forEach(card => {
            expect(validateCreditCard(card)).toBe(true)
          })
        })

        it('identifies card types', () => {
          expect(validateCreditCard('4532015112830366')).toMatchObject({
            valid: true,
            type: 'visa'
          })

          expect(validateCreditCard('5425233430109903')).toMatchObject({
            valid: true,
            type: 'mastercard'
          })

          expect(validateCreditCard('374245455400126')).toMatchObject({
            valid: true,
            type: 'amex'
          })
        })
      })

      describe('validatePostalCode', () => {
        it('validates postal codes by country', () => {
          // US ZIP codes
          expect(validatePostalCode('12345', 'US')).toBe(true)
          expect(validatePostalCode('12345-6789', 'US')).toBe(true)

          // UK postcodes
          expect(validatePostalCode('SW1A 1AA', 'UK')).toBe(true)
          expect(validatePostalCode('EC1A 1BB', 'UK')).toBe(true)

          // Canadian postal codes
          expect(validatePostalCode('K1A 0B1', 'CA')).toBe(true)
          expect(validatePostalCode('M5W 1E6', 'CA')).toBe(true)
        })
      })
    })
    ```
  </Tab>
</Tabs>

## Test Coverage & Quality

<Card title="Coverage Requirements" icon="chart-line">
  Based on discovery phase requirements:

  * **Unit Tests:** 80% coverage minimum
  * **Integration Tests:** Critical paths covered
  * **E2E Tests:** User journeys from discovery
  * **Performance Tests:** Meet Core Web Vitals
  * **Security Tests:** OWASP Top 10 covered
</Card>

<CodeGroup>
  ```json vitest.config.ts theme={null}
  import { defineConfig } from 'vitest/config'
  import react from '@vitejs/plugin-react'
  import path from 'path'

  export default defineConfig({
    plugins: [react()],
    test: {
      globals: true,
      environment: 'jsdom',
      setupFiles: './tests/setup.ts',
      coverage: {
        provider: 'v8',
        reporter: ['text', 'json', 'html', 'lcov'],
        exclude: [
          'node_modules/',
          'tests/',
          '*.config.ts',
          '**/*.d.ts',
          '**/*.test.tsx',
          '**/*.spec.tsx'
        ],
        thresholds: {
          branches: 80,
          functions: 80,
          lines: 80,
          statements: 80
        }
      },
      mockReset: true,
      restoreMocks: true,
      include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
      watchExclude: ['node_modules', 'dist', 'coverage']
    },
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src'),
      },
    },
  })
  ```

  ```yaml .github/workflows/test.yml theme={null}
  name: Test Suite

  on:
    push:
      branches: [main, develop]
    pull_request:
      branches: [main, develop]

  jobs:
    test:
      runs-on: ubuntu-latest

      services:
        postgres:
          image: postgres:15
          env:
            POSTGRES_PASSWORD: postgres
          options: >-
            --health-cmd pg_isready
            --health-interval 10s
            --health-timeout 5s
            --health-retries 5
          ports:
            - 5432:5432

      steps:
        - uses: actions/checkout@v4

        - uses: pnpm/action-setup@v2
          with:
            version: 8

        - uses: actions/setup-node@v4
          with:
            node-version: '20'
            cache: 'pnpm'

        - name: Install dependencies
          run: pnpm install --frozen-lockfile

        - name: Run unit tests
          run: pnpm test:unit --coverage

        - name: Run integration tests
          env:
            DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
          run: |
            pnpm prisma migrate deploy
            pnpm test:integration

        - name: Run E2E tests
          run: |
            pnpm build
            pnpm test:e2e

        - name: Upload coverage
          uses: codecov/codecov-action@v3
          with:
            files: ./coverage/lcov.info

        - name: Upload test results
          if: always()
          uses: actions/upload-artifact@v3
          with:
            name: test-results
            path: |
              coverage/
              playwright-report/
              test-results/
  ```
</CodeGroup>

***

<Card title="Next Step" icon="arrow-right" href="/ai-development/prompting/deployment">
  With comprehensive testing in place, proceed to deployment strategies →
</Card>
