Files
familysync/apps/pwa/e2e/README.md
T
2026-06-18 21:06:32 -04:00

118 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# E2E Test Harness
Playwright test harness for the FamilySync PWA — mobile-emulated (iPhone 14/WebKit + Pixel 7/Chromium) and Desktop Chrome (1280×720), authenticated via `DEV_AUTH_BYPASS`, deterministically seeded, runs headlessly in CI.
---
## Prerequisites
**You bring up the dev stack first (D-09).** The harness waits for it — it does not start it.
See `docs/deployment.md` under "Running locally (host-side, no Docker)" for the canonical bring-up command.
The stack must include:
- API on `:3000` started with `DEV_AUTH_BYPASS=true` (see Security Guardrail below)
- PWA dev server on `:5173` (`pnpm --filter @familysync/pwa dev`)
- Dev MariaDB on `:3306` (exposed via `docker-compose.dev.yml`)
**`DEV_AUTH_BYPASS=true` MUST be set in the API's environment BEFORE the API process starts.** The harness cannot inject it at runtime — the API reads the env var once at startup. If the API is running without it, all `/api/*` requests return an auth redirect and every spec fails.
---
## Run Commands
`global-setup.ts` is **fail-closed**: it refuses to run (throws before any DB write) unless
`DEV_AUTH_BYPASS=true` **and** `NODE_ENV !== 'production'` — the same handshake the API uses
(see Security Guardrail). So `DEV_AUTH_BYPASS=true` must be exported in the **test process**
environment (not only the API's). Source the DB credentials from the repo-root `.env` and point
`DB_HOST` at the host-side MariaDB:
```bash
# Load DB creds, then run. DEV_AUTH_BYPASS=true is required by the global-setup guard.
set -a; source .env; set +a
export DEV_AUTH_BYPASS=true DB_HOST=127.0.0.1 DB_PORT=3306
# Full suite — iPhone (WebKit), Pixel (Chromium), Desktop Chrome profiles
pnpm --filter @familysync/pwa test:e2e
# Single profile (faster local iteration)
pnpm --filter @familysync/pwa exec playwright test --project=pixel
pnpm --filter @familysync/pwa exec playwright test --project=desktop
# Headed (local debug — shows the browser)
pnpm --filter @familysync/pwa exec playwright test --headed
# UI mode (interactive test explorer)
pnpm --filter @familysync/pwa test:e2e:ui
```
---
## Env Vars
The harness reads these from the environment. DB credentials are env-only — never hardcoded in seed scripts or specs.
| Var | Default | Purpose |
| --------------------- | ----------------------- | --------------------------------------------------------------- |
| `PLAYWRIGHT_BASE_URL` | `http://localhost:5173` | Base URL for all spec navigation and the /health readiness poll |
| `DB_HOST` | `127.0.0.1` | MariaDB host for the global-setup seed script |
| `DB_PORT` | `3306` | MariaDB port |
| `DB_USER` | `familysync` | MariaDB user |
| `DB_PASSWORD` | _(empty)_ | MariaDB password — set in environment or `.env` |
| `DB_NAME` | `familysync` | MariaDB database name |
Set `DB_PASSWORD` (and other non-default values) via the shell or the repo root `.env` file before running. The `.env` file is gitignored — never commit credentials.
---
## Security Guardrail — DEV_AUTH_BYPASS
`DEV_AUTH_BYPASS=true` is a **development-only bypass** that resolves all API requests to Dev User id 1 without OIDC authentication.
The API enforces this via `apps/api/src/auth/devBypass.ts`:
```text
if (process.env.NODE_ENV === 'production') → bypass is a no-op (always)
if (process.env.DEV_AUTH_BYPASS !== 'true') → bypass is a no-op
```
**The production Docker Compose (`docker-compose.yml`) MUST NOT set `DEV_AUTH_BYPASS`.** Setting it in production is an Elevation of Privilege vulnerability — any request would be resolved as the dev user with no authentication.
The `docker-compose.dev.yml` override sets it for local dev and CI. Review it before any production deployment to confirm `DEV_AUTH_BYPASS` is absent from the production compose file.
---
## No Session State File
This harness uses **no `storageState` file** (D-01). There is no checked-in session cookie, no expiring auth artifact, and no per-run login flow. `DEV_AUTH_BYPASS=true` makes the API respond as user 1 unconditionally — the tests run repeatably day-over-day without re-authentication. See PITFALLS.md §Pitfall 14 for why `storageState` is excluded.
---
## What globalSetup Does
Before any spec runs, `global-setup.ts`:
0. **Fail-closed guard:** throws immediately if `NODE_ENV === 'production'` or `DEV_AUTH_BYPASS !== 'true'`, before opening any DB connection — so the TRUNCATE/seed can never run against a production (or unconfirmed) database.
1. **Polls `PLAYWRIGHT_BASE_URL/health`** until 200 OK (60s timeout, then fails fast with a clear error).
2. **Truncates** `list_items`, `list_shares`, `lists`, `calendar_events` (FK checks disabled around TRUNCATE).
3. **Seeds** deterministic fixtures for Dev User 1:
- One timed calendar event (`'Seeded Test Event'`) on calendar_id=10
- One shared list (`'E2E Grocery List'`) owned by user 1, with a `list_shares` row and two items (`'Milk'`, `'Eggs'`)
This seeding is idempotent — two consecutive runs leave the same row counts, no stale rows, no duplicate-key errors.
---
## CI (Phase 8)
Phase 8 (Gitea CI) runs these specs unchanged as a PR UI-regression step. The CI workflow owns:
- Bringing up the dev stack (compose) with `DEV_AUTH_BYPASS=true`
- Waiting for the MariaDB health check before starting the API
- Setting `PLAYWRIGHT_BASE_URL`, `DB_*`, and `DEV_AUTH_BYPASS=true` env vars in the runner environment (the global-setup guard requires `DEV_AUTH_BYPASS=true` in the Playwright process, not only the API's)
The harness handles its own readiness gate (`/health` poll) once the runner sets things up. No changes to spec files are needed for CI — the harness is stack-agnostic via env vars.
CI Dockerfile must use `playwright install --with-deps webkit chromium` to install WebKit system deps (see RESEARCH.md §Pitfall 6).