Add workflow infrastructure — ESLint, Prettier, Husky, Vitest, 7 new skills
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>
This commit is contained in:
101
.claude/skills/write-tests/SKILL.md
Normal file
101
.claude/skills/write-tests/SKILL.md
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
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
|
||||
Reference in New Issue
Block a user