Files
seminarhof-e2e-mqo7np5j/e2e/README.md
2026-06-21 22:00:02 +02:00

4.1 KiB

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):

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

B. Let the harness manage the servers:

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:5001 Vite frontend URL
API_URL http://localhost:5003 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:

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

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:

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.