// F8-10 end-to-end coverage for the planets table → map navigation. // Boots an authenticated session, mocks the gateway with a small // planets-only report, navigates to `table → planets`, clicks the // first row, and asserts the active view switches to the map (which // also implicitly proves that the `SelectionStore.focus` → map mount // hand-off lands inside the live shell). Ship-groups and fleets are // covered by their vitest specs — one e2e is enough to smoke the // composed flow. import { fromJson, type JsonValue } from "@bufbuild/protobuf"; import { expect, test, type Page } from "@playwright/test"; import { ExecuteCommandRequestSchema } from "../../src/proto/edge/v1/edge_gateway_pb"; import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response"; import { buildMyGamesListPayload, type GameFixture, } from "./fixtures/lobby-fbs"; import { buildReportPayload } from "./fixtures/report-fbs"; const SESSION_ID = "f8-10-tables-session"; const GAME_ID = "f8101010-0000-4000-8000-101010101010"; async function mockGateway(page: Page): Promise { const game: GameFixture = { gameId: GAME_ID, gameName: "F8-10 Game", gameType: "private", status: "running", ownerUserId: "user-1", minPlayers: 2, maxPlayers: 8, enrollmentEndsAtMs: BigInt(Date.now() + 86_400_000), createdAtMs: BigInt(Date.now() - 86_400_000), updatedAtMs: BigInt(Date.now()), currentTurn: 1, }; await page.route( "**/edge.v1.Gateway/ExecuteCommand", async (route) => { const reqText = route.request().postData(); if (reqText === null) { await route.fulfill({ status: 400 }); return; } const req = fromJson( ExecuteCommandRequestSchema, JSON.parse(reqText) as JsonValue, ); let payload: Uint8Array; switch (req.messageType) { case "lobby.my.games.list": payload = buildMyGamesListPayload([game]); break; case "user.games.report": payload = buildReportPayload({ turn: 1, mapWidth: 4000, mapHeight: 4000, localPlanets: [ { number: 1, name: "Home", x: 1000, y: 1000 }, ], otherPlanets: [ { number: 2, name: "Frontier", x: 2000, y: 1500, owner: "Federation", }, ], uninhabitedPlanets: [ { number: 3, name: "Rock", x: 1500, y: 2200 }, ], }); break; default: payload = new Uint8Array(); } const body = await forgeExecuteCommandResponseJson({ requestId: req.requestId, timestampMs: BigInt(Date.now()), resultCode: "ok", payloadBytes: payload, }); await route.fulfill({ status: 200, contentType: "application/json", body, }); }, ); // Hold SubscribeEvents open — mirrors the pattern in other e2e // specs to avoid the revocation watcher signing the session out. await page.route( "**/edge.v1.Gateway/SubscribeEvents", async () => { await new Promise(() => {}); }, ); } async function bootSession(page: Page): Promise { await page.goto("/__debug/store"); await expect(page.getByTestId("debug-store-ready")).toBeVisible(); await page.waitForFunction(() => window.__galaxyDebug?.ready === true); await page.evaluate(() => window.__galaxyDebug!.clearSession()); await page.evaluate( (id) => window.__galaxyDebug!.setDeviceSessionId(id), SESSION_ID, ); } test("clicking a row in the planets table opens the map", async ({ page }) => { await mockGateway(page); await bootSession(page); await page.goto("/"); await page.waitForFunction(() => window.__galaxyNav !== undefined); await page.evaluate( (id) => window.__galaxyNav!.enterGame(id, "table", { tableEntity: "planets", }), GAME_ID, ); const table = page.getByTestId("planets-table"); await expect(table).toBeVisible(); const rows = page.getByTestId("planets-row"); await expect(rows).toHaveCount(3); // Click the foreign planet — the data-* stamps let the spec assert // against a deterministic row regardless of default sort. await page .locator('[data-testid="planets-row"][data-number="2"]') .click(); await expect(page.getByTestId("active-view-map")).toBeVisible(); });