Skip to content

11 — Add Tests to an Existing Module

Add comprehensive unit tests to a module that has incomplete or missing test coverage.
Covers mapper tests, component render tests, interaction tests, and a11y tests.


Read these files before executing the prompt:

docs/dev-frontend-guides/03_MODULE_DEVELOPMENT_LIFECYCLE.md # testing section
packages/modules/src/m04-page-hero/m04-page-hero.test.tsx # existing test example
packages/modules/src/{MODULE_PATH}/ # all files in the module
packages/modules/vitest.config.ts # test configuration

Read the following context files first:
- docs/dev-frontend-guides/03_MODULE_DEVELOPMENT_LIFECYCLE.md
- packages/modules/src/m04-page-hero/m04-page-hero.test.tsx (use as test pattern reference)
- packages/modules/vitest.config.ts
- {MODULE_FILES — list each file in the module directory}
Now add comprehensive tests to the following module:
**Module Path:** packages/modules/src/{MODULE_PATH}/
**Module Name:** {MODULE_NAME}
**Module ID:** {MODULE_ID}
**Test File:** packages/modules/src/{MODULE_PATH}/{PascalName}.test.tsx
**Current Test Coverage:** {DESCRIPTION — e.g., "no tests exist", "only basic render test", "mapper tests missing"}
**What to Test:**
- Mapper: {YES_OR_NO}
- Component rendering: {YES_OR_NO}
- User interactions: {YES_OR_NO}
- Accessibility (axe-core): {YES_OR_NO}
**Module Props:**
{LIST_EACH_PROP_WITH_TYPE — for context on what to test}
**Specific Edge Cases to Cover:**
{LIST_EDGE_CASES — e.g., empty cards array, missing optional link, null image, extremely long title}
**Mapper Input Shape (Umbraco API response):**
{DESCRIBE_OR_PASTE_THE_UMBRACO_RESPONSE_STRUCTURE — so mapper tests can be accurate}
Create the test file at:
packages/modules/src/{MODULE_PATH}/{PascalName}.test.tsx
Follow these conventions:
- Use Vitest with @testing-library/react
- Group tests with `describe` blocks: "Mapper", "Rendering", "Interactions", "Accessibility"
- Test behavior, not implementation details (no testing internal state or CSS classes)
- Mapper tests: valid input, missing optional fields, empty arrays, null safety, default values
- Render tests: renders with valid props, conditional elements shown/hidden, responsive variants
- Interaction tests: user events (click, keyboard) trigger expected behavior
- A11y tests: integrate axe-core via @axe-core/react or vitest-axe
- Use `screen.getByRole`, `screen.getByText`, `screen.getByLabelText` — avoid `getByTestId`
- Mock data should be realistic hotel content, not placeholder text
- Every test must have a clear, descriptive name explaining the expected behavior
- All tests must pass with `pnpm test`

  • Test file created at packages/modules/src/{MODULE_PATH}/{PascalName}.test.tsx
  • Tests grouped into describe blocks: Mapper, Rendering, Interactions (if applicable), Accessibility
  • Mapper tests include:
    • Valid input transforms correctly to component props
    • Missing optional fields result in correct defaults (not crashes)
    • Empty arrays handled gracefully (e.g., cards: [] produces cards: [])
    • Null or undefined values in API response do not throw
    • Default values applied when optional fields are absent
  • Render tests include:
    • Component renders without crashing with valid props
    • All required content is visible (headings, text, images)
    • Conditional elements shown when prop is present, hidden when absent
    • Edge case: empty arrays render gracefully (empty state or no items)
    • Edge case: very long text does not break layout (no overflow test needed, just no crash)
  • Interaction tests include (if component is interactive):
    • Click events trigger expected state changes
    • Keyboard events (Enter, Space, Escape, Arrow keys) work
    • Focus moves correctly during interaction
  • Accessibility tests include:
    • axe-core reports zero violations on the default render
    • axe-core reports zero violations on key state variations (e.g., accordion open vs closed)
  • Tests use semantic queries (getByRole, getByText, getByLabelText) not getByTestId
  • Mock data uses realistic hotel content
  • All tests pass with pnpm test
  • No TypeScript errors in the test file
  • Pixel Perfect visual tests pass (pnpm --filter storybook visual:test) — visual regression baselines must not be broken by any code change

  1. Testing implementation details instead of behavior — Do not test internal state variables, CSS class names, or component structure. Test what the user sees and experiences: “heading is visible”, “clicking button shows content”, “image has alt text”.
  2. Not testing mapper edge cases — The Umbraco API can return null, undefined, empty strings, or missing fields for any optional property. The mapper must handle all of these without crashing. Test: { ...validData, optionalField: null }, { ...validData, optionalField: undefined }, and data with the optional field entirely absent.
  3. Not testing all story variants as render cases — If Storybook has 6 story variants, there should be corresponding render tests for the key differences each variant represents.
  4. Brittle selectors — Avoid container.querySelector('.m08-card-grid__item'). Use screen.getByRole('heading', { name: 'Guest Rooms' }) or screen.getAllByRole('listitem').
  5. Forgetting async in interaction testsuserEvent.click() and similar calls are async. Always await them.
  6. Not cleaning up after tests — If a test modifies the DOM or uses timers, ensure proper cleanup. @testing-library/react handles render cleanup automatically, but manual DOM manipulation needs explicit cleanup.
  7. Missing accessibility test — Every component test file should include at least one axe-core integration test. This catches regressions early.
  8. Overly broad assertionsexpect(container).toBeTruthy() tests nothing useful. Assert specific content: expect(screen.getByRole('heading')).toHaveTextContent('Guest Rooms').

Filled-in prompt for M08 Card Grid — Adding comprehensive tests:

Read the following context files first:
- docs/dev-frontend-guides/03_MODULE_DEVELOPMENT_LIFECYCLE.md
- packages/modules/src/m04-page-hero/m04-page-hero.test.tsx (use as test pattern reference)
- packages/modules/vitest.config.ts
- packages/modules/src/m08-card-grid/index.tsx
- packages/modules/src/m08-card-grid/m08-card-grid.types.ts
- packages/modules/src/m08-card-grid/m08-card-grid.mapper.ts
- packages/modules/src/m08-card-grid/m08-card-grid.module.scss
Now add comprehensive tests to the following module:
**Module Path:** packages/modules/src/m08-card-grid/
**Module Name:** Card Grid
**Module ID:** M08
**Test File:** packages/modules/src/m08-card-grid/CardGrid.test.tsx
**Current Test Coverage:** No test file exists.
**What to Test:**
- Mapper: Yes
- Component rendering: Yes
- User interactions: No (Server Component, no interactivity)
- Accessibility (axe-core): Yes
**Module Props:**
- heading: string — Section heading
- subheading?: string — Optional intro text
- cards: CardItem[] — Array of card objects:
- imageDesktop: ImageData
- imageMobile: ImageData
- title: string
- description: string
- link?: { url: string; label: string }
- columns?: 2 | 3 | 4 — Desktop column count (default: 3)
**Specific Edge Cases to Cover:**
- Empty cards array: cards = [] — should render heading but no cards
- Missing optional link on cards: some cards have link, some do not
- Missing subheading: component renders without subheading paragraph
- columns defaults to 3 when not provided
- Very long card title (100+ characters) — should not crash
- Single card: cards array with only 1 item
**Mapper Input Shape (Umbraco API response):**
{
"contentType": "cardGrid",
"properties": {
"heading": "Our Rooms & Suites",
"subheading": "Discover luxury accommodation...",
"columns": 3,
"cards": [
{
"imageDesktop": { "url": "/media/room-desktop.jpg", "width": 800, "height": 600, "alt": "Deluxe Suite" },
"imageMobile": { "url": "/media/room-mobile.jpg", "width": 400, "height": 300, "alt": "Deluxe Suite" },
"title": "Deluxe River Suite",
"description": "Elegant suite overlooking the Thames with Art Deco furnishings.",
"link": { "url": "/rooms/deluxe-river-suite", "label": "View Suite" }
}
]
}
}
Create the test file at:
packages/modules/src/m08-card-grid/CardGrid.test.tsx
Follow these conventions:
- Use Vitest with @testing-library/react
- Group tests with `describe` blocks: "Mapper", "Rendering", "Accessibility"
- Test behavior, not implementation details
- Mapper tests: valid input, missing subheading, empty cards array, null link, missing columns (default to 3)
- Render tests: renders heading, renders correct number of cards, shows/hides subheading, shows/hides card links
- A11y tests: axe-core on default render and on empty cards state
- Use `screen.getByRole`, `screen.getByText` — avoid `getByTestId`
- Mock data should use realistic hotel room and dining content
- All tests must pass with `pnpm test`