326 lines
9.5 KiB
JavaScript
326 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);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|