---
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(<ThemeProvider theme={theme}>{ui}</ThemeProvider>);

describe('ComponentName', () => {
  it('renders with default props', () => {
    renderWithTheme(<ComponentName />);
    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
