Skip to main content

Lesson 9: Testing and Quality Gates

Build reliable quality gates for primitives: interaction tests, accessibility assertions, and regression protection.

Why quality gates matter

Primitives reduce risk, but wrapper drift can still introduce breakages:

  • keyboard interactions regress,
  • focus restoration breaks,
  • semantics disappear after refactors,
  • visual state attributes stop matching CSS rules.

A clear test strategy catches these before release.


Test pyramid for primitive wrappers

  1. Unit/integration: wrapper behavior and event contracts.
  2. Accessibility assertions: role/name/state checks.
  3. End-to-end keyboard flows: realistic user journeys.
  4. Visual snapshots: state-specific styling checks.

Do not rely on only one layer.


Example interaction test

import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
 
it('restores focus to trigger after dialog close', async () => {
  const user = userEvent.setup()
  render(<DeleteProjectDialog />)
 
  const trigger = screen.getByRole('button', { name: /delete project/i })
  await user.click(trigger)
  await user.keyboard('{Escape}')
 
  expect(trigger).toHaveFocus()
})
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
 
it('restores focus to trigger after dialog close', async () => {
  const user = userEvent.setup()
  render(<DeleteProjectDialog />)
 
  const trigger = screen.getByRole('button', { name: /delete project/i })
  await user.click(trigger)
  await user.keyboard('{Escape}')
 
  expect(trigger).toHaveFocus()
})

Accessibility assertion examples

Assert:

  • correct role (dialog, menu, menuitem, etc.),
  • accessible name from title/label,
  • state attributes (aria-expanded, aria-checked),
  • disabled behavior.

When possible, run an automated a11y check and keep manual SR audits in release checklist.


Regression suites for overlays

Create dedicated scenarios for:

  • nested dialog inside dialog,
  • popover inside dialog,
  • menu inside popover,
  • async close guards,
  • reduced-motion mode.

Most regressions happen at composition boundaries.


CI quality gates

Minimum recommended gates:

  1. Typecheck.
  2. Lint.
  3. Wrapper tests.
  4. Critical keyboard e2e flows.
  5. Accessibility smoke checks.

If any gate fails, block merge.


Internal registry note

If you run an internal design system, create a separate internal registry package (or docs section) that stores vetted primitive wrapper examples.

Suggested structure:

  • registry/primitives/dialog/*
  • registry/primitives/popover/*
  • registry/primitives/menu/*
  • each entry with: source, usage examples, known constraints, test links.

This gives teams a trusted, reusable catalog instead of ad hoc snippets.


Lab

  1. Add one interaction + one a11y assertion test for Dialog wrapper.
  2. Add one keyboard e2e flow for menu navigation.
  3. Create a first internal-registry entry for one primitive wrapper.