ui/phase-13: planet inspector — read-only
Plumbs the map → inspector pathway: a click on a planet selects it through the new SelectionStore, the sidebar Inspector tab swaps its empty-state copy for a per-kind read-only field set, and a mobile-only bottom-sheet mirrors the same content over the map. Field projection in api/game-state.ts now surfaces every documented planet field.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
// Component tests for the Phase 10 in-game shell sidebar. Validates
|
||||
// the default selected tab, the Calculator / Inspector / Order
|
||||
// switching, the empty-state copy that matches the IA section, and
|
||||
// the `?sidebar=` URL seed convention used by the mobile bottom-tabs.
|
||||
// switching, the empty-state copy that matches the IA section, the
|
||||
// `?sidebar=` URL seed convention used by the mobile bottom-tabs,
|
||||
// and the Phase 13 selection-driven planet inspector content.
|
||||
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { fireEvent, render } from "@testing-library/svelte";
|
||||
@@ -14,6 +15,15 @@ import {
|
||||
} from "vitest";
|
||||
|
||||
import { i18n } from "../src/lib/i18n/index.svelte";
|
||||
import {
|
||||
GAME_STATE_CONTEXT_KEY,
|
||||
GameStateStore,
|
||||
} from "../src/lib/game-state.svelte";
|
||||
import {
|
||||
SELECTION_CONTEXT_KEY,
|
||||
SelectionStore,
|
||||
} from "../src/lib/selection.svelte";
|
||||
import type { GameReport, ReportPlanet } from "../src/api/game-state";
|
||||
|
||||
const pageMock = vi.hoisted(() => ({
|
||||
url: new URL("http://localhost/games/g1/map"),
|
||||
@@ -26,6 +36,53 @@ vi.mock("$app/state", () => ({
|
||||
|
||||
import Sidebar from "../src/lib/sidebar/sidebar.svelte";
|
||||
|
||||
function makePlanet(overrides: Partial<ReportPlanet>): ReportPlanet {
|
||||
return {
|
||||
number: 0,
|
||||
name: "",
|
||||
x: 0,
|
||||
y: 0,
|
||||
kind: "local",
|
||||
owner: null,
|
||||
size: null,
|
||||
resources: null,
|
||||
industryStockpile: null,
|
||||
materialsStockpile: null,
|
||||
industry: null,
|
||||
population: null,
|
||||
colonists: null,
|
||||
production: null,
|
||||
freeIndustry: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function makeReport(planets: ReportPlanet[]): GameReport {
|
||||
return {
|
||||
turn: 1,
|
||||
mapWidth: 1000,
|
||||
mapHeight: 1000,
|
||||
planetCount: planets.length,
|
||||
planets,
|
||||
};
|
||||
}
|
||||
|
||||
function withStores(report: GameReport | null): {
|
||||
gameState: GameStateStore;
|
||||
selection: SelectionStore;
|
||||
context: Map<unknown, unknown>;
|
||||
} {
|
||||
const gameState = new GameStateStore();
|
||||
gameState.report = report;
|
||||
gameState.status = report === null ? "idle" : "ready";
|
||||
const selection = new SelectionStore();
|
||||
const context = new Map<unknown, unknown>([
|
||||
[GAME_STATE_CONTEXT_KEY, gameState],
|
||||
[SELECTION_CONTEXT_KEY, selection],
|
||||
]);
|
||||
return { gameState, selection, context };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
i18n.resetForTests("en");
|
||||
pageMock.url = new URL("http://localhost/games/g1/map");
|
||||
@@ -95,4 +152,63 @@ describe("game-shell sidebar", () => {
|
||||
await fireEvent.click(ui.getByTestId("sidebar-close"));
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("inspector tab swaps to the planet view when a planet is selected", async () => {
|
||||
const planet = makePlanet({
|
||||
number: 17,
|
||||
name: "Galactica",
|
||||
kind: "local",
|
||||
x: 50,
|
||||
y: 75,
|
||||
size: 1000,
|
||||
resources: 10,
|
||||
population: 800,
|
||||
colonists: 0,
|
||||
industry: 600,
|
||||
industryStockpile: 0,
|
||||
materialsStockpile: 0,
|
||||
production: "drive",
|
||||
freeIndustry: 200,
|
||||
});
|
||||
const { selection, context } = withStores(makeReport([planet]));
|
||||
const ui = render(
|
||||
Sidebar,
|
||||
{
|
||||
props: { open: false, onClose: () => {} },
|
||||
context,
|
||||
},
|
||||
);
|
||||
expect(ui.getByTestId("sidebar-tool-inspector")).toHaveTextContent(
|
||||
"select an object on the map",
|
||||
);
|
||||
|
||||
selection.selectPlanet(17);
|
||||
await Promise.resolve();
|
||||
expect(ui.queryByText("select an object on the map")).toBeNull();
|
||||
expect(ui.getByTestId("inspector-planet")).toHaveAttribute(
|
||||
"data-planet-id",
|
||||
"17",
|
||||
);
|
||||
expect(ui.getByTestId("inspector-planet-name")).toHaveTextContent(
|
||||
"Galactica",
|
||||
);
|
||||
});
|
||||
|
||||
test("selection that points at a missing planet falls back to the empty state", () => {
|
||||
const { selection, context } = withStores(
|
||||
makeReport([makePlanet({ number: 1, name: "Visible", kind: "local" })]),
|
||||
);
|
||||
selection.selectPlanet(999);
|
||||
const ui = render(
|
||||
Sidebar,
|
||||
{
|
||||
props: { open: false, onClose: () => {} },
|
||||
context,
|
||||
},
|
||||
);
|
||||
expect(ui.queryByTestId("inspector-planet")).toBeNull();
|
||||
expect(ui.getByTestId("sidebar-tool-inspector")).toHaveTextContent(
|
||||
"select an object on the map",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user