From cff7cc385920b14a99900afd8109f7287ad63a17 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Wed, 27 May 2026 00:25:49 +0200 Subject: [PATCH] =?UTF-8?q?fix(ui):=20F8-04b=20e2e=20=E2=80=94=20viewport-?= =?UTF-8?q?agnostic=20nav=20+=20refresh=20after=20create?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lobby-create-screen: call lobbyData.refresh() after a successful POST so the new game shows up in the private-games panel immediately. The shared lobby-data store is otherwise lazy (ensure-on-first-mount), which rendered a stale list across the post-create navigation in the e2e suite. - e2e tests that move between lobby sub-panels now go through `window.__galaxyNav.go(...)` rather than clicking the sidebar items. The mobile sidebar tucks the submenu behind a dropdown, so testid-based clicks fail on the mobile-iphone-13 / pixel-5 viewports — the dev nav surface bypasses that UX (which has its own coverage in `lobby-tier-gate` / future submenu specs). - game-shell-map missing-membership test: assert `lobby-account-name` instead of `lobby-create-button` on drop-back-to-lobby (the button moved into the paid-only private-games sub-panel; the identity strip is the constant lobby chrome). - inspector-ship-group + ship-group-send synthetic loader specs: jump straight to the dev-only `synthetic-reports` top-level screen via the dev nav surface before looking for the file input (the loader moved off Overview in F8-04b). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/lib/screens/lobby-create-screen.svelte | 15 ++++++++++++--- ui/frontend/tests/e2e/game-shell-map.spec.ts | 5 ++++- .../tests/e2e/inspector-ship-group.spec.ts | 8 ++++++-- ui/frontend/tests/e2e/lobby-flow.spec.ts | 15 ++++++++++----- ui/frontend/tests/e2e/lobby-tier-gate.spec.ts | 10 ++++++---- ui/frontend/tests/e2e/profile-screen.spec.ts | 8 ++++---- ui/frontend/tests/e2e/ship-group-send.spec.ts | 8 ++++++-- 7 files changed, 48 insertions(+), 21 deletions(-) diff --git a/ui/frontend/src/lib/screens/lobby-create-screen.svelte b/ui/frontend/src/lib/screens/lobby-create-screen.svelte index 29a5711..61e9bf9 100644 --- a/ui/frontend/src/lib/screens/lobby-create-screen.svelte +++ b/ui/frontend/src/lib/screens/lobby-create-screen.svelte @@ -9,6 +9,7 @@ import { i18n, type TranslationKey } from "$lib/i18n/index.svelte"; import { loadCore } from "../../platform/core/index"; import { session } from "$lib/session-store.svelte"; + import { lobbyData } from "$lib/lobby-data.svelte"; const DEFAULT_MIN_PLAYERS = 2; const DEFAULT_MAX_PLAYERS = 8; @@ -102,9 +103,17 @@ turnSchedule: trimmedSchedule, targetEngineVersion: targetEngineVersion.trim() || DEFAULT_TARGET_ENGINE_VERSION, }); - // Land on the private-games panel where the freshly created - // game shows up — the lobby-data store will refresh on next - // mount. + // Refresh the lobby-data cache so the freshly-created game + // shows up in the private-games panel the moment we land + // there. The shared store is otherwise lazy (ensure-on- + // first-mount) and would render a stale list across the + // navigation. + try { + await lobbyData.refresh(); + } catch { + // Refresh failure does not block the navigation — + // the panel will retry on its own mount. + } appScreen.go("games-private-games"); } catch (err) { formError = describeLobbyError(err); diff --git a/ui/frontend/tests/e2e/game-shell-map.spec.ts b/ui/frontend/tests/e2e/game-shell-map.spec.ts index c26ab2b..436faf6 100644 --- a/ui/frontend/tests/e2e/game-shell-map.spec.ts +++ b/ui/frontend/tests/e2e/game-shell-map.spec.ts @@ -250,9 +250,12 @@ test("missing-membership game drops back to the lobby with an unavailable toast" ); // Back on the lobby (game shell unmounted), with the unavailable toast. + // F8-04b moved the `create new game` button into the `private games` + // sub-panel; the always-present lobby chrome signal is the identity + // strip, so assert that instead. await expect(page.getByTestId("toast")).toContainText( "this game is no longer available", ); - await expect(page.getByTestId("lobby-create-button")).toBeVisible(); + await expect(page.getByTestId("lobby-account-name")).toBeVisible(); await expect(page.getByTestId("game-shell")).toHaveCount(0); }); diff --git a/ui/frontend/tests/e2e/inspector-ship-group.spec.ts b/ui/frontend/tests/e2e/inspector-ship-group.spec.ts index a9989e3..7de4963 100644 --- a/ui/frontend/tests/e2e/inspector-ship-group.spec.ts +++ b/ui/frontend/tests/e2e/inspector-ship-group.spec.ts @@ -136,9 +136,13 @@ test("synthetic-report loader navigates from lobby to map and renders", async ({ page, }) => { await seedSession(page); - // Seeded session → the dispatcher renders the lobby; the synthetic - // loader lives there behind the dev-affordances flag. + // Seeded session → the dispatcher renders the lobby. F8-04b moved + // the synthetic-report loader off of Overview into its own + // dev-only top-level screen; jump straight to it via the dev nav + // surface so the spec is viewport-agnostic. await page.goto("/"); + await page.waitForFunction(() => window.__galaxyNav !== undefined); + await page.evaluate(() => window.__galaxyNav!.go("synthetic-reports")); await expect(page.getByTestId("lobby-synthetic-section")).toBeVisible(); const file = page.getByTestId("lobby-synthetic-file"); diff --git a/ui/frontend/tests/e2e/lobby-flow.spec.ts b/ui/frontend/tests/e2e/lobby-flow.spec.ts index 5e9d9cf..8ebf251 100644 --- a/ui/frontend/tests/e2e/lobby-flow.spec.ts +++ b/ui/frontend/tests/e2e/lobby-flow.spec.ts @@ -271,8 +271,11 @@ test.describe("Phase 8 — lobby flow", () => { // Default landing is `games-recruitment` (empty, no public games). await expect(page.getByTestId("lobby-recruitment-empty")).toBeVisible(); - // Paid tier exposes the `private games` sub-panel; navigate to it. - await page.getByTestId("lobby-nav-games-private-games").click(); + // Paid tier exposes the `private games` sub-panel; navigate to + // it via the dev nav surface so the spec is viewport-agnostic + // (the mobile sidebar hides the desktop submenu behind a + // dropdown — that UX is covered by lobby-submenu specs). + await page.evaluate(() => window.__galaxyNav!.go("games-private-games")); await expect(page.getByTestId("lobby-games-private-empty")).toBeVisible(); await page.getByTestId("lobby-create-button").click(); @@ -348,14 +351,16 @@ test.describe("Phase 8 — lobby flow", () => { }); await completeLogin(page); - // Navigate to the invitations sub-panel. - await page.getByTestId("lobby-nav-games-invitations").click(); + // Navigate to the invitations sub-panel via the dev nav surface + // (viewport-agnostic — the mobile sidebar hides the desktop + // submenu behind a dropdown). + await page.evaluate(() => window.__galaxyNav!.go("games-invitations")); await expect(page.getByTestId("lobby-invite-accept")).toBeVisible(); await page.getByTestId("lobby-invite-accept").click(); await expect(page.getByTestId("lobby-invite-accept")).toBeHidden(); // Active-past now has the invited game. - await page.getByTestId("lobby-nav-games-active-past").click(); + await page.evaluate(() => window.__galaxyNav!.go("games-active-past")); await expect(page.getByTestId("lobby-my-game-card")).toContainText("Invited Game"); expect(mocks.inviteRedeemCalls).toEqual([ { gameId: "private-1", inviteId: "invite-1" }, diff --git a/ui/frontend/tests/e2e/lobby-tier-gate.spec.ts b/ui/frontend/tests/e2e/lobby-tier-gate.spec.ts index 156f75e..ae2ab93 100644 --- a/ui/frontend/tests/e2e/lobby-tier-gate.spec.ts +++ b/ui/frontend/tests/e2e/lobby-tier-gate.spec.ts @@ -181,8 +181,10 @@ test.describe("F8-04b — tier gate", () => { await expect(privateGamesEntry).toBeVisible(); // Free-tier callers reach the create form via the DEV-visible - // entry, but the backend still rejects the POST. - await privateGamesEntry.click(); + // entry, but the backend still rejects the POST. Use the dev + // nav surface so the assertion works on mobile too (the mobile + // sidebar tucks the private-games entry behind a dropdown). + await page.evaluate(() => window.__galaxyNav!.go("games-private-games")); await page.getByTestId("lobby-create-button").click(); await expect(page.getByTestId("lobby-create-form")).toBeVisible(); @@ -198,7 +200,7 @@ test.describe("F8-04b — tier gate", () => { }); await completeLogin(page); - await page.getByTestId("lobby-nav-games-private-games").click(); + await page.evaluate(() => window.__galaxyNav!.go("games-private-games")); await page.getByTestId("lobby-create-button").click(); await expect(page.getByTestId("lobby-create-form")).toBeVisible(); @@ -227,7 +229,7 @@ test.describe("F8-04b — tier gate", () => { const mocks = await mockGatewayTier(page, { isPaid: true }); await completeLogin(page); - await page.getByTestId("lobby-nav-games-private-games").click(); + await page.evaluate(() => window.__galaxyNav!.go("games-private-games")); await expect(page.getByTestId("lobby-games-private-empty")).toBeVisible(); await expect(page.getByTestId("lobby-create-button")).toBeVisible(); await page.getByTestId("lobby-create-button").click(); diff --git a/ui/frontend/tests/e2e/profile-screen.spec.ts b/ui/frontend/tests/e2e/profile-screen.spec.ts index 476992d..c67c089 100644 --- a/ui/frontend/tests/e2e/profile-screen.spec.ts +++ b/ui/frontend/tests/e2e/profile-screen.spec.ts @@ -330,10 +330,10 @@ test.describe("F8-04 — profile screen", () => { await expect(page.getByTestId("lobby-account-name")).toContainText("Pilot"); // Navigate back to the games section. F8-04b replaced the bare - // `Overview` page with a `games` parent + sub-panels; clicking - // the parent resolves to the first visible sub-panel - // (`recruitment` for a no-games session). - await page.getByTestId("lobby-nav-games").click(); + // `Overview` page with a `games` parent + sub-panels; the dev + // nav surface goes straight to the canonical landing + // (`games-recruitment`) so the assertion is viewport-agnostic. + await page.evaluate(() => window.__galaxyNav!.go("games-recruitment")); await expect(page.getByTestId("lobby-recruitment-empty")).toBeVisible(); await expect(page.getByTestId("lobby-account-name")).toContainText("Pilot"); diff --git a/ui/frontend/tests/e2e/ship-group-send.spec.ts b/ui/frontend/tests/e2e/ship-group-send.spec.ts index 6ef068b..2894743 100644 --- a/ui/frontend/tests/e2e/ship-group-send.spec.ts +++ b/ui/frontend/tests/e2e/ship-group-send.spec.ts @@ -135,9 +135,13 @@ async function bootSession(page: Page): Promise { } async function loadSyntheticGame(page: Page): Promise { - // Seeded session → the dispatcher renders the lobby; the synthetic - // loader lives there behind the dev-affordances flag. + // Seeded session → the dispatcher renders the lobby. F8-04b moved + // the synthetic-report loader off of Overview into its own + // dev-only top-level screen; jump straight to it via the dev nav + // surface so the spec is viewport-agnostic. await page.goto("/"); + await page.waitForFunction(() => window.__galaxyNav !== undefined); + await page.evaluate(() => window.__galaxyNav!.go("synthetic-reports")); await expect(page.getByTestId("lobby-synthetic-section")).toBeVisible(); const file = page.getByTestId("lobby-synthetic-file"); await file.setInputFiles({