Phase 1: Session log archived (1096→91 lines), D031 token access convention
Phase 2: ESLint v9 + Prettier + jsx-a11y, initial config and lint fixes
Phase 3: 7 new skills (polish, harden, normalize, clarify, typeset, quieter, adapt)
+ Vercel reference docs, updated audit/review-component refs
Phase 4: Husky + lint-staged pre-commit hooks, preflight updated to 8 checks
Phase 5: Vitest + Testing Library + /write-tests skill
- Badge.tsx colour maps unified to CSS variables (D031)
- 5 empty interface→type alias fixes (Switch, Radio, Divider, IconButton, Link)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
102 lines
3.8 KiB
Markdown
102 lines
3.8 KiB
Markdown
---
|
|
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
|