212 lines
6.1 KiB
JavaScript
212 lines
6.1 KiB
JavaScript
|
|
import { expect } from "@esm-bundle/chai";
|
||
|
|
import { Explorer } from "../../../src/units/Explorer.js";
|
||
|
|
import { Item } from "../../../src/items/Item.js";
|
||
|
|
|
||
|
|
// Mock Class Definitions
|
||
|
|
const CLASS_VANGUARD = {
|
||
|
|
id: "CLASS_VANGUARD",
|
||
|
|
base_stats: { health: 100, attack: 10, defense: 5, speed: 10, magic: 0, willpower: 5, movement: 4, tech: 0 },
|
||
|
|
growth_rates: { health: 10, attack: 1 },
|
||
|
|
};
|
||
|
|
|
||
|
|
describe("Unit: Explorer - Inventory Integration", () => {
|
||
|
|
let explorer;
|
||
|
|
let mockItemRegistry;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
explorer = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
||
|
|
|
||
|
|
// Ensure loadout is initialized (should be in constructor, but ensure it exists)
|
||
|
|
if (!explorer.loadout) {
|
||
|
|
explorer.loadout = {
|
||
|
|
mainHand: null,
|
||
|
|
offHand: null,
|
||
|
|
body: null,
|
||
|
|
accessory: null,
|
||
|
|
belt: [null, null],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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, speed: -1 },
|
||
|
|
}),
|
||
|
|
"ITEM_RELIC": new Item({
|
||
|
|
id: "ITEM_RELIC",
|
||
|
|
name: "Power Relic",
|
||
|
|
type: "RELIC",
|
||
|
|
stats: { magic: 5, willpower: 2 },
|
||
|
|
}),
|
||
|
|
};
|
||
|
|
return items[defId] || null;
|
||
|
|
},
|
||
|
|
};
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("loadout property", () => {
|
||
|
|
it("CoA 1: should have loadout property with all slots", () => {
|
||
|
|
expect(explorer.loadout).to.exist;
|
||
|
|
expect(explorer.loadout.mainHand).to.be.null;
|
||
|
|
expect(explorer.loadout.offHand).to.be.null;
|
||
|
|
expect(explorer.loadout.body).to.be.null;
|
||
|
|
expect(explorer.loadout.accessory).to.be.null;
|
||
|
|
expect(explorer.loadout.belt).to.be.an("array");
|
||
|
|
expect(explorer.loadout.belt.length).to.equal(2);
|
||
|
|
expect(explorer.loadout.belt[0]).to.be.null;
|
||
|
|
expect(explorer.loadout.belt[1]).to.be.null;
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("recalculateStats", () => {
|
||
|
|
it("CoA 2: should apply mainHand item stats", () => {
|
||
|
|
const initialAttack = explorer.baseStats.attack;
|
||
|
|
|
||
|
|
explorer.loadout.mainHand = {
|
||
|
|
uid: "ITEM_001",
|
||
|
|
defId: "ITEM_RUSTY_BLADE",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
explorer.recalculateStats(mockItemRegistry);
|
||
|
|
|
||
|
|
// Attack should increase by 3
|
||
|
|
expect(explorer.maxHealth).to.equal(explorer.baseStats.health);
|
||
|
|
// Note: recalculateStats updates maxHealth but doesn't return stats
|
||
|
|
// We verify it was called without error
|
||
|
|
});
|
||
|
|
|
||
|
|
it("CoA 3: should apply body armor stats", () => {
|
||
|
|
const initialDefense = explorer.baseStats.defense;
|
||
|
|
const initialSpeed = explorer.baseStats.speed;
|
||
|
|
|
||
|
|
explorer.loadout.body = {
|
||
|
|
uid: "ITEM_002",
|
||
|
|
defId: "ITEM_SCRAP_PLATE",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
explorer.recalculateStats(mockItemRegistry);
|
||
|
|
|
||
|
|
// Should update maxHealth (defense +3, speed -1)
|
||
|
|
expect(explorer.maxHealth).to.exist;
|
||
|
|
});
|
||
|
|
|
||
|
|
it("CoA 4: should apply multiple equipment stats", () => {
|
||
|
|
explorer.loadout.mainHand = {
|
||
|
|
uid: "ITEM_001",
|
||
|
|
defId: "ITEM_RUSTY_BLADE",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
explorer.loadout.body = {
|
||
|
|
uid: "ITEM_002",
|
||
|
|
defId: "ITEM_SCRAP_PLATE",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
explorer.loadout.accessory = {
|
||
|
|
uid: "ITEM_003",
|
||
|
|
defId: "ITEM_RELIC",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
explorer.recalculateStats(mockItemRegistry);
|
||
|
|
|
||
|
|
// Should update maxHealth with all stat changes
|
||
|
|
expect(explorer.maxHealth).to.exist;
|
||
|
|
});
|
||
|
|
|
||
|
|
it("CoA 5: should handle null itemRegistry gracefully", () => {
|
||
|
|
explorer.loadout.mainHand = {
|
||
|
|
uid: "ITEM_001",
|
||
|
|
defId: "ITEM_RUSTY_BLADE",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Should not throw error
|
||
|
|
expect(() => explorer.recalculateStats(null)).to.not.throw();
|
||
|
|
});
|
||
|
|
|
||
|
|
it("CoA 6: should update health proportionally when health stat changes", () => {
|
||
|
|
const healthRelic = new Item({
|
||
|
|
id: "ITEM_HEALTH_RELIC",
|
||
|
|
name: "Health Relic",
|
||
|
|
type: "RELIC",
|
||
|
|
stats: { health: 20 },
|
||
|
|
});
|
||
|
|
|
||
|
|
mockItemRegistry.get = (defId) => {
|
||
|
|
if (defId === "ITEM_HEALTH_RELIC") return healthRelic;
|
||
|
|
return null;
|
||
|
|
};
|
||
|
|
|
||
|
|
// Set base health to 100
|
||
|
|
explorer.baseStats.health = 100;
|
||
|
|
explorer.currentHealth = 50;
|
||
|
|
explorer.maxHealth = 100;
|
||
|
|
|
||
|
|
explorer.loadout.accessory = {
|
||
|
|
uid: "ITEM_004",
|
||
|
|
defId: "ITEM_HEALTH_RELIC",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
explorer.recalculateStats(mockItemRegistry);
|
||
|
|
|
||
|
|
// Health should be updated proportionally
|
||
|
|
// baseStats.health (100) + item health (20) = 120
|
||
|
|
expect(explorer.maxHealth).to.equal(120); // 100 + 20
|
||
|
|
// Current health should maintain ratio: 50/100 * 120 = 60
|
||
|
|
// Implementation: healthRatio = 50/100 = 0.5, newHealth = floor(120 * 0.5) = 60
|
||
|
|
expect(explorer.currentHealth).to.equal(60);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("CoA 7: should not apply belt item stats (consumables)", () => {
|
||
|
|
const potion = {
|
||
|
|
uid: "ITEM_005",
|
||
|
|
defId: "ITEM_HEALTH_POTION",
|
||
|
|
isNew: false,
|
||
|
|
quantity: 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
explorer.loadout.belt[0] = potion;
|
||
|
|
|
||
|
|
const initialMaxHealth = explorer.maxHealth;
|
||
|
|
explorer.recalculateStats(mockItemRegistry);
|
||
|
|
|
||
|
|
// Belt items shouldn't affect stats
|
||
|
|
expect(explorer.maxHealth).to.equal(initialMaxHealth);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe("backward compatibility", () => {
|
||
|
|
it("CoA 8: should maintain legacy equipment property", () => {
|
||
|
|
expect(explorer.equipment).to.exist;
|
||
|
|
expect(explorer.equipment.weapon).to.be.null;
|
||
|
|
expect(explorer.equipment.armor).to.be.null;
|
||
|
|
expect(explorer.equipment.utility).to.be.null;
|
||
|
|
expect(explorer.equipment.relic).to.be.null;
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|