ADR 008: Next.js 15 and Web Worker Testing Patterns
Date: 2026-01-19
Status: Accepted
Context
The migration to Next.js 15 introduced significant changes to server-side rendering (SSR), particularly the asynchronous nature of cookies() and headers(). Our initial test suite failed because previous mocking strategies were incompatible with these new APIs.
Additionally, our AI system uses Web Workers (webllm.worker.ts) which are difficult to unit test directly in Vitest/JSDOM environments because Worker APIs are not fully supported or behave differently than in browsers.
Decision
We have adopted specific standardized patterns for testing these architectural components.
1. Next.js 15 SSR Mocks
We standardize on a global mocking strategy for Next.js 15 server interfaces using vi.hoisted and Standard Mock Definitions.
Pattern:
- Use
tests/setup/next-mocks.tsfor consistent mock implementations. - Mock
next/headersto return synchronous-behaving mocks forcookies()andheaders(). - Mock
@supabase/ssrto return a standardized "Builder Pattern" mock client that supports both Promise-based and method-chaining usage.
// Example Standard Mock
vi.mock("next/headers", () => ({
cookies: vi.fn().mockReturnValue({
/* ... */
}),
headers: vi.fn().mockReturnValue(new Map()),
}))
2. Web Worker Testing Strategy
We explicitly do not unit test *.worker.ts files directly. Instead, we extract the core business logic into a standalone, testable class/module.
Pattern:
- Refactor: Move logic from
webllm.worker.tstowebllm-engine.ts. - Unit Test: Test
webllm-engine.tswith standard Vitest patterns. - Integration: The worker file becomes a thin shell that imports the engine and handles message passing (tested via E2E only).
3. Centralized Test Fixtures
To avoid "mock drift" where different tests use slightly different versions of data structures, we use centralized fixtures.
Pattern:
- Place fixtures in
tests/fixtures/. - Export strongly-typed constant objects (e.g.,
mockServiceL3). - Export factory functions for variations (e.g.,
createMockService({ ... })). - Do not define ad-hoc mock data in test files unless specific to a unique edge case.
Consequences
Positive
- Stability: Tests are robust against Next.js internal changes.
- Maintainability: Fix mock logic in one place (
next-mocks.ts) updates all tests. - Testability: Complex AI logic is fully unit-testable without browser execution context.
- Consistency: All developers use the same valid data structures.
Negative
- Boilerplate: Tests require standardized setup code at the top of files.
- Indirection: Worker logic is split across two files (worker shell + engine), which may be less intuitive for new contributors.
Compliance
All new tests MUST follow these patterns. Existing tests have been migrated.