// Vitest coverage for the F8-10 fleets table active view. // Mounts the component against a synthetic `RenderedReportSource` // and a real `SelectionStore`; verifies the click semantics for both // on-planet (focus the destination planet) and in-space (centre the // camera on the interpolated point, leave selection untouched). import "@testing-library/jest-dom/vitest"; import { fireEvent, render } from "@testing-library/svelte"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { i18n } from "../src/lib/i18n/index.svelte"; import type { GameReport, ReportLocalFleet, ReportPlanet, } from "../src/api/game-state"; import { RENDERED_REPORT_CONTEXT_KEY } from "../src/lib/rendered-report.svelte"; import { SELECTION_CONTEXT_KEY, SelectionStore, } from "../src/lib/selection.svelte"; import { activeView } from "../src/lib/app-nav.svelte"; import { EMPTY_SHIP_GROUPS } from "./helpers/empty-ship-groups"; const pageMock = vi.hoisted(() => ({ url: new URL("http://localhost/games/g1/table/fleets"), params: { id: "g1" } as Record, })); const gotoMock = vi.hoisted(() => vi.fn()); vi.mock("$app/state", () => ({ page: pageMock, })); vi.mock("$app/navigation", () => ({ goto: gotoMock, pushState: vi.fn(), replaceState: vi.fn(), })); import TableFleets from "../src/lib/active-view/table-fleets.svelte"; let selection: SelectionStore; beforeEach(() => { selection = new SelectionStore(); i18n.resetForTests("en"); pageMock.params = { id: "g1" }; gotoMock.mockClear(); activeView.reset(); }); afterEach(() => { selection.dispose(); }); function planet(num: number, x = 0, y = 0): ReportPlanet { return { number: num, name: `P${num}`, x, y, kind: "local", owner: null, size: null, resources: null, industryStockpile: null, materialsStockpile: null, industry: null, population: null, colonists: null, production: null, freeIndustry: null, }; } function fleet( overrides: Partial & Pick, ): ReportLocalFleet { return { groupCount: 1, origin: null, range: null, speed: 0, state: "In_Orbit", ...overrides, }; } function makeReport(opts: { planets?: ReportPlanet[]; fleets?: ReportLocalFleet[]; }): GameReport { return { turn: 1, mapWidth: 1000, mapHeight: 1000, planetCount: opts.planets?.length ?? 0, planets: opts.planets ?? [], race: "Me", localShipClass: [], routes: [], localPlayerDrive: 0, localPlayerWeapons: 0, localPlayerShields: 0, localPlayerCargo: 0, ...EMPTY_SHIP_GROUPS, localFleets: opts.fleets ?? [], }; } function mount(report: GameReport | null) { const renderedReport = { get report() { return report; }, }; const context = new Map([ [RENDERED_REPORT_CONTEXT_KEY, renderedReport], [SELECTION_CONTEXT_KEY, selection], ]); return render(TableFleets, { context }); } describe("fleets table", () => { test("renders a loading placeholder before the report lands", () => { const ui = mount(null); expect(ui.getByTestId("fleets-loading")).toBeInTheDocument(); }); test("renders an empty placeholder when no fleets exist", () => { const ui = mount(makeReport({ planets: [planet(1)] })); expect(ui.getByTestId("fleets-empty")).toBeInTheDocument(); }); test("renders one row per fleet", () => { const ui = mount( makeReport({ planets: [planet(1), planet(2)], fleets: [ fleet({ name: "Alpha", destination: 1 }), fleet({ name: "Bravo", destination: 2, origin: 1, range: 4, state: "In_Space", }), ], }), ); expect(ui.getAllByTestId("fleets-row")).toHaveLength(2); }); test("planet dropdown filters by destination OR origin", async () => { const ui = mount( makeReport({ planets: [planet(1), planet(2), planet(3)], fleets: [ fleet({ name: "Alpha", destination: 1 }), fleet({ name: "Bravo", destination: 3, origin: 2, range: 4, state: "In_Space", }), ], }), ); const sel = ui.getByTestId("fleets-filter-planet") as HTMLSelectElement; await fireEvent.change(sel, { target: { value: "2" } }); const names = ui .getAllByTestId("fleets-row") .map((r) => r.getAttribute("data-name")); expect(names).toEqual(["Bravo"]); }); test("click on on-planet fleet focuses the destination planet", async () => { const ui = mount( makeReport({ planets: [planet(7)], fleets: [fleet({ name: "Alpha", destination: 7 })], }), ); await fireEvent.click(ui.getByTestId("fleets-row")); expect(selection.selected).toEqual({ kind: "planet", id: 7 }); expect(selection.consumePendingFocus()).toEqual({ kind: "planet", id: 7, }); expect(activeView.view).toBe("map"); }); test("click on in-space fleet centres camera point and leaves selection alone", async () => { const ui = mount( makeReport({ planets: [planet(1, 0, 0), planet(2, 100, 0)], fleets: [ fleet({ name: "Bravo", destination: 2, origin: 1, range: 25, state: "In_Space", }), ], }), ); expect(selection.selected).toBeNull(); await fireEvent.click(ui.getByTestId("fleets-row")); expect(selection.selected).toBeNull(); // 25 world units away from dest (2 at x=100) toward origin (1 at x=0): // dest + (range/total)*(origin-dest) = 100 + 0.25 * -100 = 75 expect(selection.consumePendingCenter()).toEqual({ x: 75, y: 0 }); expect(activeView.view).toBe("map"); }); });