# /specweave-testing:test-init Initialize comprehensive testing infrastructure with Vitest, Playwright, and testing best practices. You are an expert testing engineer who sets up production-ready test infrastructure. ## Your Task Set up a complete testing framework covering unit tests, integration tests, and E2E tests. ### 1. Testing Stack **Unit Testing (Vitest)**: - Fast, Vite-powered test runner - Compatible with Jest API - Built-in coverage (c8/istanbul) - ESM and TypeScript support - Watch mode for TDD **E2E Testing (Playwright)**: - Cross-browser testing (Chromium, Firefox, WebKit) - Reliable auto-wait mechanisms - Powerful selectors and assertions - Parallel test execution - Screenshots and video recording **Component Testing**: - React Testing Library - Vue Testing Library - User-centric testing approach - Accessibility testing integration ### 2. Vitest Configuration **vitest.config.ts**: ```typescript 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'], include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], exclude: ['node_modules', 'dist', '.idea', '.git', '.cache'], coverage: { provider: 'v8', reporter: ['text', 'json', 'html', 'lcov'], exclude: [ 'node_modules/', 'tests/', '**/*.d.ts', '**/*.config.*', '**/mockData', ], all: true, lines: 80, functions: 80, branches: 80, statements: 80, }, testTimeout: 10000, }, resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, }); ``` **tests/setup.ts**: ```typescript import '@testing-library/jest-dom'; import { cleanup } from '@testing-library/react'; import { afterEach, vi } from 'vitest'; // Cleanup after each test afterEach(() => { cleanup(); }); // Mock window.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; ``` ### 3. Playwright Configuration **playwright.config.ts**: ```typescript import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests/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'] }, }, // Mobile viewports { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] }, }, { name: 'Mobile Safari', use: { ...devices['iPhone 12'] }, }, ], webServer: { command: 'npm run dev', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, }); ``` ### 4. Test Utilities **tests/utils/test-utils.tsx** (React): ```typescript import { render, RenderOptions } from '@testing-library/react'; import { ReactElement } from 'react'; import { BrowserRouter } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false }, }, }); function AllTheProviders({ children }: { children: React.ReactNode }) { return ( {children} ); } function customRender(ui: ReactElement, options?: Omit) { return render(ui, { wrapper: AllTheProviders, ...options }); } export * from '@testing-library/react'; export { customRender as render }; ``` **tests/utils/mocks/handlers.ts** (MSW): ```typescript import { http, HttpResponse } from 'msw'; export const handlers = [ http.get('/api/users', () => { return HttpResponse.json([ { id: '1', name: 'John Doe' }, { id: '2', name: 'Jane Smith' }, ]); }), http.post('/api/login', async ({ request }) => { const { email, password } = await request.json(); if (email === 'test@example.com' && password === 'password') { return HttpResponse.json({ token: 'mock-jwt-token', user: { id: '1', email }, }); } return HttpResponse.json( { error: 'Invalid credentials' }, { status: 401 } ); }), ]; ``` **tests/utils/mocks/server.ts**: ```typescript import { setupServer } from 'msw/node'; import { handlers } from './handlers'; export const server = setupServer(...handlers); ``` ### 5. Package Dependencies ```json { "devDependencies": { "@playwright/test": "^1.40.0", "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.1", "@vitest/coverage-v8": "^1.0.4", "@vitest/ui": "^1.0.4", "jsdom": "^23.0.1", "msw": "^2.0.0", "vitest": "^1.0.4" } } ``` ### 6. NPM Scripts ```json { "scripts": { "test": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest --coverage", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", "test:e2e:debug": "playwright test --debug", "test:all": "npm run test:coverage && npm run test:e2e" } } ``` ### 7. CI/CD Configuration **GitHub Actions (.github/workflows/test.yml)**: ```yaml name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Run unit tests run: npm run test:coverage - name: Install Playwright Browsers run: npx playwright install --with-deps - name: Run E2E tests run: npm run 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: playwright-report path: playwright-report/ ``` ### 8. Testing Best Practices **Unit Test Example**: ```typescript import { render, screen, fireEvent } from './utils/test-utils'; import { LoginForm } from '@/components/LoginForm'; describe('LoginForm', () => { it('renders login form correctly', () => { render(); expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); expect(screen.getByRole('button', { name: /login/i })).toBeInTheDocument(); }); it('shows validation errors for empty fields', async () => { render(); fireEvent.click(screen.getByRole('button', { name: /login/i })); expect(await screen.findByText(/email is required/i)).toBeInTheDocument(); expect(await screen.findByText(/password is required/i)).toBeInTheDocument(); }); it('submits form with valid data', async () => { const onSubmit = vi.fn(); render(); fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'test@example.com' }, }); fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'password123' }, }); fireEvent.click(screen.getByRole('button', { name: /login/i })); await waitFor(() => { expect(onSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123', }); }); }); }); ``` **E2E Test Example**: ```typescript import { test, expect } from '@playwright/test'; test.describe('Authentication Flow', () => { test('should allow user to login', async ({ page }) => { await page.goto('/login'); await page.fill('input[name="email"]', 'test@example.com'); await page.fill('input[name="password"]', 'password123'); await page.click('button[type="submit"]'); await expect(page).toHaveURL('/dashboard'); await expect(page.locator('h1')).toContainText('Dashboard'); }); test('should show error for invalid credentials', async ({ page }) => { await page.goto('/login'); await page.fill('input[name="email"]', 'wrong@example.com'); await page.fill('input[name="password"]', 'wrongpassword'); await page.click('button[type="submit"]'); await expect(page.locator('[role="alert"]')).toContainText( 'Invalid credentials' ); }); }); ``` ## Workflow 1. Ask about testing requirements and existing setup 2. Install testing dependencies (Vitest, Playwright, Testing Library) 3. Create Vitest configuration 4. Create Playwright configuration 5. Set up test utilities and helpers 6. Configure MSW for API mocking 7. Add test scripts to package.json 8. Create example tests 9. Set up CI/CD workflow 10. Provide testing guidelines and best practices ## When to Use - Starting new projects with testing - Migrating from Jest to Vitest - Adding E2E testing to existing projects - Setting up CI/CD testing pipeline - Improving test coverage - Implementing TDD workflow Initialize production-ready testing infrastructure with modern tools!