ui/phase-15: planet inspector production controls + order-draft collapse
Adds the second end-to-end command (`setProductionType`) with a collapse-by-`planetNumber` rule on the order draft, the segmented production-controls component on the planet inspector, the FBS encoder/decoder pair for `CommandPlanetProduce`, and the `localShipClass` projection on `GameReport`. Forecast number is deferred and tracked in the new `ui/docs/calc-bridge.md`.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
// FlatBuffers payload builders for the Phase 14 Playwright suite.
|
||||
// Mirrors what `pkg/transcoder/order.go` produces in production for
|
||||
// the `user.games.order` POST response and the
|
||||
// `user.games.order.get` GET response.
|
||||
// FlatBuffers payload builders for the Phase 14 / Phase 15 Playwright
|
||||
// suites. Mirrors what `pkg/transcoder/order.go` produces in production
|
||||
// for the `user.games.order` POST response and the
|
||||
// `user.games.order.get` GET response. Phase 15 extends the fixture
|
||||
// with a `setProductionType` variant so a single mocked gateway can
|
||||
// echo either rename or production-switch commands back to the client.
|
||||
|
||||
import { Builder } from "flatbuffers";
|
||||
|
||||
@@ -10,20 +12,46 @@ import { UUID } from "../../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandItem,
|
||||
CommandPayload,
|
||||
CommandPlanetProduce,
|
||||
CommandPlanetRename,
|
||||
PlanetProduction,
|
||||
UserGamesOrder,
|
||||
UserGamesOrderGetResponse,
|
||||
UserGamesOrderResponse,
|
||||
} from "../../../src/proto/galaxy/fbs/order";
|
||||
|
||||
export interface CommandResultFixture {
|
||||
interface CommandResultFixtureBase {
|
||||
cmdId: string;
|
||||
planetNumber: number;
|
||||
name: string;
|
||||
applied: boolean | null;
|
||||
errorCode: number | null;
|
||||
}
|
||||
|
||||
export interface PlanetRenameResultFixture extends CommandResultFixtureBase {
|
||||
kind: "planetRename";
|
||||
planetNumber: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SetProductionTypeResultFixture
|
||||
extends CommandResultFixtureBase {
|
||||
kind: "setProductionType";
|
||||
planetNumber: number;
|
||||
productionType:
|
||||
| "MAT"
|
||||
| "CAP"
|
||||
| "DRIVE"
|
||||
| "WEAPONS"
|
||||
| "SHIELDS"
|
||||
| "CARGO"
|
||||
| "SCIENCE"
|
||||
| "SHIP";
|
||||
subject: string;
|
||||
}
|
||||
|
||||
export type CommandResultFixture =
|
||||
| PlanetRenameResultFixture
|
||||
| SetProductionTypeResultFixture;
|
||||
|
||||
export function buildOrderResponsePayload(
|
||||
gameId: string,
|
||||
commands: CommandResultFixture[],
|
||||
@@ -83,19 +111,61 @@ export function buildOrderGetResponsePayload(
|
||||
|
||||
function encodeItem(builder: Builder, c: CommandResultFixture): number {
|
||||
const cmdIdOffset = builder.createString(c.cmdId);
|
||||
const nameOffset = builder.createString(c.name);
|
||||
const inner = CommandPlanetRename.createCommandPlanetRename(
|
||||
builder,
|
||||
BigInt(c.planetNumber),
|
||||
nameOffset,
|
||||
);
|
||||
let payloadType: CommandPayload;
|
||||
let inner: number;
|
||||
switch (c.kind) {
|
||||
case "planetRename": {
|
||||
const nameOffset = builder.createString(c.name);
|
||||
inner = CommandPlanetRename.createCommandPlanetRename(
|
||||
builder,
|
||||
BigInt(c.planetNumber),
|
||||
nameOffset,
|
||||
);
|
||||
payloadType = CommandPayload.CommandPlanetRename;
|
||||
break;
|
||||
}
|
||||
case "setProductionType": {
|
||||
const subjectOffset = builder.createString(c.subject);
|
||||
inner = CommandPlanetProduce.createCommandPlanetProduce(
|
||||
builder,
|
||||
BigInt(c.planetNumber),
|
||||
productionTypeToFBS(c.productionType),
|
||||
subjectOffset,
|
||||
);
|
||||
payloadType = CommandPayload.CommandPlanetProduce;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CommandItem.startCommandItem(builder);
|
||||
CommandItem.addCmdId(builder, cmdIdOffset);
|
||||
if (c.applied !== null) CommandItem.addCmdApplied(builder, c.applied);
|
||||
if (c.errorCode !== null) {
|
||||
CommandItem.addCmdErrorCode(builder, BigInt(c.errorCode));
|
||||
}
|
||||
CommandItem.addPayloadType(builder, CommandPayload.CommandPlanetRename);
|
||||
CommandItem.addPayloadType(builder, payloadType);
|
||||
CommandItem.addPayload(builder, inner);
|
||||
return CommandItem.endCommandItem(builder);
|
||||
}
|
||||
|
||||
function productionTypeToFBS(
|
||||
value: SetProductionTypeResultFixture["productionType"],
|
||||
): PlanetProduction {
|
||||
switch (value) {
|
||||
case "MAT":
|
||||
return PlanetProduction.MAT;
|
||||
case "CAP":
|
||||
return PlanetProduction.CAP;
|
||||
case "DRIVE":
|
||||
return PlanetProduction.DRIVE;
|
||||
case "WEAPONS":
|
||||
return PlanetProduction.WEAPONS;
|
||||
case "SHIELDS":
|
||||
return PlanetProduction.SHIELDS;
|
||||
case "CARGO":
|
||||
return PlanetProduction.CARGO;
|
||||
case "SCIENCE":
|
||||
return PlanetProduction.SCIENCE;
|
||||
case "SHIP":
|
||||
return PlanetProduction.SHIP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
// fixture with the optional rich planet fields (size, resources,
|
||||
// stockpiles, population, industry, colonists, production, free
|
||||
// industry) so the inspector e2e can drive the read-only display
|
||||
// against realistic values. Later phases extend the helper as ships,
|
||||
// fleets, sciences, etc. land.
|
||||
// against realistic values. Phase 15 adds a minimal `LocalShipClass`
|
||||
// projection so the planet inspector's Build-Ship sub-picker has data
|
||||
// in e2e specs (`name` only — Phase 17 widens this when ship-class
|
||||
// CRUD lands). Later phases extend the helper as fleets, sciences,
|
||||
// etc. land.
|
||||
|
||||
import { Builder } from "flatbuffers";
|
||||
|
||||
@@ -17,6 +20,7 @@ import {
|
||||
LocalPlanet,
|
||||
OtherPlanet,
|
||||
Report,
|
||||
ShipClass,
|
||||
UnidentifiedPlanet,
|
||||
UninhabitedPlanet,
|
||||
} from "../../../src/proto/galaxy/fbs/report";
|
||||
@@ -44,6 +48,10 @@ export interface OtherPlanetFixture extends InhabitedFixture {
|
||||
owner: string;
|
||||
}
|
||||
|
||||
export interface ShipClassFixture {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ReportFixture {
|
||||
turn: number;
|
||||
mapWidth?: number;
|
||||
@@ -52,6 +60,7 @@ export interface ReportFixture {
|
||||
otherPlanets?: OtherPlanetFixture[];
|
||||
uninhabitedPlanets?: PlanetFixture[];
|
||||
unidentifiedPlanets?: { number: number; x: number; y: number }[];
|
||||
localShipClass?: ShipClassFixture[];
|
||||
}
|
||||
|
||||
export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
@@ -131,6 +140,13 @@ export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
},
|
||||
);
|
||||
|
||||
const localShipClassOffsets = (fixture.localShipClass ?? []).map((cls) => {
|
||||
const name = builder.createString(cls.name);
|
||||
ShipClass.startShipClass(builder);
|
||||
ShipClass.addName(builder, name);
|
||||
return ShipClass.endShipClass(builder);
|
||||
});
|
||||
|
||||
const localVec =
|
||||
localOffsets.length === 0
|
||||
? null
|
||||
@@ -147,6 +163,10 @@ export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
unidentifiedOffsets.length === 0
|
||||
? null
|
||||
: Report.createUnidentifiedPlanetVector(builder, unidentifiedOffsets);
|
||||
const localShipClassVec =
|
||||
localShipClassOffsets.length === 0
|
||||
? null
|
||||
: Report.createLocalShipClassVector(builder, localShipClassOffsets);
|
||||
|
||||
const totalPlanets =
|
||||
(fixture.localPlanets ?? []).length +
|
||||
@@ -163,6 +183,8 @@ export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
if (otherVec !== null) Report.addOtherPlanet(builder, otherVec);
|
||||
if (uninhabitedVec !== null) Report.addUninhabitedPlanet(builder, uninhabitedVec);
|
||||
if (unidentifiedVec !== null) Report.addUnidentifiedPlanet(builder, unidentifiedVec);
|
||||
if (localShipClassVec !== null)
|
||||
Report.addLocalShipClass(builder, localShipClassVec);
|
||||
const reportOff = Report.endReport(builder);
|
||||
builder.finish(reportOff);
|
||||
return builder.asUint8Array();
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
// Phase 15 end-to-end coverage for the planet-production flow. Boots
|
||||
// an authenticated session, mocks the lobby + report + order routes
|
||||
// (including a seeded `Scout` ship class so the Build-Ship branch is
|
||||
// reachable), drives a click into the renderer to select a planet,
|
||||
// then walks the segmented control through three production choices.
|
||||
// The final assertion verifies that the order tab carries exactly
|
||||
// one row at all times (the collapse-by-`planetNumber` rule), that
|
||||
// the gateway received the latest choice, and that the row survives
|
||||
// a reload via `user.games.order.get`.
|
||||
|
||||
import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { ByteBuffer } from "flatbuffers";
|
||||
|
||||
import { ExecuteCommandRequestSchema } from "../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
|
||||
import { UUID } from "../../src/proto/galaxy/fbs/common";
|
||||
import {
|
||||
CommandPlanetProduce,
|
||||
PlanetProduction,
|
||||
UserGamesOrder,
|
||||
UserGamesOrderGet,
|
||||
} from "../../src/proto/galaxy/fbs/order";
|
||||
import { GameReportRequest } from "../../src/proto/galaxy/fbs/report";
|
||||
import { forgeExecuteCommandResponseJson } from "./fixtures/sign-response";
|
||||
import {
|
||||
buildMyGamesListPayload,
|
||||
type GameFixture,
|
||||
} from "./fixtures/lobby-fbs";
|
||||
import { buildReportPayload } from "./fixtures/report-fbs";
|
||||
import {
|
||||
buildOrderGetResponsePayload,
|
||||
buildOrderResponsePayload,
|
||||
type CommandResultFixture,
|
||||
} from "./fixtures/order-fbs";
|
||||
|
||||
const SESSION_ID = "phase-15-production-session";
|
||||
const GAME_ID = "15151515-1515-1515-1515-151515151515";
|
||||
const WORLD = 4000;
|
||||
const CENTRE = WORLD / 2;
|
||||
const TURN = 5;
|
||||
const SHIP_CLASS = "Scout";
|
||||
|
||||
interface MockHandle {
|
||||
get lastSubmitted(): {
|
||||
productionType: PlanetProduction;
|
||||
subject: string;
|
||||
planetNumber: number;
|
||||
} | null;
|
||||
get submitCount(): number;
|
||||
}
|
||||
|
||||
async function mockGateway(page: Page): Promise<MockHandle> {
|
||||
const game: GameFixture = {
|
||||
gameId: GAME_ID,
|
||||
gameName: "Phase 15 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: TURN,
|
||||
};
|
||||
|
||||
let storedOrder: CommandResultFixture[] = [];
|
||||
let lastReportProduction = "Drive";
|
||||
let lastSubmitted: {
|
||||
productionType: PlanetProduction;
|
||||
subject: string;
|
||||
planetNumber: number;
|
||||
} | null = null;
|
||||
let submitCount = 0;
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/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 resultCode = "ok";
|
||||
let payload: Uint8Array;
|
||||
switch (req.messageType) {
|
||||
case "lobby.my.games.list":
|
||||
payload = buildMyGamesListPayload([game]);
|
||||
break;
|
||||
case "user.games.report": {
|
||||
GameReportRequest.getRootAsGameReportRequest(
|
||||
new ByteBuffer(req.payloadBytes),
|
||||
).gameId(new UUID());
|
||||
payload = buildReportPayload({
|
||||
turn: TURN,
|
||||
mapWidth: WORLD,
|
||||
mapHeight: WORLD,
|
||||
localPlanets: [
|
||||
{
|
||||
number: 17,
|
||||
name: "Earth",
|
||||
x: CENTRE,
|
||||
y: CENTRE,
|
||||
size: 1000,
|
||||
resources: 10,
|
||||
capital: 0,
|
||||
material: 0,
|
||||
population: 850,
|
||||
colonists: 25,
|
||||
industry: 700,
|
||||
production: lastReportProduction,
|
||||
freeIndustry: 175,
|
||||
},
|
||||
],
|
||||
localShipClass: [{ name: SHIP_CLASS }],
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "user.games.order": {
|
||||
const decoded = UserGamesOrder.getRootAsUserGamesOrder(
|
||||
new ByteBuffer(req.payloadBytes),
|
||||
);
|
||||
submitCount += 1;
|
||||
const length = decoded.commandsLength();
|
||||
const fixtures: CommandResultFixture[] = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
const item = decoded.commands(i);
|
||||
if (item === null) continue;
|
||||
const cmdId = item.cmdId() ?? "";
|
||||
const inner = new CommandPlanetProduce();
|
||||
item.payload(inner);
|
||||
const productionType = inner.production();
|
||||
const subject = inner.subject() ?? "";
|
||||
const planetNumber = Number(inner.number());
|
||||
lastSubmitted = { productionType, subject, planetNumber };
|
||||
fixtures.push({
|
||||
kind: "setProductionType",
|
||||
cmdId,
|
||||
planetNumber,
|
||||
productionType: planetProductionToLiteral(productionType),
|
||||
subject,
|
||||
applied: true,
|
||||
errorCode: null,
|
||||
});
|
||||
}
|
||||
storedOrder = fixtures;
|
||||
if (lastSubmitted !== null) {
|
||||
lastReportProduction = displayFromSubmitted(lastSubmitted);
|
||||
}
|
||||
payload = buildOrderResponsePayload(GAME_ID, fixtures, Date.now());
|
||||
break;
|
||||
}
|
||||
case "user.games.order.get": {
|
||||
UserGamesOrderGet.getRootAsUserGamesOrderGet(
|
||||
new ByteBuffer(req.payloadBytes),
|
||||
);
|
||||
payload = buildOrderGetResponsePayload(
|
||||
GAME_ID,
|
||||
storedOrder,
|
||||
Date.now(),
|
||||
storedOrder.length > 0,
|
||||
);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
resultCode = "internal_error";
|
||||
payload = new Uint8Array();
|
||||
}
|
||||
|
||||
const body = await forgeExecuteCommandResponseJson({
|
||||
requestId: req.requestId,
|
||||
timestampMs: BigInt(Date.now()),
|
||||
resultCode,
|
||||
payloadBytes: payload,
|
||||
});
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
body,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
await page.route(
|
||||
"**/galaxy.gateway.v1.EdgeGateway/SubscribeEvents",
|
||||
async () => {
|
||||
await new Promise<void>(() => {});
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
get lastSubmitted() {
|
||||
return lastSubmitted;
|
||||
},
|
||||
get submitCount() {
|
||||
return submitCount;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function bootSession(page: Page): Promise<void> {
|
||||
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,
|
||||
);
|
||||
await page.evaluate(
|
||||
(gameId) => window.__galaxyDebug!.clearOrderDraft(gameId),
|
||||
GAME_ID,
|
||||
);
|
||||
}
|
||||
|
||||
async function clickPlanetCentre(page: Page): Promise<void> {
|
||||
const canvas = page.locator("canvas");
|
||||
const box = await canvas.boundingBox();
|
||||
expect(box).not.toBeNull();
|
||||
if (box === null) throw new Error("canvas has no bounding box");
|
||||
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
|
||||
}
|
||||
|
||||
function planetProductionToLiteral(
|
||||
value: PlanetProduction,
|
||||
): "MAT" | "CAP" | "DRIVE" | "WEAPONS" | "SHIELDS" | "CARGO" | "SCIENCE" | "SHIP" {
|
||||
switch (value) {
|
||||
case PlanetProduction.MAT:
|
||||
return "MAT";
|
||||
case PlanetProduction.CAP:
|
||||
return "CAP";
|
||||
case PlanetProduction.DRIVE:
|
||||
return "DRIVE";
|
||||
case PlanetProduction.WEAPONS:
|
||||
return "WEAPONS";
|
||||
case PlanetProduction.SHIELDS:
|
||||
return "SHIELDS";
|
||||
case PlanetProduction.CARGO:
|
||||
return "CARGO";
|
||||
case PlanetProduction.SCIENCE:
|
||||
return "SCIENCE";
|
||||
case PlanetProduction.SHIP:
|
||||
return "SHIP";
|
||||
default:
|
||||
throw new Error(`unexpected production enum ${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
function displayFromSubmitted(value: {
|
||||
productionType: PlanetProduction;
|
||||
subject: string;
|
||||
}): string {
|
||||
switch (value.productionType) {
|
||||
case PlanetProduction.MAT:
|
||||
return "Material";
|
||||
case PlanetProduction.CAP:
|
||||
return "Capital";
|
||||
case PlanetProduction.DRIVE:
|
||||
return "Drive";
|
||||
case PlanetProduction.WEAPONS:
|
||||
return "Weapons";
|
||||
case PlanetProduction.SHIELDS:
|
||||
return "Shields";
|
||||
case PlanetProduction.CARGO:
|
||||
return "Cargo";
|
||||
case PlanetProduction.SCIENCE:
|
||||
case PlanetProduction.SHIP:
|
||||
return value.subject;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
test("switching production three times collapses to one auto-synced row", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name.startsWith("chromium-mobile"),
|
||||
"phase 15 spec covers desktop layout; mobile inherits the same store",
|
||||
);
|
||||
|
||||
const handle = await mockGateway(page);
|
||||
await bootSession(page);
|
||||
await page.goto(`/games/${GAME_ID}/map`);
|
||||
await expect(page.getByTestId("active-view-map")).toHaveAttribute(
|
||||
"data-status",
|
||||
"ready",
|
||||
);
|
||||
|
||||
await clickPlanetCentre(page);
|
||||
const sidebar = page.getByTestId("sidebar-tool-inspector");
|
||||
await expect(sidebar.getByTestId("inspector-planet-name")).toHaveText("Earth");
|
||||
|
||||
// Initial state: report.production = "Drive" → research segment is
|
||||
// active, sub-row reveals Drive as the highlighted tech.
|
||||
await expect(
|
||||
sidebar.getByTestId("inspector-planet-production-segment-research"),
|
||||
).toHaveClass(/active/);
|
||||
|
||||
// Click 1: Industry → CAP
|
||||
await sidebar
|
||||
.getByTestId("inspector-planet-production-segment-industry")
|
||||
.click();
|
||||
await page.getByTestId("sidebar-tab-order").click();
|
||||
const orderTool = page.getByTestId("sidebar-tool-order");
|
||||
await expect(orderTool.getByTestId("order-list").locator("li")).toHaveCount(
|
||||
1,
|
||||
);
|
||||
await expect(orderTool.getByTestId("order-command-label-0")).toContainText(
|
||||
"Capital",
|
||||
);
|
||||
await expect(orderTool.getByTestId("order-command-status-0")).toHaveText(
|
||||
"applied",
|
||||
);
|
||||
|
||||
// Click 2: Materials → MAT (replaces CAP via collapse)
|
||||
await page.getByTestId("sidebar-tab-inspector").click();
|
||||
await sidebar
|
||||
.getByTestId("inspector-planet-production-segment-materials")
|
||||
.click();
|
||||
await page.getByTestId("sidebar-tab-order").click();
|
||||
await expect(orderTool.getByTestId("order-list").locator("li")).toHaveCount(
|
||||
1,
|
||||
);
|
||||
await expect(orderTool.getByTestId("order-command-label-0")).toContainText(
|
||||
"Material",
|
||||
);
|
||||
|
||||
// Click 3: Build Ship → expand sub-row → Scout (replaces MAT)
|
||||
await page.getByTestId("sidebar-tab-inspector").click();
|
||||
await sidebar
|
||||
.getByTestId("inspector-planet-production-segment-ship")
|
||||
.click();
|
||||
await sidebar
|
||||
.getByTestId(`inspector-planet-production-ship-${SHIP_CLASS}`)
|
||||
.click();
|
||||
await page.getByTestId("sidebar-tab-order").click();
|
||||
await expect(orderTool.getByTestId("order-list").locator("li")).toHaveCount(
|
||||
1,
|
||||
);
|
||||
await expect(orderTool.getByTestId("order-command-label-0")).toContainText(
|
||||
SHIP_CLASS,
|
||||
);
|
||||
await expect(orderTool.getByTestId("order-sync")).toHaveAttribute(
|
||||
"data-sync-status",
|
||||
"synced",
|
||||
);
|
||||
|
||||
expect(handle.lastSubmitted).not.toBeNull();
|
||||
expect(handle.lastSubmitted!.planetNumber).toBe(17);
|
||||
expect(handle.lastSubmitted!.productionType).toBe(PlanetProduction.SHIP);
|
||||
expect(handle.lastSubmitted!.subject).toBe(SHIP_CLASS);
|
||||
expect(handle.submitCount).toBeGreaterThanOrEqual(3);
|
||||
|
||||
// Reload: the layout polls user.games.order.get on boot, so the
|
||||
// row is restored from the server's stored state even when the
|
||||
// local cache is wiped.
|
||||
await page.reload();
|
||||
await expect(page.getByTestId("active-view-map")).toHaveAttribute(
|
||||
"data-status",
|
||||
"ready",
|
||||
);
|
||||
await page.getByTestId("sidebar-tab-order").click();
|
||||
await expect(orderTool.getByTestId("order-list").locator("li")).toHaveCount(
|
||||
1,
|
||||
);
|
||||
await expect(orderTool.getByTestId("order-command-label-0")).toContainText(
|
||||
SHIP_CLASS,
|
||||
);
|
||||
});
|
||||
@@ -131,6 +131,7 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
lastSubmittedName = submittedName;
|
||||
const applied = opts.submitOutcome === "applied";
|
||||
fixtures.push({
|
||||
kind: "planetRename",
|
||||
cmdId,
|
||||
planetNumber: Number(inner.number()),
|
||||
name: submittedName,
|
||||
@@ -140,7 +141,10 @@ async function mockGateway(page: Page, opts: MockOpts): Promise<MockHandle> {
|
||||
}
|
||||
if (opts.submitOutcome === "applied") {
|
||||
storedOrder = fixtures;
|
||||
lastReportName = fixtures[0]?.name ?? lastReportName;
|
||||
const head = fixtures[0];
|
||||
if (head !== undefined && head.kind === "planetRename") {
|
||||
lastReportName = head.name;
|
||||
}
|
||||
}
|
||||
payload = buildOrderResponsePayload(GAME_ID, fixtures, Date.now());
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user