feat(e2e): add freight e2e docker-compose stack with isolated services

This commit is contained in:
Nathnael
2026-07-21 11:00:02 +00:00
parent 2a35ee97fa
commit b8b8c4adf7
19 changed files with 1701 additions and 54 deletions

80
e2e/freight/README.md Normal file
View File

@@ -0,0 +1,80 @@
# @edr/freight-e2e — Cypress e2e suite for the freight system
Containerized, fully isolated e2e environment: throwaway Postgres (tmpfs),
MinIO, freight-api, portal, and backoffice — plus a Cypress runner that works
both headless-in-Docker and interactively from the host against the same URLs.
## Stack (`docker-compose.e2e.yaml`, project name `edr-freight-e2e`)
| Service | Host port | Notes |
| ----------------------- | --------- | ---------------------------------------------- |
| `freight-api-e2e` | 3101 | migrations + seeders run at boot |
| `freight-portal-e2e` | 5273 | nginx static build, API baked to `:3101` |
| `freight-backoffice-e2e`| 5283 | nginx static build, API baked to `:3101` |
| `postgres-freight-e2e` | 5533 | `edr_freight_e2e`, tmpfs — gone on `down` |
| `minio-e2e` | 9310/9311 | object storage for file features |
| `cypress` | (host net)| profile `cypress`, headless chrome |
Ports deliberately avoid the dev stack (3001/5173/5183/5433). The dev database
is never touched.
## Usage (from repo root)
```bash
pnpm e2e:freight:up # build + start stack, wait for healthchecks
pnpm e2e:freight:open # interactive Cypress on the host
pnpm e2e:freight:run # headless run from the host
pnpm e2e:freight:ci # headless run inside the cypress container
pnpm e2e:freight:down # teardown, drop all data
```
First `up` is slow (image builds + 240 migrations + seeders — healthcheck
allows 3 min). Requires the same root `.npmrc` (GitHub Packages auth for
`@tria-plc`) as the main compose file.
The `cypress` service uses `network_mode: host` (Linux). On macOS/Windows run
Cypress from the host (`e2e:freight:open` / `e2e:freight:run`) instead of the
container.
## Test users
Inserted by Cypress itself — a global `before()` hook runs
`cy.task("db:seedUsers")`, which executes
`cypress/fixtures/seed-users.sql` (idempotent, pre-hashed argon2 passwords)
against the e2e database. No API code is involved; the app's user seeders
stay disabled. The API's always-on boot seeders must have run first
(org/unit/positions) — guaranteed once `freight-api-e2e` is healthy.
- Staff (backoffice): `linestaff|chief|director|ceo|marketer|operation|gl-et|gl-dj@edr.local`
— password `password@tria`
- Customers (portal): `user@gmail.com`, `user2@gmail.com`
— password `12345678`
Full map in `cypress/fixtures/users.json`.
## Conventions
- **Programmatic login** everywhere except the two dedicated UI-login specs:
`cy.loginBackoffice(email?)` / `cy.loginPortal(email?)``cy.session`-cached
(across specs), `POST /api/auth/login`, sets the `auth-token` /
`refresh-token` cookies the apps read.
- **Origins**: `baseUrl` is the backoffice (5283). Portal specs `cy.visit`
the absolute portal URL; a test that touches *both* apps wraps portal steps
in `cy.origin()` (different port = different origin). Cookies ignore ports —
always call the matching login command right before switching apps so
`cy.session` restores the right cookie snapshot.
- **DB access**: `cy.task("db:query", { sql, params })` runs SQL against the
e2e database (`E2E_DB_URL`, default `localhost:5533`). Use for seeding
edge-case data and asserting side effects — it can never reach the dev DB.
- **Spec layout**:
- `cypress/e2e/api/` — API contract via `cy.request` (no browser)
- `cypress/e2e/backoffice/` — staff app
- `cypress/e2e/portal/` — customer app
- `cypress/e2e/flows/` — cross-app journeys (both directions)
## Extending
Deep module flows (booking wizard → staff approval → scheduling → billing)
belong in `flows/`. Pattern: arrange via API/`db:query`, act through the UI of
one app, assert through the UI of the other + a `db:query` cross-check. Prefer
adding `data-testid` attributes to app code over brittle text selectors.