Lesson 9: Testing and Quality Gates
Build reliable quality gates for primitives: interaction tests, accessibility assertions, and regression protection.
Lesson 9 of 10: This lesson defines the test strategy that keeps primitive wrappers stable as your team grows.
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
- Unit/integration: wrapper behavior and event contracts.
- Accessibility assertions: role/name/state checks.
- End-to-end keyboard flows: realistic user journeys.
- 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:
- Typecheck.
- Lint.
- Wrapper tests.
- Critical keyboard e2e flows.
- 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
- Add one interaction + one a11y assertion test for
Dialogwrapper. - Add one keyboard e2e flow for menu navigation.
- Create a first internal-registry entry for one primitive wrapper.