import { describe, expect, it } from "vitest"; import { buildingShopAt, dialogueActiveInWorld, dialogueChoicesFromNode, inventorySummary, playerStatsFromSnapshot, seedListFromSnapshot, shopRowsFromDef, } from "../src/renderer/playtest-hud.js"; describe("playerStatsFromSnapshot", () => { it("reads gold, stamina, seed and inventory from the player entity", () => { const stats = playerStatsFromSnapshot([ { id: "plot:0:0", type: "plot" }, { id: "player", type: "player", gold: 42, stamina: 30, selectedSeed: "carrot-seed", inventory: { carrot: 3, "carrot-seed": 1 } }, ]); expect(stats.gold).toBe(42); expect(stats.stamina).toBe(30); expect(stats.selectedSeed).toBe("carrot-seed"); expect(stats.inventory).toEqual({ carrot: 3, "carrot-seed": 1 }); }); it("defaults when the player or fields are missing", () => { const stats = playerStatsFromSnapshot([]); expect(stats.gold).toBe(0); expect(stats.stamina).toBe(0); expect(stats.selectedSeed).toBeNull(); expect(stats.inventory).toEqual({}); }); it("drops zero/negative inventory quantities", () => { const stats = playerStatsFromSnapshot([ { id: "player", type: "player", inventory: { carrot: 2, hoe: 0, weed: -1 } }, ]); expect(stats.inventory).toEqual({ carrot: 2 }); }); }); describe("inventorySummary", () => { it("joins inventory items", () => { expect(inventorySummary({ inventory: { carrot: 3, hoe: 1 }, gold: 0, stamina: 0, selectedSeed: null })).toBe( "carrot ×3 · hoe ×1", ); }); it("shows a placeholder for an empty inventory", () => { expect(inventorySummary({ inventory: {}, gold: 0, stamina: 0, selectedSeed: null })).toBe("items: —"); }); }); describe("seedListFromSnapshot", () => { const world = [ { id: "player", type: "player", inventory: { "carrot-seed": 2, carrot: 3, hoe: 1 } }, ]; const items: Array<{ id: string; name: string; kind: string }> = [ { id: "carrot-seed", name: "Carrot Seeds", kind: "seed" }, { id: "carrot", name: "Carrot", kind: "material" }, { id: "hoe", name: "Hoe", kind: "tool" }, ]; it("keeps only seed-kind items when an item catalog is given", () => { expect(seedListFromSnapshot(world, items as never)).toEqual([{ id: "carrot-seed", name: "Carrot Seeds", qty: 2 }]); }); it("offers every inventory item without a catalog (ws parity)", () => { expect(seedListFromSnapshot(world)).toEqual([ { id: "carrot-seed", name: "carrot-seed", qty: 2 }, { id: "carrot", name: "carrot", qty: 3 }, { id: "hoe", name: "hoe", qty: 1 }, ]); }); it("returns an empty list for an empty inventory", () => { expect(seedListFromSnapshot([])).toEqual([]); }); }); describe("shopRowsFromDef", () => { it("maps buys/sells with the shop-viewpoint convention and names from the catalog", () => { const rows = shopRowsFromDef( { id: "general-store", name: "General Store", buys: [{ itemId: "carrot-seed", price: 20 }], sells: [{ itemId: "carrot", price: 15, stock: 5 }], } as never, { carrot: 2 }, [ { id: "carrot-seed", name: "Carrot Seeds", kind: "seed" }, { id: "carrot", name: "Carrot", kind: "material" }, ] as never, ); expect(rows.shopId).toBe("general-store"); expect(rows.buyRows).toEqual([{ itemId: "carrot-seed", name: "Carrot Seeds", price: 20, stock: null, owned: 0 }]); expect(rows.sellRows).toEqual([{ itemId: "carrot", name: "Carrot", price: 15, stock: 5, owned: 2 }]); }); it("falls back to the item id without a catalog and handles empty lines", () => { const rows = shopRowsFromDef({ id: "s", buys: [], sells: [{ itemId: "egg", price: 40 }] } as never, undefined); expect(rows.buyRows).toEqual([]); expect(rows.sellRows).toEqual([{ itemId: "egg", name: "egg", price: 40, stock: null, owned: 0 }]); }); }); describe("dialogueChoicesFromNode", () => { it("maps text and choice labels", () => { const view = dialogueChoicesFromNode({ dialogueId: "mayor", nodeId: "n1", text: "Hello!", choices: [{ text: "Hi", to: "n2" }, { text: "Bye", to: "n3" }], }); expect(view.text).toBe("Hello!"); expect(view.choices).toEqual(["Hi", "Bye"]); }); it("returns empty for an absent dialogue state", () => { expect(dialogueChoicesFromNode(undefined)).toEqual({ text: "", choices: [] }); }); }); describe("buildingShopAt", () => { const world = [ { id: "building:shed:2:3", type: "building", x: 2, y: 3, w: 2, h: 2, shop: "general-store" }, { id: "building:barn:0:0", type: "building", x: 0, y: 0, w: 1, h: 1 }, ]; it("finds the shop whose footprint contains the tile", () => { expect(buildingShopAt(world, { x: 3, y: 4 })).toEqual({ buildingId: "building:shed:2:3", shopId: "general-store" }); }); it("misses outside the footprint, on shopless buildings and on empty world", () => { expect(buildingShopAt(world, { x: 4, y: 4 })).toBeUndefined(); expect(buildingShopAt(world, { x: 0, y: 0 })).toBeUndefined(); expect(buildingShopAt([], { x: 0, y: 0 })).toBeUndefined(); }); }); describe("dialogueActiveInWorld", () => { it("detects the dialogue:active sentinel entity", () => { expect(dialogueActiveInWorld([{ id: "dialogue:active", type: "dialogue" }])).toBe(true); expect(dialogueActiveInWorld([{ id: "player", type: "player" }])).toBe(false); expect(dialogueActiveInWorld([])).toBe(false); }); });