From c4f1409329afb7879d498f135470edaa4d1e9c46 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Sat, 9 May 2026 14:19:47 +0200 Subject: [PATCH] ui/order-draft: silence hydrate path on non-UUID game ids + Phase 10 e2e fixture upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 14's auto-sync calls `uuidToHiLo` on every layout boot. The existing Phase 10 e2e specs use a placeholder string `test-shell` as the game id, which throws in the FBS request encoder and surfaced as a noisy `console.warn` plus a flaky webkit-desktop test on the local-ci ARM runner. `OrderDraftStore.hydrateFromServer` and `scheduleSync` now skip when the active game id isn't a real UUID — the auto-sync path is inert for fixture data and the placeholder-warning is gone. The Phase 10 spec switches to a deterministic UUID (`10101010-1010-1010-1010-101010101010`) so future Phase 14+ specs don't have to special-case it. Co-Authored-By: Claude Opus 4.7 --- ui/frontend/src/sync/order-draft.svelte.ts | 17 +++++++++++++++++ ui/frontend/tests/e2e/game-shell.spec.ts | 13 +++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/ui/frontend/src/sync/order-draft.svelte.ts b/ui/frontend/src/sync/order-draft.svelte.ts index d1b8b26..debfc01 100644 --- a/ui/frontend/src/sync/order-draft.svelte.ts +++ b/ui/frontend/src/sync/order-draft.svelte.ts @@ -131,6 +131,14 @@ export class OrderDraftStore { }): Promise { if (this.status !== "ready") return; this.client = opts.client; + // Guard against placeholder game ids the Phase 10 e2e specs + // still use — auto-sync needs a real UUID for the FBS request + // envelope, and a parser exception here would only be visible + // as a noisy `console.warn` deep in the layout boot path. + if (!isUuid(this.gameId)) { + this.syncStatus = "idle"; + return; + } this.syncStatus = "syncing"; this.syncError = null; try { @@ -232,6 +240,9 @@ export class OrderDraftStore { private scheduleSync(): void { if (this.client === null) return; + // Same UUID guard as `hydrateFromServer` — placeholder game + // ids in test fixtures must not blow up the auto-sync path. + if (!isUuid(this.gameId)) return; if (this.syncing !== null) { this.pending = true; return; @@ -379,6 +390,12 @@ export class OrderDraftStore { } } +const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +function isUuid(value: string): boolean { + return UUID_RE.test(value); +} + function validateCommand(cmd: OrderCommand): CommandStatus { switch (cmd.kind) { case "planetRename": diff --git a/ui/frontend/tests/e2e/game-shell.spec.ts b/ui/frontend/tests/e2e/game-shell.spec.ts index 65751d1..4aa9c12 100644 --- a/ui/frontend/tests/e2e/game-shell.spec.ts +++ b/ui/frontend/tests/e2e/game-shell.spec.ts @@ -1,7 +1,9 @@ // Phase 10 end-to-end coverage for the in-game shell. Every spec -// boots an authenticated session through `/__debug/store` (no -// gateway calls — the shell makes none in Phase 10), navigates into -// `/games/test-shell/map`, and exercises one slice of the chrome: +// boots an authenticated session through `/__debug/store` (the +// in-game shell makes a handful of gateway calls — for the lobby +// record, the report, and the order read-back; we don't mock them +// here, the shell tolerates ECONNREFUSED), navigates into +// `/games//map`, and exercises one slice of the chrome: // header navigation, sidebar tab preservation, mobile bottom-tabs, // and the breakpoint switches at 768 / 1024 px. @@ -14,7 +16,10 @@ import { expect, test, type Page } from "@playwright/test"; // `setDeviceSessionId`); the merged global declaration covers both. const SESSION_ID = "phase-10-shell-session"; -const GAME_ID = "test-shell"; +// GAME_ID has to be a real UUID — Phase 14's auto-sync calls +// `uuidToHiLo` on it for the FBS request envelope, and an +// arbitrary string would throw on every layout boot. +const GAME_ID = "10101010-1010-1010-1010-101010101010"; async function bootShell(page: Page): Promise { await page.goto("/__debug/store");