--- name: write-tests description: Write or update tests for a component — determines whether it needs unit tests (Vitest), interaction tests (Storybook play), or both, then generates appropriate test code. user-invocable: true argument-hint: "[ComponentName]" --- Write tests for the specified component. **Component:** $ARGUMENTS ## Preparation 1. Read `docs/conventions/component-conventions.md` for component patterns 2. Read the component source file in `src/components/` 3. Read the component's existing Storybook stories 4. Check `docs/memory/component-registry.md` for component status and composition ## Determine Test Strategy Categorise the component: ### Interactive components (need Storybook `play` functions) Components with user interactions: clicks, toggles, keyboard navigation, form inputs, selection state changes. **Examples:** Button, Input, SearchBar, ServiceOption, AddOnOption, Switch, Radio, FuneralFinder For these, add `play` functions to existing stories: ```tsx import { expect, userEvent, within } from '@storybook/test'; export const ClickTest: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); const button = canvas.getByRole('button'); await userEvent.click(button); await expect(button).toBeVisible(); }, }; ``` **What to test in `play` functions:** - Click/tap fires expected callback - Disabled state prevents interaction - Keyboard navigation works (Enter, Space, Arrow keys) - Loading state disables interaction - Error states show correct feedback - Selection state changes visually - Form validation triggers on submit ### Logic-heavy components (need Vitest unit tests) Components with significant internal logic: conditional rendering, validation, state machines, computed values. **Examples:** FuneralFinder (validation logic), PackageDetail (price calculations), ServiceSelector (selection management) Create `{ComponentName}.test.tsx` alongside the component: ```tsx import { describe, it, expect } from 'vitest'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ThemeProvider } from '@mui/material/styles'; import theme from '../../../theme'; import { ComponentName } from './ComponentName'; const renderWithTheme = (ui: React.ReactElement) => render({ui}); describe('ComponentName', () => { it('renders with default props', () => { renderWithTheme(); expect(screen.getByRole('...')).toBeInTheDocument(); }); }); ``` **What to test in Vitest:** - Conditional rendering logic (shows/hides elements based on props) - Validation rules (required fields, format checks) - Callback props fire with correct arguments - Accessibility: correct ARIA roles and states - Edge cases: empty data, maximum values, missing optional props ### Display-only components (minimal testing needed) Components that only render static content from props: Typography, Badge, Divider, Card (non-interactive). For these, stories ARE the tests. Ensure stories cover all variants. No additional test files needed unless there's conditional rendering logic. ## After Writing Tests 1. Run `npm run test` to verify Vitest tests pass 2. If Storybook `play` functions were added, verify they work in Storybook's test panel 3. Update `docs/memory/component-registry.md` with test status note ## Rules - Always wrap components in `ThemeProvider` with FA theme in Vitest tests - Use `screen.getByRole()` over `getByTestId()` — test what the user sees - Test behaviour, not implementation — don't test internal state directly - Keep tests focused: one assertion per test where practical - Don't test MUI internals — only test our component's API - Don't snapshot test — snapshots are too brittle for an evolving design system