9.3 KiB
Testing Guide
Philosophy
- Test behaviour, not implementation: Focus on what the user can see and do, not internal details.
- Single source of truth per component: Each component should have one consolidated test file.
- Accessibility is mandatory: Basic a11y checks run as part of every component suite.
- E2E is sparse: Only cover critical user journeys that span pages or systems.
Test Structure
The test directory structure is organized as follows:
tests/
components/ # All component-focused tests (Vitest + RTL)
Button.test.tsx
Input.test.tsx
Checkbox.test.tsx
Select.test.tsx
Switch.test.tsx
pages/ # Page-level tests (home, blog, etc.)
home.test.jsx
blog.test.jsx
e2e/ # True end‑to‑end flows + visual regression (Playwright)
critical-journeys.spec.ts # Main user journeys (homepage, navigation, interactions)
visual-regression.spec.ts # Critical page screenshots only (5 tests)
edge-cases.spec.ts # Critical error scenarios (4 tests)
performance.spec.ts # Essential performance checks (2 tests)
utils/ # Shared test utilities
componentTestSuite.tsx
msw/ # MSW server setup for mocking
server.ts
accessibility/
e2e/ # E2E accessibility checks (WCAG compliance)
wcag-compliance.spec.ts
Component tests (tests/components/) use the standard componentTestSuite utility to ensure consistent baseline coverage for all UI components. Page tests (tests/pages/) cover page-level rendering and flows. E2E tests (tests/e2e/) focus on critical user journeys, visual regression, and performance. Accessibility E2E (tests/accessibility/e2e/) provides high-level WCAG compliance checks.
E2E Testing Philosophy
E2E tests follow a sparse, critical-path approach optimized for open source projects:
- Focus on user value: Test critical user journeys that span multiple pages or systems, not individual component interactions
- Maintainability over coverage: Keep tests maintainable and contributor-friendly rather than comprehensive
- Visual regression is minimal: Only capture screenshots of major pages (homepage, blog listing/post, 404), not every component or viewport
- Performance monitoring is essential: Track homepage load and Core Web Vitals, but detailed performance analysis is handled by Lighthouse CI
- Edge cases are critical only: Test scenarios that would break user experience (slow network, offline mode, JS errors, missing images)
This approach reduces test maintenance burden while ensuring critical functionality remains stable.
Standard Component Test Suite
Use the shared suite in tests/utils/componentTestSuite.tsx to get a consistent baseline:
import Component from "../../app/components/Component";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
type Props = React.ComponentProps<typeof Component>;
const config: ComponentTestSuiteConfig<Props> = {
component: Component,
name: "Component",
props: {
/* default props */
} as Props,
requiredProps: ["label"],
optionalProps: {
disabled: true,
},
queries: {
getPrimaryElement: (s) => s.getByRole("button"),
},
variants: {
disabled: {
props: { disabled: true },
assert: (el) => {
expect(el).toBeDisabled();
},
},
error: {
props: { error: true } as Partial<Props>,
assert: (el) => {
expect(el).toHaveClass(
"border-[var(--color-border-default-utility-negative)]",
);
},
},
},
testCases: {
renders: true,
accessibility: true,
keyboardNavigation: true,
disabledState: true,
errorState: true,
},
};
componentTestSuite<Props>(config);
What the Standard Suite Covers
-
Rendering
- Component renders without throwing using the provided
props. - Required props are present and do not break rendering.
- Optional props can be applied without breaking.
- Component renders without throwing using the provided
-
Accessibility
- Runs
axeagainst the rendered output. - Fails on common WCAG 2.1 issues (roles, labels, contrast, etc.).
- Runs
-
Keyboard Navigation
- Ensures the primary element can receive focus.
- Smoke‑tests basic keyboard activation (
Enter,Space) without runtime errors.
-
Disabled State
- Uses
variants.disabledto verify disabled behaviour (e.g.,aria-disabled,disabledattribute, tab index).
- Uses
-
Error State
- Uses
variants.errorto verify error styling/attributes when applicable.
- Uses
When to Add Custom Tests
Use the standard suite for baseline coverage, then add custom describe blocks in the same file when:
- The component has important variants (different sizes, modes, label variants).
- There is non‑trivial interaction (menus, dropdowns, complex keyboard behaviour).
- You need to exercise stateful flows (forms, validation, error messages).
Example (inside the same *.test.tsx file):
describe("Input – behaviour specifics", () => {
it("calls onChange when user types", async () => {
// ...
});
});
Test Commands
-
All component tests (Vitest + RTL):
npm test -
Component-only tests (faster inner loop, focused on
tests/components/):npm run test:component # filter by name: npm run test:component -- --run tests/components/Button.test.tsx -
E2E tests only (Playwright):
npm run test:e2e # or, equivalently: npm run e2e
What to Test vs. What Not to Test
-
Do test
- Public behaviour: visible text, roles, labels, ARIA, keyboard paths.
- State transitions that users rely on (error -> success, disabled -> enabled).
- Critical component interactions (clicks, form submissions, dropdown selection).
- Accessibility invariants (no axe violations, basic keyboard support).
-
Avoid testing
- Pure styling details that are likely to change frequently (exact shadow radius, minor spacing).
- Internal implementation details (private helpers, hook internals, memoisation specifics).
- Responsive visibility in JSDOM (use Playwright visual / responsive tests instead).
Adding Tests for a New Component (≈5 minutes)
-
Create the component file in
app/components/. -
Create a test file in
tests/components/ComponentName.test.tsx. -
Wire the standard suite using
componentTestSuite. -
Add 1–3 custom tests for any unique behaviours.
-
Run:
npm run test:component -- --run tests/components/ComponentName.test.tsx
E2E and Visual Regression
E2E tests are organized into focused files:
critical-journeys.spec.ts: Main user journeys (homepage loads, navigation, key interactions)visual-regression.spec.ts: Critical page screenshots only (homepage full/viewport, blog listing/post, 404)edge-cases.spec.ts: Critical error scenarios (slow network, offline mode, JS errors, missing images)performance.spec.ts: Essential performance checks (homepage load, Core Web Vitals)
Commands:
# Run all E2E tests
npm run test:e2e
# Run visual regression tests only
npm run visual:test
# Update visual regression snapshots (after UI changes)
npm run visual:update
# Run specific test file
npx playwright test tests/e2e/critical-journeys.spec.ts
When to add E2E tests:
-
Add E2E tests when:
- A new critical user journey is introduced (e.g., new multi-step flow)
- A major page is added that needs visual regression coverage
- A critical error scenario needs to be tested (e.g., payment failure, form submission errors)
-
Don't add E2E tests for:
- Component-level interactions (use component tests instead)
- Single-page functionality (use page tests instead)
- Minor UI changes (visual regression will catch major regressions)
- Edge cases that don't impact core user experience
Visual regression snapshots:
Visual regression tests capture screenshots of critical pages. When UI changes are intentional, update snapshots:
npm run visual:update
This updates snapshots for all 5 critical page tests. Review the changes carefully before committing.
Storybook
Storybook is used for component documentation and visual review only. It is not used for automated testing.
- Component tests (
tests/components/*.test.tsx) provide all test coverage previously handled by Storybook test-runner - Storybook stories (
stories/*.stories.js) serve as living documentation and visual examples - Interaction functions are inlined in story files for demonstration purposes
- Run Storybook locally with
npm run storybookfor component development and review
Accessibility Testing
Accessibility is tested at two levels:
-
Component-level accessibility (
tests/components/*.test.tsx):- Automatically covered by
componentTestSuiteusingjest-axe - Tests roles, labels, ARIA attributes, keyboard navigation
- Runs as part of every component test suite
- Automatically covered by
-
Full-page accessibility (
tests/accessibility/e2e/wcag-compliance.spec.ts):- E2E tests using Playwright and
@axe-core/playwright - Validates WCAG 2.1 AA compliance across entire pages
- Tests complete user journeys for accessibility barriers
- E2E tests using Playwright and
This two-tier approach ensures both individual components and full page experiences meet accessibility standards.