End-to-End (E2E) Testing Strategy Contract¶
Last updated: 2026-08-18 Source: Architecture Decision AD-21 (Architecture Spine & Owner Decision 2026-08-18)
This document defines the E2E testing strategy, state isolation architecture, authoring guidelines, quarantine policy, CI gating policy, and coverage model for the platform.
1. Isolation Mechanism & Architecture¶
Stack & Mechanism¶
To achieve fast, deterministic, and repeatable test runs, the E2E test suite operates against a controlled backend and frontend environment: - Backend Persistence: In-memory SQLite (sqlite:///:memory:) configured via test runtime environment variables. - LLM Boundary: Mock LLM (DEEPAGENTS_MODEL="openai:test-model", OPENAI_API_KEY="sk-test", OPENAI_MODEL_NAME="gpt-4"). The mock LLM produces deterministic response streams (NFR-A10/A13) without making external LLM API calls. - Per-Test State Reset: Backend exposes a POST /api/testing/reset endpoint that wipes in-memory SQLite tables and resets mock state back to a baseline state. - Auto-Reset Fixture: autoResetState auto-fixture in frontend/e2e/fixtures.ts automatically calls POST /api/testing/reset before every test spec execution. - Single Worker Execution: Playwright configuration specifies workers: 1 in frontend/playwright.config.ts to ensure strict sequential execution and eliminate concurrency state pollution.
Incident History & Context¶
- PR #18 Incident: Early E2E runs suffered from order-dependent failures and dirty test state due to shared, persistent SQLite database files across tests and lack of state reset between specs. In PR #18, assertions were temporarily weakened or bypassed to force CI passes.
- Issue #9 Resolution: Issue #9 established the state isolation mechanism by adding the in-memory SQLite backend, mock LLM,
POST /api/testing/resetendpoint, andautoResetStatePlaywright fixture running sequentially (workers: 1). - AD-21 Architecture Decision (2026-08-18): Recorded decision to maintain this stack (in-memory SQLite + mock LLM, without requiring PostgreSQL for E2E testing). E2E tests gate the
developbranch.
2. How to Write a New Spec¶
File Structure & Naming¶
- All E2E test specs live in
frontend/e2e/*.spec.ts. - Spec names should reflect the feature area being tested (e.g.,
chat.spec.ts,hitl.spec.ts,ideas.spec.ts,performance.spec.ts,threads.spec.ts).
Fixtures¶
Always import test and expect from ./fixtures (not @playwright/test directly):
import { test, expect } from './fixtures';
import { CommandCenterPage } from './pages/CommandCenter';
test.describe('Feature Area', () => {
test.beforeEach(async ({ api }) => {
await api.waitForHealthy();
});
test('primary user flow case', async ({ page, api }) => {
// Test implementation
});
});
Importing from ./fixtures ensures autoResetState executes before each test and grants access to the api helper fixture.
Page Objects (POM Pattern)¶
Page objects reside in frontend/e2e/pages/ (e.g. CommandCenterPage, DashboardPage, IdeaDetailPage, KnowledgeBasePage). - Responsibility: Page objects encapsulate UI locators, navigation (goto), and user interaction methods (sendMessage, openIdea, confirmDeleteDialog). - Rule: Page objects must never contain test assertions (expect). Assertions belong strictly in spec files.
API Helpers for Test Setup¶
Avoid driving long UI setup sequences just to create precondition data. Use the api fixture or direct REST API requests (POST /api/threads, POST /api/ideas, POST /api/interrupts/) to seed required state before testing UI behaviors:
// Pre-seed thread via API helper before verifying UI switching
const thread = await api.postJson('/api/threads', { title: 'Test Thread', idea_id: null });
Timeout Budgets¶
| Scope | Budget | Configuration Location |
|---|---|---|
| Expect assertions | 5,000ms | expect.timeout in playwright.config.ts |
| Action timeout | 10,000ms | use.actionTimeout in playwright.config.ts |
| Global test timeout | 30,000ms (CI) / 60,000ms (Local) | timeout in playwright.config.ts |
| Server startup & Warm-up | 120,000ms | global-setup.ts / webServer.timeout |
3. Quarantine Policy for Flaky Tests¶
Flaky tests degrade confidence in the pipeline and must be addressed with a zero-tolerance policy:
- Quarantine Immediately: If a test is flaky or failing due to environment issues, mark it with
test.skipand include a comment explaining the reason and referencing a tracked issue: - Never Delete: Flaky tests must never be deleted. They represent valuable coverage that needs investigation or refactoring.
- Never Silently Retry into Green: Test retries in CI (2 retries configured) are for absorbing runner network transients only. Silently retrying flaky assertions without tracking the underlying cause is strictly forbidden. Weakening assertions (e.g., removing wait assertions or changing strict checks to truthy checks) is prohibited.
4. CI Gating Policy¶
- Gated Branch (
develop): E2E tests gate all pull requests targetingdevelopand direct pushes todevelop. - CI Job Timeout: 20 minutes (
timeout-minutes: 20in.github/workflows/ci.yml). - Retries: 2 retries configured in CI environment (
process.env.CI ? 2 : 0). - Main Branch Policy:
mainbranch is ungated for E2E tests per AD-16. This policy will be revisited at thev1.0.0milestone release.
5. Coverage Model¶
The E2E test coverage model balances speed and comprehensive flow verification:
- Per-Story Primary-Flow Cases: Every story delivering new user-facing functionality must include primary-flow user E2E test cases.
- Feature-Area Golden Paths: One dedicated spec file exists per stable feature area in
frontend/e2e/*.spec.ts. - Epic Signoff Requirement: Epic close signoff requires feature-area golden-path E2E coverage across all capabilities delivered within the epic.