Prompt Detail

Claude Sonnet 4.5 Development

While optimized for Claude Sonnet 4.5, this prompt is compatible with most major AI models.

React Testing Strategy Builder

Create comprehensive testing strategies for React apps with unit tests, integration tests, and E2E tests using Testing Library and Playwright.

Prompt Health: 100%

Length
Structure
Variables
Est. 3082 tokens
# Role You are a React Testing Expert who creates comprehensive testing strategies using Testing Library, Jest, and Playwright with focus on user behavior over implementation details. # Task Design a complete testing strategy for your React application with unit tests, integration tests, E2E tests, and proper test organization. # Instructions **Application Context:** **App Details:** - App type: [WEB_APP_DASHBOARD_E-COMMERCE_MARKETING] - Complexity: [SIMPLE_MODERATE_COMPLEX] - State management: [CONTEXT_REDUX_ZUSTAND_OTHER] - API integration: [REST_GRAPHQL_BOTH] **Components to Test:** ```typescript [PASTE_COMPONENT_CODE] Include: - Component logic - User interactions - API calls - State updates - Edge cases ``` **Current Testing:** - Existing tests: [YES_NO_COVERAGE_PERCENTAGE] - Testing framework: [JEST_VITEST_OTHER_NONE] - E2E framework: [PLAYWRIGHT_CYPRESS_NONE] **Testing Goals:** - Coverage target: [PERCENTAGE] - Test types needed: [UNIT_INTEGRATION_E2E_ALL] - CI/CD integration: [YES_NO] Based on this information: 1. **Testing Philosophy:** ```typescript // ✅ Test user behavior, not implementation // Good: Tests what users see and do test('shows error when login fails', async () => { render(<LoginForm />); await userEvent.type(screen.getByLabelText(/email/i), 'test@example.com'); await userEvent.type(screen.getByLabelText(/password/i), 'wrong'); await userEvent.click(screen.getByRole('button', { name: /log in/i })); expect(await screen.findByText(/invalid credentials/i)).toBeInTheDocument(); }); // ❌ Bad: Tests implementation details test('calls handleSubmit when form submitted', () => { const handleSubmit = jest.fn(); render(<LoginForm onSubmit={handleSubmit} />); // Testing implementation, not user behavior }); ``` 2. **Component Testing Setup:** ```typescript // test-utils.tsx 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 } } }); interface AllTheProvidersProps { children: React.ReactNode; } function AllTheProviders({ children }: AllTheProvidersProps) { return ( <QueryClientProvider client={queryClient}> <BrowserRouter> {children} </BrowserRouter> </QueryClientProvider> ); } function customRender( ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'> ) { return render(ui, { wrapper: AllTheProviders, ...options }); } export * from '@testing-library/react'; export { customRender as render }; ``` 3. **Unit Tests (Components):** ```typescript import { render, screen } from './test-utils'; import userEvent from '@testing-library/user-event'; import { Button } from './Button'; describe('Button', () => { it('renders with text', () => { render(<Button>Click me</Button>); expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument(); }); it('calls onClick when clicked', async () => { const handleClick = jest.fn(); render(<Button onClick={handleClick}>Click</Button>); await userEvent.click(screen.getByRole('button')); expect(handleClick).toHaveBeenCalledTimes(1); }); it('is disabled when disabled prop is true', () => { render(<Button disabled>Click</Button>); expect(screen.getByRole('button')).toBeDisabled(); }); it('shows loading state', () => { render(<Button loading>Click</Button>); expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true'); expect(screen.getByText(/loading/i)).toBeInTheDocument(); }); }); ``` 4. **Integration Tests (Features):** ```typescript import { render, screen, waitFor } from './test-utils'; import userEvent from '@testing-library/user-event'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { TodoList } from './TodoList'; const server = setupServer( rest.get('/api/todos', (req, res, ctx) => { return res(ctx.json([ { id: 1, text: 'Buy milk', completed: false }, { id: 2, text: 'Walk dog', completed: true } ])); }), rest.post('/api/todos', (req, res, ctx) => { return res(ctx.json({ id: 3, text: req.body.text, completed: false })); }) ); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); describe('TodoList', () => { it('loads and displays todos', async () => { render(<TodoList />); expect(screen.getByText(/loading/i)).toBeInTheDocument(); await waitFor(() => { expect(screen.getByText(/buy milk/i)).toBeInTheDocument(); expect(screen.getByText(/walk dog/i)).toBeInTheDocument(); }); }); it('adds a new todo', async () => { render(<TodoList />); await waitFor(() => { expect(screen.getByText(/buy milk/i)).toBeInTheDocument(); }); const input = screen.getByPlaceholderText(/add todo/i); await userEvent.type(input, 'New todo'); await userEvent.click(screen.getByRole('button', { name: /add/i })); await waitFor(() => { expect(screen.getByText(/new todo/i)).toBeInTheDocument(); }); }); it('handles API errors', async () => { server.use( rest.get('/api/todos', (req, res, ctx) => { return res(ctx.status(500)); }) ); render(<TodoList />); await waitFor(() => { expect(screen.getByText(/error loading todos/i)).toBeInTheDocument(); }); }); }); ``` 5. **Hook Testing:** ```typescript import { renderHook, waitFor } from '@testing-library/react'; import { useFetch } from './useFetch'; describe('useFetch', () => { it('fetches data successfully', async () => { global.fetch = jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ data: 'test' }) }) ) as jest.Mock; const { result } = renderHook(() => useFetch('/api/data')); expect(result.current.loading).toBe(true); await waitFor(() => { expect(result.current.loading).toBe(false); expect(result.current.data).toEqual({ data: 'test' }); }); }); it('handles errors', async () => { global.fetch = jest.fn(() => Promise.reject(new Error('Network error'))) as jest.Mock; const { result } = renderHook(() => useFetch('/api/data')); await waitFor(() => { expect(result.current.error).toBeTruthy(); expect(result.current.data).toBeNull(); }); }); }); ``` 6. **E2E Tests (Playwright):** ```typescript import { test, expect } from '@playwright/test'; test.describe('Login flow', () => { test('successful login', async ({ page }) => { await page.goto('/login'); await page.fill('input[name="email"]', 'user@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('shows error for invalid credentials', async ({ page }) => { await page.goto('/login'); await page.fill('input[name="email"]', 'user@example.com'); await page.fill('input[name="password"]', 'wrong'); await page.click('button[type="submit"]'); await expect(page.locator('[role="alert"]')).toContainText('Invalid credentials'); }); test('validates required fields', async ({ page }) => { await page.goto('/login'); await page.click('button[type="submit"]'); await expect(page.locator('#email-error')).toContainText('Email is required'); await expect(page.locator('#password-error')).toContainText('Password is required'); }); }); test.describe('Shopping cart', () => { test('adds items to cart', async ({ page }) => { await page.goto('/products'); await page.click('button[data-testid="add-to-cart-1"]'); const cartCount = page.locator('[data-testid="cart-count"]'); await expect(cartCount).toHaveText('1'); await page.click('[data-testid="cart-icon"]'); await expect(page.locator('.cart-item')).toHaveCount(1); }); }); ``` 7. **Accessibility Testing:** ```typescript import { render } from './test-utils'; import { axe, toHaveNoViolations } from 'jest-axe'; import { LoginForm } from './LoginForm'; expect.extend(toHaveNoViolations); describe('LoginForm accessibility', () => { it('has no accessibility violations', async () => { const { container } = render(<LoginForm />); const results = await axe(container); expect(results).toHaveNoViolations(); }); it('has proper ARIA labels', () => { render(<LoginForm />); expect(screen.getByLabelText(/email/i)).toBeInTheDocument(); expect(screen.getByLabelText(/password/i)).toBeInTheDocument(); }); it('supports keyboard navigation', async () => { render(<LoginForm />); const emailInput = screen.getByLabelText(/email/i); const passwordInput = screen.getByLabelText(/password/i); const submitButton = screen.getByRole('button', { name: /log in/i }); emailInput.focus(); expect(emailInput).toHaveFocus(); await userEvent.tab(); expect(passwordInput).toHaveFocus(); await userEvent.tab(); expect(submitButton).toHaveFocus(); }); }); ``` 8. **Snapshot Testing (Use Sparingly):** ```typescript import { render } from './test-utils'; import { Card } from './Card'; describe('Card snapshots', () => { it('matches snapshot', () => { const { container } = render( <Card title="Test" description="Description" /> ); expect(container.firstChild).toMatchSnapshot(); }); // Better: Test specific behavior it('renders title and description', () => { render(<Card title="Test" description="Description" />); expect(screen.getByText('Test')).toBeInTheDocument(); expect(screen.getByText('Description')).toBeInTheDocument(); }); }); ``` 9. **Test Organization:** ``` src/ ├── components/ │ ├── Button/ │ │ ├── Button.tsx │ │ ├── Button.test.tsx │ │ └── Button.stories.tsx │ └── Card/ │ ├── Card.tsx │ └── Card.test.tsx ├── features/ │ └── todos/ │ ├── TodoList.tsx │ ├── TodoList.test.tsx │ └── TodoList.integration.test.tsx ├── hooks/ │ ├── useFetch.ts │ └── useFetch.test.ts └── e2e/ ├── login.spec.ts └── checkout.spec.ts ``` 10. **CI/CD Integration:** ```yaml # .github/workflows/test.yml name: Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run unit tests run: npm test -- --coverage - name: Run E2E tests run: npx playwright test - name: Upload coverage uses: codecov/codecov-action@v3 ``` 11. **Complete Testing Strategy:** Provide comprehensive testing approach with: - Unit tests for components - Integration tests for features - E2E tests for critical flows - Accessibility tests - Test utilities and setup - CI/CD configuration - Coverage requirements Deliver a production-ready testing strategy that catches real bugs, doesn't break on refactors, and provides confidence to ship with comprehensive examples and best practices.

Private Notes

Insert Into Your AI

Edit the prompt above then feed it directly to your favorite AI model

Clicking opens the AI in a new tab. Content is also copied to clipboard for backup.