---
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