# E2E Test Harness

Cucumber + Playwright end-to-end tests for the project. The intent is that
**every locked workflow has a `.feature` file before it gets a line of
implementation code** (see "Phase 7.5 — Acceptance scenarios" in the
discovery playbook).

## Layout

```
e2e/
  package.json          — cucumber, @playwright/test, tsx
  cucumber.mjs          — runner config
  tests/
    features/*.feature  — Gherkin scenarios (one feature per locked workflow)
    steps/*.steps.ts    — step definitions; common.steps.ts is generic
    support/
      world.ts          — AppWorld (browser, page, login, gotoApp, expectText)
      hooks.ts          — spawns servers (optional), DB reset hook, browser lifecycle
      types.ts          — config (URLs, timeouts) read from env
    reports/            — generated HTML reports
```

## Running

The harness assumes the app is reachable. Either:

**A. Start servers yourself** (recommended during dev):

```sh
yarn dev          # in repo root — starts frontend + backend
yarn workspace @project/e2e test
```

**B. Let the harness manage the servers**:

```sh
E2E_MANAGE_SERVERS=1 \
E2E_BACKEND_CMD="yarn dev:backend" \
E2E_FRONTEND_CMD="yarn dev" \
yarn workspace @project/e2e test
```

## Configuration

All env vars optional; defaults match the kanban-board-template's ports.

| Var                  | Default                    | Purpose                                       |
|----------------------|----------------------------|-----------------------------------------------|
| `APP_URL`            | `http://localhost:7104`    | Vite frontend URL                             |
| `API_URL`            | `http://localhost:4003`    | Pikku backend URL                             |
| `E2E_TIMEOUT`        | `30000`                    | Per-step Playwright timeout (ms)              |
| `E2E_MANAGE_SERVERS` | unset                      | Set to `1` to have hooks spawn dev servers    |
| `E2E_BACKEND_CMD`    | `yarn dev:backend`         | Used when `E2E_MANAGE_SERVERS=1`              |
| `E2E_FRONTEND_CMD`   | `yarn dev`                 | Used when `E2E_MANAGE_SERVERS=1`              |
| `E2E_RESET_URL`      | unset                      | POST URL that resets DB to seed between tests |
| `HEADED`             | unset                      | Set to `1` to run with a visible browser      |

## DB reset between scenarios

Scenarios pollute each other. To get a deterministic DB state per scenario,
implement a `__test_reset` Pikku function gated behind a test-only flag, and
point `E2E_RESET_URL` at it. The harness will POST to it in the `Before`
hook. If the URL is unset or unreachable the harness logs a warning and
continues.

Example shape:

```ts
export const testReset = pikkuSessionlessFunc({
  expose: true,
  description: 'Reset DB to seed state. Test-only — gated by NODE_ENV.',
  func: async ({ kysely, config }) => {
    if (config.env !== 'test') throw new Error('forbidden')
    await kysely.deleteFrom('booking').execute()
    // ... drop + reseed all mutable tables
    return { ok: true as const }
  },
})
```

## Writing a feature

```gherkin
Feature: <one locked workflow>

  Scenario: <one happy path>
    Given I am logged in as "demo@yoga-retreat.example" with password "demo1234"
    When I visit "/bookings"
    Then I see "Summer Yoga Retreat"
```

Generic steps live in `tests/steps/common.steps.ts`:

- `Given I visit "<path>"`
- `Given I am logged in as "<email>" with password "<password>"`
- `When I log in as "..." with password "..."`
- `When I log out`
- `When I fill "<selector>" with "<value>"`
- `When I click "<button label>"`
- `Then I see "<text>"`
- `Then I do not see "<text>"`
- `Then the URL contains "<fragment>"`

Project-specific steps go in their own `*.steps.ts` files (e.g.
`bookings.steps.ts`). Keep `common.steps.ts` framework-agnostic so the next
project that copies this harness inherits a clean baseline.

## Tags

Run a subset by tag:

```sh
yarn workspace @project/e2e test:tag '@auth'
yarn workspace @project/e2e test:tag 'not @slow'
```

Mark scenarios as `@skip` to exclude from the default run.

## Reports

After a run, open `tests/reports/cucumber-report.html`.
