# @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.