import { expect } from "@esm-bundle/chai"; import { CharacterSheet } from "../../../src/ui/components/character-sheet.js"; import { Explorer } from "../../../src/units/Explorer.js"; import { InventoryManager } from "../../../src/managers/InventoryManager.js"; import { InventoryContainer } from "../../../src/models/InventoryContainer.js"; import { Item } from "../../../src/items/Item.js"; import vanguardDef from "../../../src/assets/data/classes/vanguard.json" with { type: "json", }; // Import SkillTreeUI to register the custom element import "../../../src/ui/components/SkillTreeUI.js"; describe("UI: CharacterSheet - Inventory Integration", () => { let element; let container; let testUnit; let inventoryManager; let mockItemRegistry; beforeEach(() => { container = document.createElement("div"); document.body.appendChild(container); element = document.createElement("character-sheet"); container.appendChild(element); // Create mock item registry mockItemRegistry = { get: (defId) => { const items = { "ITEM_RUSTY_BLADE": new Item({ id: "ITEM_RUSTY_BLADE", name: "Rusty Blade", type: "WEAPON", stats: { attack: 3 }, }), "ITEM_SCRAP_PLATE": new Item({ id: "ITEM_SCRAP_PLATE", name: "Scrap Plate", type: "ARMOR", stats: { defense: 3 }, }), "ITEM_HEALTH_POTION": new Item({ id: "ITEM_HEALTH_POTION", name: "Health Potion", type: "CONSUMABLE", stats: {}, }), }; return items[defId] || null; }, }; // Create inventory manager const runStash = new InventoryContainer("RUN_LOOT"); const hubStash = new InventoryContainer("HUB_VAULT"); inventoryManager = new InventoryManager(mockItemRegistry, runStash, hubStash); // Create test Explorer unit testUnit = new Explorer("test-unit-1", "Test Vanguard", "CLASS_VANGUARD", vanguardDef); testUnit.classMastery["CLASS_VANGUARD"] = { level: 5, xp: 250, skillPoints: 2, unlockedNodes: [], }; testUnit.recalculateBaseStats(vanguardDef); testUnit.currentHealth = 100; testUnit.maxHealth = 120; // Ensure loadout is initialized (should be done in constructor, but ensure it exists) if (!testUnit.loadout) { testUnit.loadout = { mainHand: null, offHand: null, body: null, accessory: null, belt: [null, null], }; } }); afterEach(() => { if (container.parentNode) { container.parentNode.removeChild(container); } }); // Helper to wait for LitElement update async function waitForUpdate() { await element.updateComplete; await new Promise((resolve) => setTimeout(resolve, 10)); } // Helper to query shadow DOM function queryShadow(selector) { return element.shadowRoot?.querySelector(selector); } function queryShadowAll(selector) { return element.shadowRoot?.querySelectorAll(selector) || []; } describe("CoA 1: inventoryManager property", () => { it("should accept inventoryManager as property", async () => { element.unit = testUnit; element.inventoryManager = inventoryManager; await waitForUpdate(); expect(element.inventoryManager).to.equal(inventoryManager); }); it("should work without inventoryManager (legacy mode)", async () => { element.unit = testUnit; element.inventory = []; await waitForUpdate(); expect(element.inventoryManager).to.be.null; // Should still render const inventoryGrid = queryShadow(".inventory-grid"); expect(inventoryGrid).to.exist; }); }); describe("CoA 2: rendering stash items", () => { it("should render items from runStash in DUNGEON mode", async () => { // Add items to run stash inventoryManager.runStash.addItem({ uid: "ITEM_001", defId: "ITEM_RUSTY_BLADE", isNew: true, quantity: 1, }); inventoryManager.runStash.addItem({ uid: "ITEM_002", defId: "ITEM_SCRAP_PLATE", isNew: false, quantity: 1, }); element.unit = testUnit; element.inventoryManager = inventoryManager; element.gameMode = "DUNGEON"; element.activeTab = "INVENTORY"; await waitForUpdate(); const itemCards = queryShadowAll(".item-card"); expect(itemCards.length).to.equal(2); }); it("should render items from hubStash in HUB mode", async () => { // Add items to hub stash inventoryManager.hubStash.addItem({ uid: "ITEM_001", defId: "ITEM_RUSTY_BLADE", isNew: true, quantity: 1, }); element.unit = testUnit; element.inventoryManager = inventoryManager; element.gameMode = "HUB"; element.activeTab = "INVENTORY"; await waitForUpdate(); const itemCards = queryShadowAll(".item-card"); expect(itemCards.length).to.equal(1); }); it("should convert ItemInstance to UI format with name and type", async () => { inventoryManager.runStash.addItem({ uid: "ITEM_001", defId: "ITEM_RUSTY_BLADE", isNew: true, quantity: 1, }); element.unit = testUnit; element.inventoryManager = inventoryManager; element.gameMode = "DUNGEON"; element.activeTab = "INVENTORY"; await waitForUpdate(); const itemCard = queryShadow(".item-card"); expect(itemCard).to.exist; expect(itemCard.getAttribute("title")).to.equal("Rusty Blade"); }); }); describe("CoA 3: equipping items via inventoryManager", () => { it("should equip item using inventoryManager.equipItem", async () => { // Add item to stash const itemInstance = { uid: "ITEM_001", defId: "ITEM_RUSTY_BLADE", isNew: true, quantity: 1, }; inventoryManager.runStash.addItem(itemInstance); element.unit = testUnit; element.inventoryManager = inventoryManager; element.gameMode = "DUNGEON"; element.activeTab = "INVENTORY"; element.selectedSlot = "WEAPON"; await waitForUpdate(); let equipEventFired = false; element.addEventListener("equip-item", () => { equipEventFired = true; }); const itemCard = queryShadow(".item-card"); itemCard.click(); await waitForUpdate(); expect(equipEventFired).to.be.true; // Item should be equipped to loadout expect(testUnit.loadout.mainHand).to.exist; expect(testUnit.loadout.mainHand.defId).to.equal("ITEM_RUSTY_BLADE"); // Item should be removed from stash expect(inventoryManager.runStash.findItem("ITEM_001")).to.be.null; }); it("should map legacy slot types to new slot types", async () => { const itemInstance = { uid: "ITEM_001", defId: "ITEM_RUSTY_BLADE", isNew: true, quantity: 1, }; inventoryManager.runStash.addItem(itemInstance); element.unit = testUnit; element.inventoryManager = inventoryManager; element.gameMode = "DUNGEON"; element.activeTab = "INVENTORY"; element.selectedSlot = "WEAPON"; // Legacy slot type await waitForUpdate(); const itemCard = queryShadow(".item-card"); itemCard.click(); await waitForUpdate(); // Should equip to MAIN_HAND (mapped from WEAPON) expect(testUnit.loadout.mainHand).to.exist; }); it("should filter inventory by slot type", async () => { inventoryManager.runStash.addItem({ uid: "ITEM_001", defId: "ITEM_RUSTY_BLADE", isNew: true, quantity: 1, }); inventoryManager.runStash.addItem({ uid: "ITEM_002", defId: "ITEM_SCRAP_PLATE", isNew: false, quantity: 1, }); element.unit = testUnit; element.inventoryManager = inventoryManager; element.gameMode = "DUNGEON"; element.activeTab = "INVENTORY"; element.selectedSlot = "WEAPON"; await waitForUpdate(); // Should only show weapons const itemCards = queryShadowAll(".item-card"); expect(itemCards.length).to.equal(1); }); }); describe("CoA 4: fallback to legacy system", () => { it("should use legacy inventory array when inventoryManager is not provided", async () => { const legacyItem = new Item({ id: "ITEM_LEGACY", name: "Legacy Item", type: "WEAPON", stats: { attack: 5 }, }); element.unit = testUnit; element.inventory = [legacyItem]; element.selectedSlot = "WEAPON"; element.activeTab = "INVENTORY"; await waitForUpdate(); const itemCards = queryShadowAll(".item-card"); expect(itemCards.length).to.equal(1); }); it("should use legacy equip logic when item has no uid", async () => { const legacyItem = new Item({ id: "ITEM_LEGACY", name: "Legacy Item", type: "WEAPON", stats: { attack: 5 }, canEquip: () => true, }); element.unit = testUnit; element.inventory = [legacyItem]; element.selectedSlot = "WEAPON"; element.activeTab = "INVENTORY"; await waitForUpdate(); let equipEventFired = false; element.addEventListener("equip-item", () => { equipEventFired = true; }); const itemCard = queryShadow(".item-card"); itemCard.click(); await waitForUpdate(); expect(equipEventFired).to.be.true; // Should use legacy equipment system expect(testUnit.equipment.weapon).to.equal(legacyItem); }); }); });