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.
240 lines
7.4 KiB
JavaScript
240 lines
7.4 KiB
JavaScript
import { expect } from "@esm-bundle/chai";
|
|
import { Explorer } from "../../src/units/Explorer.js";
|
|
|
|
// Mock Class Definitions
|
|
const CLASS_VANGUARD = {
|
|
id: "CLASS_VANGUARD",
|
|
base_stats: { health: 100, attack: 10, speed: 5 },
|
|
growth_rates: { health: 10, attack: 1 },
|
|
};
|
|
|
|
const CLASS_TINKER = {
|
|
id: "CLASS_TINKER",
|
|
base_stats: { health: 80, attack: 8, speed: 7 },
|
|
growth_rates: { health: 5, attack: 2 },
|
|
};
|
|
|
|
describe("Unit: Explorer Class Logic", () => {
|
|
it("CoA 1: Should initialize with base stats from definition", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
|
|
expect(hero.baseStats.health).to.equal(100);
|
|
expect(hero.baseStats.attack).to.equal(10);
|
|
expect(hero.classMastery["CLASS_VANGUARD"]).to.exist;
|
|
expect(hero.classMastery["CLASS_VANGUARD"].level).to.equal(1);
|
|
});
|
|
|
|
it("CoA 2: Should calculate stats based on Level Growth", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
|
|
// Manually level up to 3
|
|
hero.classMastery["CLASS_VANGUARD"].level = 3;
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
|
|
// Level 3 means 2 level-ups.
|
|
// Health: 100 + (10 * 2) = 120
|
|
// Attack: 10 + (1 * 2) = 12
|
|
expect(hero.baseStats.health).to.equal(120);
|
|
expect(hero.baseStats.attack).to.equal(12);
|
|
});
|
|
|
|
it("CoA 3: changeClass should switch stats and persist old progress", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
|
|
// Level up Vanguard
|
|
hero.classMastery["CLASS_VANGUARD"].level = 5;
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
expect(hero.baseStats.health).to.equal(140); // 100 + 40
|
|
|
|
// Switch to Tinker (New Job)
|
|
hero.changeClass("CLASS_TINKER", CLASS_TINKER);
|
|
|
|
// Should have Level 1 Tinker Stats
|
|
expect(hero.activeClassId).to.equal("CLASS_TINKER");
|
|
expect(hero.baseStats.health).to.equal(80); // Base Tinker
|
|
|
|
// Verify Vanguard history is saved
|
|
expect(hero.classMastery["CLASS_VANGUARD"].level).to.equal(5);
|
|
});
|
|
|
|
it("CoA 4: Switching BACK to old class should restore high stats", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
hero.classMastery["CLASS_VANGUARD"].level = 5;
|
|
|
|
// Switch Away
|
|
hero.changeClass("CLASS_TINKER", CLASS_TINKER);
|
|
|
|
// Switch Back
|
|
hero.changeClass("CLASS_VANGUARD", CLASS_VANGUARD);
|
|
|
|
// Should be back to Level 5 Stats
|
|
expect(hero.baseStats.health).to.equal(140);
|
|
});
|
|
|
|
describe("recalculateStats with Skill Tree", () => {
|
|
it("should apply skill tree stat boosts to maxHealth", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
hero.maxHealth = hero.baseStats.health;
|
|
hero.currentHealth = hero.maxHealth;
|
|
|
|
// Initial health should be 100
|
|
expect(hero.maxHealth).to.equal(100);
|
|
|
|
// Create skill tree with health boost
|
|
const treeDef = {
|
|
nodes: {
|
|
ROOT: {
|
|
id: "ROOT",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "health", value: 10 },
|
|
},
|
|
},
|
|
};
|
|
|
|
// Unlock the node
|
|
hero.classMastery["CLASS_VANGUARD"].unlockedNodes = ["ROOT"];
|
|
|
|
// Recalculate stats with tree definition
|
|
hero.recalculateStats(null, treeDef);
|
|
|
|
// Health should be increased by 10
|
|
expect(hero.maxHealth).to.equal(110);
|
|
});
|
|
|
|
it("should apply multiple skill tree stat boosts", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
hero.maxHealth = hero.baseStats.health;
|
|
hero.currentHealth = hero.maxHealth;
|
|
|
|
const treeDef = {
|
|
nodes: {
|
|
ROOT: {
|
|
id: "ROOT",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "health", value: 10 },
|
|
},
|
|
NODE_2: {
|
|
id: "NODE_2",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "health", value: 5 },
|
|
},
|
|
},
|
|
};
|
|
|
|
// Unlock both nodes
|
|
hero.classMastery["CLASS_VANGUARD"].unlockedNodes = ["ROOT", "NODE_2"];
|
|
|
|
hero.recalculateStats(null, treeDef);
|
|
|
|
// Health should be increased by 15 (10 + 5)
|
|
expect(hero.maxHealth).to.equal(115);
|
|
});
|
|
|
|
it("should only apply stat boosts from unlocked nodes", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
hero.maxHealth = hero.baseStats.health;
|
|
hero.currentHealth = hero.maxHealth;
|
|
|
|
const treeDef = {
|
|
nodes: {
|
|
ROOT: {
|
|
id: "ROOT",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "health", value: 10 },
|
|
},
|
|
LOCKED_NODE: {
|
|
id: "LOCKED_NODE",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "health", value: 20 },
|
|
},
|
|
},
|
|
};
|
|
|
|
// Only unlock ROOT, not LOCKED_NODE
|
|
hero.classMastery["CLASS_VANGUARD"].unlockedNodes = ["ROOT"];
|
|
|
|
hero.recalculateStats(null, treeDef);
|
|
|
|
// Should only get boost from ROOT (10), not LOCKED_NODE (20)
|
|
expect(hero.maxHealth).to.equal(110);
|
|
});
|
|
|
|
it("should apply stat boosts to non-health stats", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
|
|
const treeDef = {
|
|
nodes: {
|
|
ATTACK_BOOST: {
|
|
id: "ATTACK_BOOST",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "attack", value: 5 },
|
|
},
|
|
},
|
|
};
|
|
|
|
hero.classMastery["CLASS_VANGUARD"].unlockedNodes = ["ATTACK_BOOST"];
|
|
|
|
// Note: recalculateStats doesn't return stats, but we can verify it was called
|
|
// The actual stat application is tested through CharacterSheet UI tests
|
|
hero.recalculateStats(null, treeDef);
|
|
|
|
// Verify the method completed without error
|
|
expect(hero.maxHealth).to.exist;
|
|
});
|
|
|
|
it("should update currentHealth proportionally when maxHealth changes", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
hero.maxHealth = 100;
|
|
hero.currentHealth = 50; // 50% health
|
|
|
|
const treeDef = {
|
|
nodes: {
|
|
ROOT: {
|
|
id: "ROOT",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "health", value: 20 },
|
|
},
|
|
},
|
|
};
|
|
|
|
hero.classMastery["CLASS_VANGUARD"].unlockedNodes = ["ROOT"];
|
|
|
|
hero.recalculateStats(null, treeDef);
|
|
|
|
// maxHealth should be 120 (100 + 20)
|
|
expect(hero.maxHealth).to.equal(120);
|
|
// currentHealth should be proportionally adjusted (50% of 120 = 60)
|
|
expect(hero.currentHealth).to.equal(60);
|
|
});
|
|
|
|
it("should handle treeDef with no unlocked nodes", () => {
|
|
const hero = new Explorer("p1", "Hero", "CLASS_VANGUARD", CLASS_VANGUARD);
|
|
hero.recalculateBaseStats(CLASS_VANGUARD);
|
|
hero.maxHealth = hero.baseStats.health;
|
|
hero.currentHealth = hero.maxHealth;
|
|
|
|
const treeDef = {
|
|
nodes: {
|
|
ROOT: {
|
|
id: "ROOT",
|
|
type: "STAT_BOOST",
|
|
data: { stat: "health", value: 10 },
|
|
},
|
|
},
|
|
};
|
|
|
|
// No unlocked nodes
|
|
hero.classMastery["CLASS_VANGUARD"].unlockedNodes = [];
|
|
|
|
hero.recalculateStats(null, treeDef);
|
|
|
|
// Health should remain unchanged
|
|
expect(hero.maxHealth).to.equal(100);
|
|
});
|
|
});
|
|
});
|