Files
magnus919_agent-skills/frontend-engineering/references/responsive-layout-testing.md
T
Magnus HedemarkandGitHub c7c4d3b74f Port 11 methodology skills from hermes-profiles (#69)
Engineering: backend-engineering, frontend-engineering, data-engineering,
ml-engineering, platform-engineering, qa-methodology

Executive: go-to-market, legal-strategy, operational-design, org-design,
product-strategy

ml-engineering: added missing training-infrastructure.md reference
qa-methodology: added test-data-management, performance-testing,
security-testing references

All frontmatter converted to agent-skills convention.
Source: https://github.com/magnus919/hermes-profiles
2026-07-21 00:58:26 -04:00

32 KiB

Frontend Engineering Methodology Reference

Comprehensive reference covering responsive layout systems, breakpoint strategy, cross-device testing, and frontend testing patterns (component, integration, visual regression, accessibility, and test data management).

Compiled: June 2026


Table of Contents

  1. Responsive Layout Systems
  2. Breakpoint Strategy
  3. Cross-Device Testing Methodology
  4. Frontend Testing Overview
  5. Component Testing
  6. Integration Testing
  7. Visual Regression Testing
  8. Accessibility Testing
  9. Test Data Management

1. Responsive Layout Systems

Core Philosophy

"CSS Grid is for layout; Flexbox is for alignment."

Use the right tool for the dimensionality of the problem.

Modern CSS provides three layout primitives, each suited to different concerns. The modern approach combines all three rather than choosing one.

1.1 CSS Grid — Two-Dimensional Layout

Best for: Page-level structure, complex multi-axis layouts, precise spatial control, and layouts where rows AND columns matter simultaneously.

Use Case Example
Full-page templates Header, sidebar, main, footer regions
Card grids Gallery, dashboard, e-commerce listings
Overlapping elements Hero sections, magazine layouts
Gap-native layouts gap property avoids margin hacks
Fraction-based sizing fr units, minmax(), auto-fill/auto-fit

Key patterns:

/* Responsive grid without media queries */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 1rem;
}

/* Named grid areas for page layout */
.page {
  display: grid;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  grid-template-columns: 1fr 3fr;
}

When to choose Grid over Flexbox:

  • You need control over both rows and columns simultaneously
  • You want explicit placement (grid-column / grid-row)
  • You have a predefined layout structure (layout-first design)
  • You need overlapping elements without hacks
  • You're building page-level templates

1.2 Flexbox — One-Dimensional Alignment

Best for: Component-level layouts, linear sequences, dynamic content flow, centering, and distributing items along a single axis.

Use Case Example
Navigation bars Horizontal link lists, toolbars
Card internal layout Row of label + value, button groups
Centering Vertically/horizontally centering content
Dynamic wrapping Tags, chips, badge lists
Flexible spacing justify-content: space-between on footers
Reordering order property for responsive reflow

Key patterns:

/* Centering */
.container {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Responsive wrapping */
.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Holy grail of spacing */
.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

When to choose Flexbox over Grid:

  • Content flows in one direction (row OR column, not both)
  • Item sizes are unknown and should determine layout (content-first design)
  • You need simple centering or alignment
  • You're building reusable UI components (buttons, navs, toolbars)
  • Items need to wrap to the next line naturally

1.3 Container Queries — Component-Level Responsiveness

Best for: Reusable components that must adapt to their parent container's size rather than the viewport. The paradigm shift from viewport-centric to container-centric design.

/* Establish containment context */
.card-grid {
  container-type: inline-size;
  container-name: cards;
}

/* Query against the container */
@container cards (width >= 30rem) {
  .card {
    display: grid;
    grid-template: "media body" auto / 2fr 3fr;
  }
  .card__media {
    block-size: 100%;
    object-fit: cover;
  }
}

@container cards (width >= 60rem) {
  .card {
    grid-template: "media body aside" auto / 1fr 2fr 1fr;
  }
}

Container units:

Unit Meaning
cqw 1% of container width
cqh 1% of container height
cqi 1% of container inline size
cqb 1% of container block size
cqmin Smaller of cqi and cqb
cqmax Larger of cqi and cqb

Style queries (CSS 2025): Conditionally style based on a container's custom properties or state.

@container style(--density: compact) {
  .card { padding: 0.75rem; gap: 0.5rem; }
}

@container style(--theme: surface) {
  .card { background: #fff; color: #111; }
}

When to use container queries:

  • The same component appears in multiple contexts (sidebar vs. main content)
  • You want truly reusable design-system components
  • Component breakpoints differ from page-level breakpoints
  • You need to scale typography relative to the component, not the viewport

Progressive enhancement pattern:

/* Base — works everywhere */
.card { display: block; }

/* Enhancement — only if supported */
@supports (container-type: inline-size) {
  .card-wrapper { container-type: inline-size; }
  @container (width >= 25rem) {
    .card { display: grid; grid-template-columns: 1fr 2fr; }
  }
}

1.4 Decision Matrix: Grid vs. Flexbox vs. Container Queries

Criterion CSS Grid Flexbox Container Queries
Dimensionality 2D (rows + cols) 1D (row OR col) N/A (context only)
Primary use Page layout Component alignment Component adaptation
Content vs. layout driven Layout-first Content-first Container-driven
Gap support Native gap Native gap Via host layout
Overlap support Native (grid placement) Not designed for Not applicable
Reordering Via placement Via order N/A
Responsive technique auto-fill/minmax() + MQ flex-wrap + MQ @container queries
Browser support Universal Universal ~90%+ (2026)
Reusable components Possible, but rigid Good Best fit

1.5 Modern Fluid Layout Toolkit (2025+)

Beyond the three primitives, modern CSS offers fluid sizing tools that reduce or eliminate media queries:

/* Fluid typography */
h1 { font-size: clamp(1.5rem, 2.5vw + 1rem, 3rem); }

/* Fluid grid columns */
.grid { grid-template-columns: repeat(auto-fill, minmax(clamp(12rem, 30%, 24rem), 1fr)); }

/* Intrinsic sizing with aspect-ratio */
.card { aspect-ratio: 16 / 9; }

/* Logical properties for RTL support */
.card { margin-inline: 1rem; padding-block: 2rem; }

2. Breakpoint Strategy

2.1 Device-Agnostic vs. Content-Driven

The industry consensus in 2025-2026 is: content-driven breakpoints, not device-based presets.

Approach Description Verdict
Device-agnostic Breakpoints at key widths where content breaks (e.g., 480px, 768px, 1024px) Legacy best practice — better than fixed device targeting, but still viewport-centric
Content-driven Breakpoints determined by the content itself — resize until it looks wrong, then add a breakpoint Modern best practice
Container-driven Breakpoints live on components via @container, not the viewport Cutting edge (2025+)

2.2 The Problem With Fixed Breakpoints

Traditional breakpoint strategy used device classes:

/* Avoid: device-specific */
@media (max-width: 575px)   { /* phones */ }
@media (min-width: 576px)   { /* tablets */ }
@media (min-width: 992px)   { /* laptops */ }
@media (min-width: 1200px)  { /* desktops */ }

This fails because:

  • New devices appear constantly (foldables, ultra-wides, 2-in-1s)
  • The same component might need different breakpoints in different contexts
  • It couples layout logic to arbitrary screen widths that may not match actual content needs

2.3 Content-Driven Breakpoint Strategy

Methodology:

  1. Design in the browser — resize gradually; stop every time the layout breaks
  2. Add a breakpoint at each breaking point, naming it after what breaks, not the pixel value
  3. Use rem not px for breakpoints — respects user font-size preferences
  4. Prefer fluid techniques first (clamp, minmax, auto-fill) before adding media queries
/* Good: content-driven breakpoints in rem */
/* Breakpoints at 30rem, 48rem, 64rem */

/* Prefer: fluid techniques before media queries */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
}

/* Only add media queries when fluid isn't enough */
@media (width >= 64rem) {
  .card-grid { grid-template-columns: repeat(3, 1fr); }
}

Not fixed values, but ranges where content typically breaks:

Range (approx) Common name Behaviour
< 30rem (~480px) Single-column Stack everything
30-48rem (~480-768px) Narrow 2-column grids possible
48-64rem (~768-1024px) Medium 3-column layouts, sidebars
64-90rem (~1024-1440px) Wide Full layouts, multi-column
> 90rem (~1440px) Extra-wide Max-width constraints, whitespace

Key insight: These are guidelines, not dogma. Let YOUR content determine the exact values. A data table might need 50rem; a long-form article might need only 35rem.

2.5 Modern Media Query Syntax

CSS Media Queries Level 4+ introduced range syntax (widely supported in 2025):

/* Old syntax */
@media (min-width: 768px) and (max-width: 1024px) { }

/* New range syntax — cleaner */
@media (768px <= width <= 1024px) { }
@media (width >= 48rem) { }
@media (width < 30rem) { }

2.6 Preference Queries (Accessibility-Aware)

Beyond size, modern responsive design queries user preferences:

/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; }
}

/* Respect reduced transparency */
@media (prefers-reduced-transparency: reduce) {
  .glass { background: solid; }
}

/* Respect dark mode */
@media (prefers-color-scheme: dark) {
  :root { --bg: #111; --text: #eee; }
}

/* Respect increased contrast */
@media (prefers-contrast: more) {
  .card { border: 2px solid; }
}

3. Cross-Device Testing Methodology

3.1 Test Pyramid for Responsive Design

        /\
       /  \         Manual device testing
      / M  \        (real hardware for critical paths)
     / a  u \
    / n  a  \      Device-emulation E2E
   / u  l    \     (Playwright emulation matrix)
  / a  t  e   \
 / l  e  s  t  \
/_______________\   Automated layout-safe checks
                   (container queries, fluid validation)

3.2 Device Emulation Matrix (Playwright/Cypress)

Define your test matrix based on actual user analytics. Common strategy:

// Playwright config — device emulation
const devices = [
  { name: 'iPhone 15', width: 390, height: 844, deviceScaleFactor: 3 },
  { name: 'Pixel 8', width: 412, height: 915, deviceScaleFactor: 2.625 },
  { name: 'iPad Air', width: 820, height: 1180, deviceScaleFactor: 2 },
  { name: 'Desktop 1440', width: 1440, height: 900, deviceScaleFactor: 1 },
  { name: 'Desktop 1920', width: 1920, height: 1080, deviceScaleFactor: 1 },
];

3.3 Responsive Testing Checklist

Check Method Tooling
Content doesn't overflow Automated CSS assertion Playwright toHaveCSS
Touch targets >= 44px Automated size check Playwright bounding box
No horizontal scrollbar Visual regression Percy / Chromatic
Font size >= 16px (iOS zoom) Emulation check Safari/iOS device
Tap targets don't overlap Layout check axe-core / manual
All interactive elements work on touch E2E test Playwright touch emulation
Viewport meta tag present Lint check Lighthouse

3.4 Real Device Testing Strategy

Automated emulation covers ~80% of responsive bugs. Real devices are needed for:

  1. Touch interactions — hover states, drag, swipe, force touch
  2. Hardware-specific — notch, dynamic island, camera cutouts, safe areas
  3. Performance — real CPU/memory constraints, network throttling
  4. Rendering differences — Safari vs. Chrome font rendering, sub-pixel differences

Practical approach:

  • CI/CD: Emulation matrix (Playwright + devices)
  • Pull request review: Visual regression (Percy/Chromatic)
  • Pre-release: Real device cloud (BrowserStack / Sauce Labs / AWS Device Farm)
  • Critical paths: Physical devices owned by the team

3.5 Environment Simulation

// Playwright — responsive + environment simulation
test('homepage on slow 3G', async ({ page }) => {
  await page.emulate({ viewport: { width: 390, height: 844 } });
  await page.context().addInitScript(() => {
    // Simulate reduced motion
    window.matchMedia = (query) => ({
      matches: query.includes('reduce-motion'),
      media: query,
      addListener: () => {},
      removeListener: () => {},
    });
  });
  await page.goto('/', { waitUntil: 'networkidle' });
  // assertions...
});

4. Frontend Testing Overview

4.1 The Testing Trophy (Modern Frontend)

Replace the traditional "testing pyramid" with Kent C. Dodds' Testing Trophy, which better reflects frontend priorities:

    /\
   /  \        Static analysis (TypeScript, ESLint)
  /    \       Unit/Component tests (Vitest + Testing Library)
 /      \      Integration tests (Playwright / Cypress)
/________\     E2E tests (critical user journeys only)

Static analysis catches type errors and lint issues at compile time. Component tests verify isolated UI behaviour. Integration tests (the bulk of your test suite) verify features work together. E2E tests cover the most critical user journeys end-to-end.

4.2 Testing Matrix Summary

Layer Tool Scope Speed Flakiness CI cost
Static TypeScript, ESLint Types, lint Instant Never Free
Component Vitest + Testing Library Individual components Fast (ms) Low Cheap
Integration Playwright / Cypress Features, page interactions Medium (s) Low-Med Moderate
Visual regression Percy / Chromatic Pixel-level UI Medium (s) Medium Higher
Accessibility axe-core + Lighthouse WCAG violations Fast (ms-s) Low Cheap
E2E critical Playwright Full user journeys Slow (min) Medium Highest

5. Component Testing

5.1 Vitest + React Testing Library

Standard setup (2025-2026):

// vitest.config.js
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.js',
    css: true, // process CSS imports
  },
});
// src/test/setup.js
import '@testing-library/jest-dom';
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';

afterEach(() => { cleanup(); });

5.2 Core Query Priority

Test as users experience the UI:

1. getByRole          — Preferred for almost everything
2. getByLabelText     — Form fields
3. getByPlaceholderText — Input hints
4. getByText          — Non-interactive text
5. getByDisplayValue  — Form values
6. getByAltText       — Images
7. getByTitle         — Tooltips
8. getByTestId        — Last resort (data-testid)

5.3 Component Test Patterns

Render + Interaction + Assert:

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Counter } from './Counter';

it('increments count when button clicked', async () => {
  const user = userEvent.setup();
  render(<Counter />);

  await user.click(screen.getByRole('button', { name: /increment/i }));

  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

Test behaviour, not implementation:

// Bad: testing internal state
expect(counter.state.count).toBe(1);

// Good: testing what the user sees
expect(screen.getByText('Count: 1')).toBeInTheDocument();

Mock external dependencies:

import axios from 'axios';
vi.mock('axios');

it('displays posts after fetch', async () => {
  const posts = [{ id: 1, title: 'Hello' }];
  axios.get.mockResolvedValue({ data: posts });

  render(<PostsList />);

  await waitFor(() => {
    expect(screen.getByText('Hello')).toBeInTheDocument();
  });
});

Custom hook testing:

import { renderHook, waitFor } from '@testing-library/react';

it('returns data from fetch', async () => {
  const { result } = renderHook(() => useFetch('/api/data'));

  await waitFor(() => expect(result.current.loading).toBe(false));

  expect(result.current.data).toEqual({ id: 1 });
});

5.4 Best Practices (Component Testing)

Practice Rationale
Test user-visible outcomes, not internals Refactoring doesn't break tests
One assertion per test (when practical) Clear failure messages
Use userEvent, not fireEvent Realistic interaction simulation
Mock API calls, not modules Tests stay fast and focused
Prefer screen. methods over destructured render Keeps tests maintainable
Always clean up DOM between tests Prevents test pollution
Use descriptive test names it('disables button while submitting')
Write the test for the component's contract, not its internals Tests validate behaviour

6. Integration Testing

6.1 Playwright vs. Cypress (2025-2026)

Dimension Playwright Cypress
Browser support Chromium, Firefox, WebKit Chromium, Firefox (limited), WebKit (beta)
Language JS/TS, Python, Java, .NET JS/TS only
Architecture Browser protocol (CDP) — runs outside browser In-browser — runs inside the browser
Multi-tab/window Native support Limited
Network mocking Route interception cy.intercept
Parallel execution Native, sharding Dashboard required
Cross-origin iframes Full support Limited
Mobile emulation Built-in device descriptors cy.viewport only
API testing Same context as browser cy.request
Community Larger momentum (91% satisfaction in State of JS 2025) Mature, but satisfaction declined (72%)
CI integration Zero config Dashboard or plugin

Bottom line (2026): Playwright has become the default choice for new projects due to broader browser support, multi-tab handling, and stronger momentum. Cypress remains viable for teams already invested in its ecosystem.

6.2 Playwright Integration Test Patterns

Page Object Model (POM) — recommended:

// pages/LoginPage.ts
export class LoginPage {
  constructor(private page: Page) {}

  async goto() { await this.page.goto('/login'); }
  async login(email: string, password: string) {
    await this.page.fill('[data-testid="email"]', email);
    await this.page.fill('[data-testid="password"]', password);
    await this.page.click('[data-testid="submit"]');
  }
  async getErrorMessage() {
    return this.page.textContent('[data-testid="error"]');
  }
}
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

test('shows error on invalid credentials', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('bad@email.com', 'wrong');

  await expect(loginPage.getErrorMessage()).toContain('Invalid credentials');
});

Responsive integration test with device emulation:

test('mobile navigation is usable', async ({ page }) => {
  // Emulate mobile viewport
  await page.setViewportSize({ width: 390, height: 844 });

  await page.goto('/');
  await page.click('[data-testid="hamburger"]');
  await expect(page.locator('[data-testid="nav-menu"]')).toBeVisible();

  // Touch targets meet minimum size (44x44 CSS pixels)
  const links = page.locator('nav a');
  const count = await links.count();
  for (let i = 0; i < count; i++) {
    const box = await links.nth(i).boundingBox();
    expect(box?.width).toBeGreaterThanOrEqual(44);
    expect(box?.height).toBeGreaterThanOrEqual(44);
  }
});

API mocking in integration tests:

test('displays empty state when no results', async ({ page }) => {
  // Intercept API call and return empty results
  await page.route('**/api/search**', async route => {
    await route.fulfill({ json: { results: [] } });
  });

  await page.goto('/search');
  await page.fill('[name="q"]', 'nonexistent');
  await page.press('[name="q"]', 'Enter');

  await expect(page.getByText('No results found')).toBeVisible();
});

6.3 Test Organization

tests/
  e2e/
    login.spec.ts
    checkout.spec.ts
  integration/
    api/
      search.spec.ts
      user.spec.ts
    features/
      filters.spec.ts
      pagination.spec.ts
  visual/
    homepage.spec.ts
    product-card.spec.ts
  accessibility/
    homepage.a11y.spec.ts
    form.a11y.spec.ts

7. Visual Regression Testing

7.1 Approaches

Approach Tool Pros Cons
Pixel-by-pixel screenshot diff Percy, Applitools, Chromatic Catches every visual change Baseline management, flakiness from animated content
DOM snapshot Jest/Vitest snapshots Fast, no browser needed Fragile, doesn't catch CSS-only changes
CSS-in-JS snapshot Storybook + Chromatic Component-level, integrated with design system Requires Storybook setup
Layout diff Playwright screenshot Full-page, device-emulated Slower, per-environment diffs
AI-assisted Percy AI, Applitools Eyes Smart change detection, reduced false positives Cost, vendor lock-in

7.2 Percy (BrowserStack)

// Cypress + Percy
cy.visit('/');
cy.percySnapshot('Homepage');

// Playwright + Percy
import percySnapshot from '@percy/playwright';
test('homepage visual', async ({ page }) => {
  await page.goto('/');
  await percySnapshot(page, 'Homepage');
});

7.3 Chromatic (Storybook)

// .github/workflows/chromatic.yml
name: Chromatic
on: push
jobs:
  chromatic:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - run: npm ci
      - uses: chromaui/action@v11
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}

7.4 Playwright Native Visual Testing

import { test, expect } from '@playwright/test';

test('homepage matches snapshot', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveScreenshot('homepage.png', {
    maxDiffPixels: 100,
    fullPage: true,
  });
});

7.5 Visual Testing Best Practices

Practice Detail
Mock dynamic content Replace real data with fixtures for stable screenshots
Freeze animations Use page.addStyleTag() to disable CSS animations
Isolate component states Test loading, empty, error, and edge case states
Set consistent viewport Always snapshot from known viewport sizes
Use CI diff thresholds Allow configurable pixel tolerance to reduce flakiness
Review diffs in PRs Block merging on unapproved visual changes
Rebase baselines intentionally Not on every commit — only when changes are expected

8. Accessibility Testing

8.1 Automation Coverage

Automated accessibility testing catches roughly 30-40% of WCAG violations. This covers the "low-hanging fruit" — common, detectable issues. Manual testing is still required for the remaining 60-70%.

What automation catches well:

  • Missing alt text on images
  • Missing form labels
  • Insufficient colour contrast
  • Duplicate IDs
  • Missing ARIA attributes
  • Invalid ARIA usage
  • Missing lang attributes
  • Empty links / buttons

What requires manual testing:

  • Logical reading order
  • Keyboard navigation flow
  • Screen reader announcements
  • Focus management
  • Meaningful alt text
  • Colour-only information transmission

8.2 axe-core Integration

In Vitest/unit tests (jest-axe):

import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);

it('has no accessibility violations', async () => {
  const { container } = render(<Button>Click me</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

In Playwright E2E tests:

import AxeBuilder from '@axe-core/playwright';

test('homepage has no a11y violations', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page }).analyze();
  expect(results.violations).toEqual([]);
});

// Specific WCAG levels
test('meets WCAG AA requirements', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
    .analyze();
  expect(results.violations).toEqual([]);
});

// Targeted scan — specific element
test('navigation menu is accessible', async ({ page }) => {
  await page.goto('/');
  await page.getByRole('button', { name: 'Menu' }).click();
  const results = await new AxeBuilder({ page })
    .include('#nav-flyout')
    .analyze();
  expect(results.violations).toEqual([]);
});

8.3 Lighthouse CI

// lighthouserc.json
{
  "ci": {
    "collect": {
      "numberOfRuns": 3,
      "staticDistDir": "./build",
      "settings": { "onlyCategories": ["accessibility"] }
    },
    "assert": {
      "assertions": {
        "categories:accessibility": ["error", { "minScore": 0.9 }]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

8.4 CI/CD Integration

# .github/workflows/accessibility.yml
name: Accessibility
on: [pull_request]
jobs:
  a11y:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npm run build
      - run: npx playwright install
      - name: Run a11y tests
        run: npx playwright test --project=a11y

8.5 Accessibility Testing Maturity Model

Level What you do Coverage
1. None No accessibility testing 0%
2. Manual only Occasional Lighthouse audits 30-40% (sporadic)
3. Automated in CI axe-core on every PR in Playwright tests 30-40% (consistent)
4. Automated + enforcement Lighthouse CI score gates 30-40% + regression detection
5. Integrated into component tests jest-axe on every component 30-40% at component level
6. Full pipeline Axe + Lighthouse + manual audits + screen reader 30-40% automated + 60-70% manual

9. Test Data Management

9.1 Data Generation Strategies

Strategy Description When to use
Fixtures Static, pre-defined data in JSON/YAML files Test data that doesn't change often
Factories Programmatic data generation with overrides Many tests with slight data variations
Faker Random realistic data (names, emails, addresses) Stress testing, large datasets
Seed data Known, reproducible database state E2E tests needing a consistent baseline
API mocks Intercepted network responses Integration/component tests without a backend

9.2 Fixture Pattern

// src/test/fixtures/user.json
{
  "id": 1,
  "name": "Alice Johnson",
  "email": "alice@example.com",
  "role": "admin"
}
// Using fixture in a test
import userFixture from './fixtures/user.json';

it('renders user profile', () => {
  render(<UserProfile user={userFixture} />);
  expect(screen.getByText('Alice Johnson')).toBeInTheDocument();
});

9.3 Factory Pattern

// src/test/factories/user.js
import { faker } from '@faker-js/faker';

export function buildUser(overrides = {}) {
  return {
    id: faker.number.int({ min: 1, max: 10000 }),
    name: faker.person.fullName(),
    email: faker.internet.email(),
    role: faker.helpers.arrayElement(['user', 'admin', 'moderator']),
    avatar: faker.image.avatar(),
    createdAt: faker.date.past().toISOString(),
    ...overrides,
  };
}

// Usage
const admin = buildUser({ role: 'admin', name: 'Admin User' });
const users = Array.from({ length: 20 }, () => buildUser());

9.4 Request Mocking Patterns

// MSW (Mock Service Worker) — recommended approach
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';

const server = setupServer(
  http.get('/api/users/:id', ({ params }) => {
    return HttpResponse.json({
      id: params.id,
      name: 'Mocked User',
    });
  }),
);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

it('fetches and displays user', async () => {
  render(<UserDetail userId="42" />);
  await waitFor(() => {
    expect(screen.getByText('Mocked User')).toBeInTheDocument();
  });
});

9.5 Test Data Isolation

Concern Solution
State leakage between tests afterEach cleanup, MSW handler reset
Database state for E2E Database seeding before test suite, teardown after
Shared state in factories Return new objects, not singletons
Crypto-dependent IDs Deterministic faker seed: faker.seed(123)
Dates / timers Fake timers: vi.useFakeTimers()

9.6 Test Data Setup Patterns

// Pattern 1: Setup in describe block
describe('UserList', () => {
  const users = Array.from({ length: 5 }, (_, i) =>
    buildUser({ id: i + 1 })
  );

  it('renders all users', () => {
    render(<UserList users={users} />);
    expect(screen.getAllByRole('listitem')).toHaveLength(5);
  });
});

// Pattern 2: Custom render
function renderWithProviders(ui, { users = [], ...options } = {}) {
  const wrapper = ({ children }) => (
    <UserProvider users={users}>
      {children}
    </UserProvider>
  );
  return render(ui, { wrapper, ...options });
}

// Pattern 3: Test factory function
function setupUserList(overrides = {}) {
  const users = overrides.users ?? [buildUser()];
  const onSelect = vi.fn();
  const utils = render(<UserList users={users} onSelect={onSelect} />);
  return { ...utils, users, onSelect };
}

9.7 Frontend Test Data Best Practices

Practice Detail
Use realistic data Catch real rendering issues, not just schema validation
Separate data from assertions Factories encourage readable, intention-revealing tests
Prefer MSW over vi.mock MSW works at the network level, doesn't need module mocking
Seed deterministically faker.seed(123) for reproducible failures
Mock at the right level API mocking > module mocking > global mocking
Clean up between tests Prevent state leakage that causes flaky tests
Keep fixtures small Only include fields the test actually exercises

References & Further Reading

  • CSS Grid vs Flexbox: blog.logrocket.com/css-flexbox-vs-css-grid
  • Container Queries Guide: caisy.io/blog/css-container-queries
  • Beyond Media Queries (2025): medium.com/@orami98/beyond-media-queries
  • Modern Breakpoint Strategy: penpot.app/blog/how-to-use-css-and-media-query-breakpoints
  • Playwright Accessibility Testing: playwright.dev/docs/accessibility-testing
  • Vitest + Testing Library Setup: freecodecamp.org/news/how-to-test-react-applications-with-vitest
  • Accessibility in CI/CD: testparty.ai/blog/accessibility-testing-cicd
  • Visual Regression Guide: desplega.ai/blog/deep-dive-7-visual-regression-testing-ui-bugs
  • Responsive Web Design Basics: web.dev/articles/responsive-web-design-basics
  • Ten Modern Layouts in One Line of CSS: web.dev/articles/one-line-layouts