// Vitest coverage for the Phase 20 dismantle confirmation. The // inspector requires an explicit second click ("colonists die") when // the player tries to dismantle a colonist-laden group over a // foreign planet — engine rule reference: // `controller/ship_group.go.shipGroupDismantle:177-179` (over a // foreign planet, `UnloadColonists` is not called and the cargo is // lost). import "@testing-library/jest-dom/vitest"; import "fake-indexeddb/auto"; import { fireEvent, render, waitFor } from "@testing-library/svelte"; import { afterEach, beforeEach, describe, expect, test } from "vitest"; import { i18n } from "../src/lib/i18n/index.svelte"; import type { ReportLocalShipGroup, ReportPlanet, ShipClassSummary, } from "../src/api/game-state"; import ShipGroup, { type ShipGroupSelection, } from "../src/lib/inspectors/ship-group.svelte"; import { ORDER_DRAFT_CONTEXT_KEY, OrderDraftStore, } from "../src/sync/order-draft.svelte"; import { IDBCache } from "../src/platform/store/idb-cache"; import { openGalaxyDB, type GalaxyDB } from "../src/platform/store/idb"; import type { Cache } from "../src/platform/store/index"; import type { IDBPDatabase } from "idb"; const GAME_ID = "11111111-2222-3333-4444-555555555555"; let db: IDBPDatabase; let dbName: string; let cache: Cache; let draft: OrderDraftStore; const PLANETS: ReportPlanet[] = [ { number: 99, name: "Outpost", x: 100, y: 100, kind: "other", owner: "Foreign", size: 500, resources: 5, industryStockpile: 0, materialsStockpile: 0, industry: 500, population: 500, colonists: 100, production: "Capital", freeIndustry: 500, }, { number: 17, name: "Castle", x: 50, y: 50, kind: "local", owner: null, size: 1000, resources: 5, industryStockpile: 0, materialsStockpile: 0, industry: 1000, population: 1000, colonists: 100, production: "Capital", freeIndustry: 1000, }, ]; const SHIP_CLASS_FRONTIER: ShipClassSummary = { name: "Frontier", drive: 5, armament: 0, weapons: 0, shields: 0, cargo: 1, }; beforeEach(async () => { dbName = `galaxy-ship-group-dismantle-${crypto.randomUUID()}`; db = await openGalaxyDB(dbName); cache = new IDBCache(db); draft = new OrderDraftStore(); await draft.init({ cache, gameId: GAME_ID }); i18n.resetForTests("en"); }); afterEach(async () => { draft.dispose(); db.close(); await new Promise((resolve) => { const req = indexedDB.deleteDatabase(dbName); req.onsuccess = () => resolve(); req.onerror = () => resolve(); req.onblocked = () => resolve(); }); }); function group( overrides: Partial = {}, ): ReportLocalShipGroup { return { id: "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", count: 2, class: "Frontier", tech: { drive: 1, weapons: 0, shields: 0, cargo: 1 }, cargo: "COL", load: 1.5, destination: 99, origin: null, range: null, speed: 0, mass: 12, state: "In_Orbit", fleet: null, ...overrides, }; } function mount(g: ReportLocalShipGroup) { const selection: ShipGroupSelection = { variant: "local", group: g }; const context = new Map([ [ORDER_DRAFT_CONTEXT_KEY, draft], ]); return render(ShipGroup, { props: { selection, planets: PLANETS, localShipClass: [SHIP_CLASS_FRONTIER], localFleets: [], otherRaces: ["Aliens"], mapWidth: 1000, mapHeight: 1000, localPlayerDrive: 5, localPlayerWeapons: 1, localPlayerShields: 1, localPlayerCargo: 2, }, context, }); } describe("ship-group inspector — dismantle confirmation", () => { test("first click on dismantle of foreign-COL group shows the warning and adds nothing", async () => { const ui = mount(group()); await fireEvent.click(ui.getByTestId("inspector-ship-group-action-dismantle")); expect( ui.getByTestId("inspector-ship-group-form-dismantle-warning"), ).toBeInTheDocument(); const confirm = ui.getByTestId( "inspector-ship-group-form-dismantle-confirm", ); expect(confirm).toHaveTextContent(/colonists die/i); await fireEvent.click(confirm); expect(draft.commands).toHaveLength(0); }); test("second click on the colonists-die confirm emits dismantleShipGroup", async () => { const ui = mount(group()); await fireEvent.click(ui.getByTestId("inspector-ship-group-action-dismantle")); const confirm = ui.getByTestId( "inspector-ship-group-form-dismantle-confirm", ); await fireEvent.click(confirm); await fireEvent.click(confirm); await waitFor(() => expect(draft.commands).toHaveLength(1)); const cmd = draft.commands[0]!; expect(cmd.kind).toBe("dismantleShipGroup"); }); test("dismantle over own planet skips the warning even with COL aboard", async () => { const ui = mount(group({ destination: 17 })); await fireEvent.click(ui.getByTestId("inspector-ship-group-action-dismantle")); expect( ui.queryByTestId("inspector-ship-group-form-dismantle-warning"), ).toBeNull(); await fireEvent.click( ui.getByTestId("inspector-ship-group-form-dismantle-confirm"), ); await waitFor(() => expect(draft.commands).toHaveLength(1)); expect(draft.commands[0]!.kind).toBe("dismantleShipGroup"); }); test("dismantle over foreign planet without colonists skips the warning", async () => { const ui = mount(group({ cargo: "NONE", load: 0 })); await fireEvent.click(ui.getByTestId("inspector-ship-group-action-dismantle")); expect( ui.queryByTestId("inspector-ship-group-form-dismantle-warning"), ).toBeNull(); await fireEvent.click( ui.getByTestId("inspector-ship-group-form-dismantle-confirm"), ); await waitFor(() => expect(draft.commands).toHaveLength(1)); }); });