Rewrite ui/docs (navigation, order-composer, auth-flow, pwa-strategy, game-state + secondary topic docs) and ui/README for the single-URL app-shell (in-memory screens/views, Back→lobby via shallow routing, sessionStorage restore + validation, return-to-lobby). ui/PLAN.md gets a Phase-10 supersede note (implemented; standalone-compatible). Fix stale code comments (session-store auth gate, report-sections spec contract). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.0 KiB
UI Testing Tiers
UI client test toolchain. Project-wide testing layers (service /
inter-service / system) live in ../../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 testover the monorepo Go modules, excluding two areas:integration/— needs Docker + testcontainers and is the project'smake -C integration integrationgate.client/— the deprecated Fyne client (see../PLAN.md§74) is frozen; its tests are not run in CI.
The
pkg/<name>/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 underui/frontend/tests/**/*.test.ts. -
pnpm exec playwright test— end-to-end smoke againstpnpm run devon 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-13runner once the Capacitor mobile wrapper exists.
Both blocks are present as commented sections in
.gitea/workflows/ui-release.yaml.
Local execution
From ui/frontend/:
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):
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/:
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
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:
-
Convert a legacy text report (
tools/local-dev/reports/{dg,gplus}/) to JSON with the Go CLI:go run ./tools/local-dev/legacy-report/cmd/legacy-report-to-json \ --in tools/local-dev/reports/dg/KNNTS039.REP \ --out /tmp/dg39.jsonSee
../../tools/local-dev/legacy-report/README.mdfor what the parser populates today and how to extend it when a new UI phase decodes a newReportfield. -
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 lobby enters asynthetic-<uuid>game on the map view (activeView.reset()+appScreen.go("game", { gameId })) with the report wired into the in-game shell. The app-shell URL stays/game/— seenavigation.md.
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
applyOrderOverlayas usual), but nothing is sent to the gateway —OrderDraftStore.scheduleSyncshort-circuits because the synthetic id is not a UUID and the in-game shell deliberately does not bind aGalaxyClientfor this game. - The order draft is persisted into the platform
Cacheunder the sameorder-draftsnamespace 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; a restored
synthetic game whose report is gone falls back to the lobby
(
appScreen.go("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:
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.