aether-shards/test/ui/character-sheet/inventory-integration.test.js
Matthew Mone ac0f3cc396 Enhance testing and integration of inventory and character management systems
Add comprehensive tests for the InventoryManager and InventoryContainer to validate item management functionalities. Implement integration tests for the CharacterSheet component, ensuring proper interaction with the inventory system. Update the Explorer class to support new inventory features and maintain backward compatibility. Refactor related components for improved clarity and performance.
2025-12-27 16:54:03 -08:00

325 lines
9.5 KiB
JavaScript

import { expect } from "@esm-bundle/chai";
import { CharacterSheet } from "../../../src/ui/components/CharacterSheet.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);
});
});
});