# UI Testing Tiers UI client test toolchain. Project-wide testing layers (service / inter-service / system) live in [`../../docs/TESTING.md`](../../docs/TESTING.md); this doc covers the UI-specific tiers. ## Tier 1 — per-PR Triggered by `.gitea/workflows/ui-test.yaml` on every push and pull request that touches `ui/**`, `backend/**`, `gateway/**`, `game/**`, `pkg/**`, `go.work`, or `go.work.sum`. Linux runner only. The `actions/checkout@v4` step uses `submodules: recursive`, so the runner pulls every git submodule the suite depends on (today only `pkg/geoip/test-data`, the MaxMind-DB fixtures used by `pkg/geoip`). Runs: - `go test` over the monorepo Go modules, excluding two areas: - `integration/` — needs Docker + testcontainers and is the project's `make -C integration integration` gate. - `client/` — the deprecated Fyne client (see `../PLAN.md` §74) is frozen; its tests are not run in CI. The `pkg//` modules are listed one by one in the workflow because they are independent go.work modules and `./pkg/...` does not recurse into separate modules. The exact command lives in `.gitea/workflows/ui-test.yaml`. - `pnpm test` (Vitest + `@testing-library/svelte` + `@testing-library/jest-dom`) — component / unit tests under `ui/frontend/tests/**/*.test.ts`. - `pnpm exec playwright test` — end-to-end smoke against `pnpm run dev` on port 5173. Four projects: - `chromium-desktop` (Desktop Chrome) - `webkit-desktop` (Desktop Safari) - `chromium-mobile-iphone-13` (iPhone 13 viewport, Chromium engine) - `chromium-mobile-pixel-5` (Pixel 5 viewport, Chromium engine) Playwright traces and screenshots are retained on failure and uploaded as Gitea Actions artefacts (`playwright-report` and `playwright-traces`, 14-day retention). ## Tier 2 — release Triggered by `.gitea/workflows/ui-release.yaml` on tag push (`v*`). Currently mirrors the Tier 1 step set; the dedicated release-only checks are deferred: - **Visual regression baseline check** — deferred to the finalization plan (../Plan-finalize.md). Snapshots will live in `ui/frontend/tests/__snapshots__/` until the project shifts to Argos or another visual-diff service. - **iOS smoke (Capacitor + Appium)** — planned (see ../ROADMAP.md). Runs on a `macos-13` runner once the Capacitor mobile wrapper exists. Both blocks are present as commented sections in `.gitea/workflows/ui-release.yaml`. ## Local execution From `ui/frontend/`: ```sh pnpm test # Vitest pnpm exec playwright install # one-time pnpm exec playwright test # all projects pnpm exec playwright test --project=chromium-desktop pnpm exec playwright show-report # open last HTML report ``` From the repository root, the same scope CI uses (backend serially because most packages spawn their own Postgres testcontainer and parallel bootstraps starve each other on constrained runners): ```sh go test -count=1 -p 1 ./backend/... go test -count=1 \ ./gateway/... ./game/... \ ./pkg/calc/... ./pkg/connector/... ./pkg/cronutil/... \ ./pkg/error/... ./pkg/geoip/... ./pkg/model/... \ ./pkg/postgres/... ./pkg/redisconn/... ./pkg/schema/... \ ./pkg/storage/... ./pkg/transcoder/... ./pkg/util/... ``` ## Local development stack For UI work that needs a real authenticated stack (verifying the FlatBuffers wire end-to-end, exercising a real lobby flow, hitting real Mailpit), bring up `tools/local-dev/`: ```sh make -C tools/local-dev up # postgres + redis + mailpit + backend + gateway pnpm -C ui/frontend dev # Vite on the host, talks to the stack ``` `ui/frontend/.env.development` already targets the stack (`http://localhost:8080`) and pins the matching response-signing public key from `tools/local-dev/keys/`. Per-developer overrides go into `.env.development.local` (gitignored). The stack honours `BACKEND_AUTH_DEV_FIXED_CODE` (default `123456` in `tools/local-dev/.env`) so the login form takes that literal in addition to the real Mailpit code; see [`../../tools/local-dev/README.md`](../../tools/local-dev/README.md) for the full runbook (regenerating the dev keypair, switching the mode off, troubleshooting common boot issues). ## Synthetic reports for visual testing (DEV) For visual checks of the map, inspectors and order-overlay against rich game states, the lobby exposes a DEV-only "Load synthetic report" affordance (`import.meta.env.DEV`). The flow is: 1. Convert a legacy text report (`tools/local-dev/reports/{dg,gplus}/`) to JSON with the Go CLI: ```sh go run ./tools/local-dev/legacy-report/cmd/legacy-report-to-json \ --in tools/local-dev/reports/dg/KNNTS039.REP \ --out /tmp/dg39.json ``` See [`../../tools/local-dev/legacy-report/README.md`](../../tools/local-dev/legacy-report/README.md) for what the parser populates today and how to extend it when a new UI phase decodes a new `Report` field. 2. Run the UI dev server (`pnpm -C ui/frontend dev`), open the lobby, and use the "Load JSON…" file picker in the **Synthetic test reports (DEV)** section. The page navigates to `/games/synthetic-/map` with the report wired into the in-game shell. In synthetic mode: - The map view, inspectors and races view render against the loaded report exactly as they would for a real game. - Composing orders works locally (overlay applies through `applyOrderOverlay` as usual), but **nothing is sent to the gateway** — `OrderDraftStore.scheduleSync` short-circuits because the synthetic id is not a UUID and the layout deliberately does not bind a `GalaxyClient` for this game. - The order draft is persisted into the platform `Cache` under the same `order-drafts` namespace as real games, keyed by the synthetic id, so navigating back into the same synthetic session restores the draft. The cache is cleared with `__galaxyDebug.clearOrderDraft(gameId)` (DEV debug surface). - A page reload loses the in-memory report registry; opening the same synthetic id afterwards redirects to /lobby. Re-load the JSON to reseed. The synthetic-report parity rule requires every change that extends `decodeReport` to also extend the legacy parser in lockstep, or to record in the parser's `README.md` that the new field cannot be derived from legacy text. This keeps the synthetic-mode coverage in step with the contract as the UI grows. ## CI verification Workflow changes are exercised on the primary CI host (`gitea.lan`). Push the branch (`git push gitea …`), then open the run in the Gitea UI to inspect the status and logs. See `CLAUDE.md` (`## Per-stage CI gate`) for the per-stage workflow. For a sub-second syntax check of a workflow YAML without pulling images or running anything: ```sh act -W .gitea/workflows/ui-test.yaml -n push ``` `act` doesn't honour Gitea-specific behaviours (artifact storage, secrets, run triggers); use it only for syntax checks.