Files
community-rule/docs/guides/testing.md
T
2026-01-26 15:41:25 -07:00

7.2 KiB

Testing Strategy for CommunityRule

Overview

This document outlines our comprehensive testing strategy that properly separates unit testing from responsive behavior testing, following best practices for JSDOM limitations and real browser testing.

Current Test Status

  • 236 total tests across the project
  • 227 tests passing (96.2% success rate)
  • 9 tests failing (performance and interaction tests)
  • 15 test files covering all major components
  • Performance Monitoring: Comprehensive regression detection and budget enforcement

Testing Philosophy

The Problem with JSDOM and Responsive Testing

Short take: Unit tests in JSDOM can't truly "switch breakpoints." JSDOM doesn't evaluate CSS media queries, so Tailwind's hidden sm:block … won't change visibility when you "resize" the window.

Solution: Proper Test Separation

  • Unit / component tests (Vitest + RTL): assert structure and classes, not responsive visibility.
  • Responsive behavior: verify with browser-based tests (Playwright) or visual tests (Chromatic/Storybook) at real viewport widths.

Test Categories

1. Unit Tests (Vitest + React Testing Library)

Purpose: Test component structure, accessibility, and configuration data.

What to test:

  • DOM roles/labels exist: role="banner", nav landmark, menu items
  • The right Tailwind classes are present on wrappers (block sm:hidden, hidden md:block, etc.)
  • Data-driven bits produce the expected count/order (e.g., navigationItems, avatarImages, logoConfig)
  • Component configuration and exported data structures

Example:

// tests/unit/Header.structure.test.js
test("logo wrappers include breakpoint classes", () => {
  render(<Header />);
  const logoWrappers = screen.getAllByTestId("logo-wrapper");

  // Check first logo variant (xs only)
  expect(logoWrappers[0]).toHaveClass("block", "sm:hidden");

  // Check second logo variant (sm only)
  expect(logoWrappers[1]).toHaveClass("hidden", "sm:block", "md:hidden");
});

2. Browser-Based Tests (Playwright)

Purpose: Test real responsive behavior at actual viewport widths.

What to test:

  • Visibility at real breakpoints
  • Layout changes between breakpoints
  • Interactive behavior at different screen sizes
  • Accessibility across viewports

Example:

// tests/e2e/header.responsive.spec.js
const breakpoints = [
  { name: "xs", width: 360, height: 700 },
  { name: "sm", width: 640, height: 700 },
  { name: "md", width: 768, height: 700 },
  { name: "lg", width: 1024, height: 700 },
  { name: "xl", width: 1280, height: 700 },
];

for (const bp of breakpoints) {
  test(`header layout at ${bp.name}`, async ({ page }) => {
    await page.setViewportSize({ width: bp.width, height: bp.height });
    await page.goto("/");

    const nav = page.getByRole("navigation", { name: /main navigation/i });
    await expect(nav).toBeVisible();
  });
}

3. Visual Tests (Storybook + Chromatic)

Purpose: Visual regression testing and design system validation.

What to test:

  • Visual diffs per breakpoint
  • Design consistency across viewports
  • Component variations and states

Example:

// stories/Header.responsive.stories.js
export default {
  parameters: {
    chromatic: {
      viewports: [360, 640, 768, 1024, 1280],
      delay: 100,
    },
  },
};

Component Improvements

Header Component Enhancements

  1. Added Test IDs for easier testing:

    <div data-testid="logo-wrapper" className={config.breakpoint}>
      {renderLogo(config.size, config.showText)}
    </div>
    
  2. Exported Configuration for testing:

    export const navigationItems = [...];
    export const avatarImages = [...];
    export const logoConfig = [...];
    
  3. Structured Breakpoint Containers:

    <div data-testid="nav-xs" className="block sm:hidden">
    <div data-testid="nav-sm" className="hidden sm:block md:hidden">
    <div data-testid="nav-md" className="hidden md:block lg:hidden">
    

Test File Structure

tests/
├── unit/                    # Unit tests (Vitest + RTL)
│   ├── Header.test.jsx     # CONSOLIDATED: Comprehensive Header tests
│   ├── Footer.test.jsx
│   ├── Layout.test.jsx
│   └── Page.test.jsx
├── integration/            # Integration tests
│   └── ContentLockup.integration.test.jsx
├── e2e/                   # Browser tests (Playwright)
│   └── header.responsive.spec.js  # NEW: Responsive behavior tests
└── stories/               # Storybook stories
    └── Header.responsive.stories.js  # NEW: Visual testing

Best Practices

Unit Testing (JSDOM)

  1. Test structure, not visibility:

    // ✅ Good: Test classes exist
    expect(element).toHaveClass("block", "sm:hidden");
    
    // ❌ Bad: Test visibility (doesn't work in JSDOM)
    expect(element).toBeVisible();
    
  2. Use test IDs for containers:

    // ✅ Good: Test specific containers
    const logoWrapper = screen.getByTestId("logo-wrapper");
    
    // ❌ Bad: Query by complex class strings
    const logoWrapper = document.querySelector(".block.sm\\:hidden");
    
  3. Test configuration data:

    // ✅ Good: Test exported configuration
    expect(navigationItems).toHaveLength(3);
    expect(logoConfig).toHaveLength(5);
    

Browser Testing (Playwright)

  1. Test real viewport sizes:

    await page.setViewportSize({ width: 640, height: 700 });
    
  2. Test visibility at breakpoints:

    if (bp.name === "xs") {
      await expect(page.getByTestId("auth-xs")).toBeVisible();
    }
    
  3. Test accessibility across viewports:

    const interactiveElements = [
      page.getByRole("link", { name: /use cases/i }),
      page.getByRole("button", { name: /create rule/i }),
    ];
    
    for (const element of interactiveElements) {
      await expect(element).toBeVisible();
      await expect(element).toBeEnabled();
    }
    

Running Tests

Unit Tests

npm test                    # Run all unit tests
npm test tests/unit/        # Run only unit tests
npm test Header.structure   # Run specific test file

Browser Tests

npx playwright test         # Run all browser tests
npx playwright test header.responsive.spec.js  # Run specific test

Visual Tests

npm run storybook          # Start Storybook
npx chromatic --project-token=xxx  # Run visual tests

Future Improvements

  1. Add more Playwright tests for other components
  2. Set up Chromatic for visual regression testing
  3. Add performance tests for responsive behavior
  4. Create component-specific test utilities
  5. Add accessibility testing with axe-core

Key Takeaways

  1. JSDOM limitations require separating structure tests from visibility tests
  2. Test IDs make testing more reliable and maintainable
  3. Exported configuration enables better data structure testing
  4. Real browser testing is essential for responsive behavior
  5. Visual testing catches design regressions across breakpoints

This strategy provides comprehensive coverage while respecting the limitations of different testing environments.