Files
AutoGPT/autogpt_platform/frontend/TESTING.md
Ubbe a11199aa67 dx(frontend): set up React integration testing with Vitest + RTL + MSW (#12667)
## Summary
- Establish React integration tests (Vitest + RTL + MSW) as the primary
frontend testing strategy (~90% of tests)
- Update all contributor documentation (TESTING.md, CONTRIBUTING.md,
AGENTS.md) to reflect the integration-first convention
- Add `NuqsTestingAdapter` and `TooltipProvider` to the shared test
wrapper so page-level tests work out of the box
- Write 8 integration tests for the library page as a reference example
for the pattern

## Why
We had the testing infrastructure (Vitest, RTL, MSW, Orval-generated
handlers) but no established convention for page-level integration
tests. Most existing tests were for stores or small components. Since
our frontend is client-first, we need a documented, repeatable pattern
for testing full pages with mocked APIs.

## What
- **Docs**: Rewrote `TESTING.md` as a comprehensive guide. Updated
testing sections in `CONTRIBUTING.md`, `frontend/AGENTS.md`,
`platform/AGENTS.md`, and `autogpt_platform/AGENTS.md`
- **Test infra**: Added `NuqsTestingAdapter` (for `nuqs` query state
hooks) and `TooltipProvider` (for Radix tooltips) to `test-utils.tsx`
- **Reference tests**: `library/__tests__/main.test.tsx` with 8 tests
covering agent rendering, tabs, folders, search bar, and Jump Back In

## How
- Convention: tests live in `__tests__/` next to `page.tsx`, named
descriptively (`main.test.tsx`, `search.test.tsx`)
- Pattern: `setupHandlers()` → `render(<Page />)` → `findBy*` assertions
- MSW handlers from
`@/app/api/__generated__/endpoints/{tag}/{tag}.msw.ts` for API mocking
- Custom `render()` from `@/tests/integrations/test-utils` wraps all
required providers

## Test plan
- [x] All 422 unit/integration tests pass (`pnpm test:unit`)
- [x] `pnpm format` clean
- [x] `pnpm lint` clean (no new errors)
- [x] `pnpm types` — pre-existing onboarding type errors only, no new
errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Nicholas Tindle <nicholas.tindle@agpt.co>
Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
2026-04-06 13:17:08 +00:00

6.4 KiB

Frontend Testing

Testing Strategy

Type Tool Speed When to use
Integration (primary) Vitest + React Testing Library + MSW Fast (~100ms) ~90% of tests — page-level rendering with mocked API
E2E Playwright Slow (~5s) Critical flows: auth, payments, cross-page navigation
Visual Storybook + Chromatic N/A Design system components

Integration tests are the default. Since most of our code is client-only, we test at the page level: render the page with React Testing Library, mock API requests with MSW (handlers auto-generated by Orval), and assert with testing-library queries.

Integration Tests (Vitest + RTL + MSW)

Running

pnpm test:unit              # run all integration/unit tests with coverage
pnpm test:unit:watch        # watch mode for development

File location

Tests live in a __tests__/ folder next to the page or component they test:

app/(platform)/library/
  __tests__/
    main.test.tsx           # tests the main page rendering & interactions
    search.test.tsx         # tests search-specific behavior
  components/
    AgentCard/
      AgentCard.tsx
      __tests__/
        AgentCard.test.tsx  # only when testing the component in isolation
  page.tsx
  useLibraryPage.ts

Naming: use descriptive names like main.test.tsx, search.test.tsx, filters.test.tsx — not page.test.tsx or index.test.tsx.

Writing an integration test

  1. Render the page using the custom render() from @/tests/integrations/test-utils (wraps providers)
  2. Mock API responses using Orval-generated MSW handlers from @/app/api/__generated__/endpoints/{tag}/{tag}.msw.ts
  3. Assert with React Testing Library queries (screen.findByText, screen.getByRole, etc.)
import { render, screen } from "@/tests/integrations/test-utils";
import { server } from "@/mocks/mock-server";
import {
  getGetV2ListLibraryAgentsMockHandler200,
  getGetV2ListLibraryAgentsMockHandler422,
} from "@/app/api/__generated__/endpoints/library/library.msw";
import LibraryPage from "../page";

describe("LibraryPage", () => {
  test("renders agent list from API", async () => {
    server.use(getGetV2ListLibraryAgentsMockHandler200());

    render(<LibraryPage />);

    expect(await screen.findByText("My Agents")).toBeDefined();
  });

  test("shows error state on API failure", async () => {
    server.use(getGetV2ListLibraryAgentsMockHandler422());

    render(<LibraryPage />);

    expect(await screen.findByText(/error/i)).toBeDefined();
  });
});

MSW handlers

Orval generates typed MSW handlers for every endpoint and HTTP status code:

  • getGetV2ListLibraryAgentsMockHandler200() — success response with faker data
  • getGetV2ListLibraryAgentsMockHandler422() — validation error response
  • getGetV2ListLibraryAgentsMockHandler401() — unauthorized response

To override with custom data, pass a resolver:

import { http, HttpResponse } from "msw";

server.use(
  http.get("http://localhost:3000/api/proxy/api/library/agents", () => {
    return HttpResponse.json({
      agents: [{ id: "1", name: "My Agent" }],
      pagination: { total: 1 },
    });
  }),
);

All handlers are aggregated in src/mocks/mock-handlers.ts and the MSW server is set up in src/mocks/mock-server.ts.

Test utilities

  • @/tests/integrations/test-utils — custom render() that wraps components with QueryClientProvider, BackendAPIProvider, OnboardingProvider, NuqsTestingAdapter, and TooltipProvider, so query-state hooks and tooltips work out of the box in page-level tests
  • @/tests/integrations/setup-nextjs-mocks — mocks for next/navigation, next/image, next/headers, next/link
  • @/tests/integrations/mock-supabase-request — mocks Supabase auth (returns null user by default)

What to test at page level

  • Page renders with API data (happy path)
  • Loading and error states
  • User interactions that trigger mutations (clicks, form submissions)
  • Conditional rendering based on API responses
  • Search, filtering, pagination behavior

When to test a component in isolation

Only when the component has complex internal logic that is hard to exercise through the page test. Prefer page-level tests as the default.

E2E Tests (Playwright)

Running

pnpm test                   # build + run all Playwright tests
pnpm test-ui                # run with Playwright UI
pnpm test:no-build          # run against a running dev server

Setup

  1. Start the backend + Supabase stack:
    • From autogpt_platform: docker compose --profile local up deps_backend -d
  2. Seed rich E2E data (creates test123@gmail.com with library agents):
    • From autogpt_platform/backend: poetry run python test/e2e_test_data.py

How Playwright setup works

  • Playwright runs from frontend/playwright.config.ts with a global setup step
  • Global setup creates a user pool via the real signup UI, stored in frontend/.auth/user-pool.json
  • getTestUser() (from src/tests/utils/auth.ts) pulls a random user from the pool
  • getTestUserWithLibraryAgents() uses the rich user created by the data script

Test users

  • User pool (basic users) — created automatically by Playwright global setup. Used by getTestUser()
  • Rich user with library agents — created by backend/test/e2e_test_data.py. Used by getTestUserWithLibraryAgents()

Resetting the DB

If you reset the Docker DB and logins start failing:

  1. Delete frontend/.auth/user-pool.json
  2. Re-run poetry run python test/e2e_test_data.py

Storybook

  • pnpm storybook — run locally
  • pnpm build-storybook — build static
  • pnpm test-storybook — CI runner
  • When changing components in src/components, update or add stories and verify in Storybook/Chromatic

TDD Workflow

When fixing a bug or adding a feature:

  1. Write a failing test first — for integration tests, write the test and confirm it fails. For Playwright, use .fixme annotation
  2. Implement the fix/feature — write the minimal code to make the test pass
  3. Remove annotations — once passing, remove .fixme and run the full suite