# Task: E2E Testing Pattern — Playwright Arrangement and Spec Guide

## Context

SpecSoloist is a spec-driven AI coding framework. Read `AGENTS.md` for full project context.

The existing test patterns in SpecSoloist are unit-test oriented: Python uses pytest +
Starlette TestClient, TypeScript uses vitest. Both run in-process without a browser.

For Next.js web apps, unit tests with vitest cover logic and API routes, but they cannot
test:
- Page rendering and navigation (server components, client components, route transitions)
- HTMX interactions in browser context (for FastHTML)
- Form submission with real DOM events
- Hydration and JavaScript interactivity

This requires Playwright (or equivalent). The gap is not just a missing arrangement — it's
also a missing **spec pattern**: what does a Playwright E2E test spec look like? How does a
soloist write a Playwright test from a spec?

This task creates both the tooling and the pattern documentation.

## What to Build

### 1. Playwright arrangement template

`arrangements/nextjs-playwright.yaml` was sketched in task 08 and should be created there.
This task focuses on the spec patterns and a working example. Reference task 08 for the
template file.

### 2. Playwright spec pattern guide

Create `docs/e2e-testing.md` explaining how to write specs that generate Playwright tests.

Key differences from unit test specs:

**Selector contract:** When a spec describes a UI component, it should include a
`data-testid` attribute for each interactive element:

```markdown
## TodoItem component

Renders a single todo as `<li data-testid="todo-item">`. Contains:
- The todo text in a `<span data-testid="todo-text">`
- A delete button `<button data-testid="delete-btn" hx-delete="/todos/{index}">`
```

The implementation spec includes `data-testid` attributes; the E2E test spec references
them using `page.locator('[data-testid="todo-item"]')`.

**Test structure:** E2E specs should have a `# Test Scenarios` table where each row
describes a full user journey, not a function call:

```markdown
| Scenario | Steps | Expected |
|----------|-------|----------|
| Add a todo | 1. Navigate to /, 2. Fill "Buy milk" in input, 3. Click Add | "Buy milk" appears in list |
| Delete a todo | 1. Add "Buy milk", 2. Click delete button | Item removed from list |
| Empty add | Fill empty input, click Add | List unchanged |
```

Soloists should translate these into `test('...', async ({ page }) => { ... })` blocks.

**Dev server:** Playwright tests need a running server. The arrangement's `test:` command
should use `playwright test` which by default expects a `playwright.config.ts` specifying
the dev server:

```typescript
// playwright.config.ts (generated by soloist for this spec type)
import { defineConfig } from '@playwright/test';
export default defineConfig({
  webServer: { command: 'npm run dev', port: 3000, reuseExistingServer: true },
  testDir: './e2e',
});
```

Document how the arrangement's `setup_commands` should include generating this config.

**Mocking vs real:** E2E tests should mock external API calls (OpenAI, databases) using
Playwright's `page.route()` API. Document the pattern for speccing mock boundaries.

### 3. FastHTML E2E example

Add Playwright tests to `examples/fasthtml_app/` as an optional second test suite.

FastHTML apps are Starlette ASGI apps, so they work with Playwright's `BaseURL` and a
running server. Create `examples/fasthtml_app/specs/e2e_todos.spec.md`:

```markdown
---
name: e2e_todos
type: bundle
status: draft
dependencies:
  - layout
  - routes
---

# Overview

End-to-end browser tests for the todo app. Tests run against a live FastHTML server
using Playwright. The server is started by the test fixture.

# Test Scenarios

| Scenario | Steps | Expected |
|----------|-------|----------|
| Page loads | Navigate to http://localhost:5001 | Page title is "Todos", list is empty |
| Add todo | Fill "Buy milk", click Add | "Buy milk" appears in #todo-list |
| Delete todo | Add "Buy milk", click delete button | Item disappears from list |
| Empty add | Click Add with empty input | List unchanged |
```

Generate `tests/test_e2e_todos.py` using `pytest-playwright`. The test should start the
FastHTML app as a subprocess fixture, then use `page.goto()` to interact with it.

Add `pytest-playwright` to `examples/fasthtml_app/pyproject.toml`.

### 4. Document in `AGENTS.md` / `score/spec_format.spec.md`

Add a note in the spec format documentation explaining that E2E test specs:
- Should include `data-testid` attributes in component specs
- Use user-journey scenarios (not function calls) in Test Scenarios tables
- Require a running server — document the arrangement `test:` command setup

## Files to Read First

- `examples/fasthtml_app/specs/` — current specs (post task 06)
- `examples/fasthtml_app/arrangement.yaml`
- `score/spec_format.spec.md` — spec format to extend
- `arrangements/nextjs-playwright.yaml` (from task 08)
- `AGENTS.md`

## Success Criteria

1. `docs/e2e-testing.md` exists and covers: selector contract, test scenario format,
   dev server setup, mocking pattern.
2. `examples/fasthtml_app/specs/e2e_todos.spec.md` exists and passes `sp validate`.
3. `sp conduct` on `e2e_todos` generates a working `tests/test_e2e_todos.py`.
4. `uv run pytest tests/test_e2e_todos.py -v` passes (requires `pytest-playwright`
   and a running FastHTML server or a subprocess fixture).
5. `score/spec_format.spec.md` mentions `data-testid` and user-journey scenario format.
6. The guide explains how E2E arrangement differs from unit test arrangement.

## Notes

Playwright tests are slower and require a display (or `--headed=false`). The CI arrangement
(task 09 or a future CI task) should run Playwright in headless mode. Document this
distinction in the guide — unit tests and E2E tests should be separate `sp conduct` targets
with separate arrangements.
