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.
745 lines
23 KiB
JavaScript
745 lines
23 KiB
JavaScript
import { expect } from "@esm-bundle/chai";
|
|
import { SkillTreeFactory } from "../../src/factories/SkillTreeFactory.js";
|
|
|
|
describe("System: Skill Tree Factory", () => {
|
|
let factory;
|
|
let mockTemplates;
|
|
let mockSkills;
|
|
let mockClassConfig;
|
|
|
|
beforeEach(() => {
|
|
// 1. Setup Mock Data
|
|
mockTemplates = {
|
|
TEMPLATE_STANDARD_30: {
|
|
nodes: {
|
|
ROOT_NODE: {
|
|
tier: 1,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["CHILD_NODE"],
|
|
},
|
|
CHILD_NODE: { tier: 1, type: "SLOT_SKILL_ACTIVE_1", children: [] },
|
|
},
|
|
},
|
|
};
|
|
|
|
mockSkills = {
|
|
SKILL_FIREBALL: { id: "SKILL_FIREBALL", name: "Fireball", damage: 10 },
|
|
};
|
|
|
|
mockClassConfig = {
|
|
id: "TEST_CLASS",
|
|
skillTreeData: {
|
|
primary_stat: "attack",
|
|
secondary_stat: "defense",
|
|
active_skills: ["SKILL_FIREBALL"],
|
|
passive_skills: ["PASSIVE_BURNING"],
|
|
},
|
|
};
|
|
|
|
// 2. Initialize Factory
|
|
factory = new SkillTreeFactory(mockTemplates, mockSkills);
|
|
});
|
|
|
|
it("CoA 1: Should maintain the topology (structure) of the template", () => {
|
|
const tree = factory.createTree(mockClassConfig);
|
|
|
|
expect(tree.nodes).to.have.property("ROOT_NODE");
|
|
expect(tree.nodes).to.have.property("CHILD_NODE");
|
|
expect(tree.nodes["ROOT_NODE"].children).to.include("CHILD_NODE");
|
|
});
|
|
|
|
it("CoA 2: Should inject Primary Stats based on Class Config", () => {
|
|
const tree = factory.createTree(mockClassConfig);
|
|
const rootNode = tree.nodes["ROOT_NODE"];
|
|
|
|
expect(rootNode.type).to.equal("STAT_BOOST");
|
|
expect(rootNode.data.stat).to.equal("attack"); // Injected from config
|
|
expect(rootNode.data.value).to.equal(2); // Tier 1 * 2
|
|
});
|
|
|
|
it("CoA 3: Should inject Active Skills based on Class Config", () => {
|
|
const tree = factory.createTree(mockClassConfig);
|
|
const childNode = tree.nodes["CHILD_NODE"];
|
|
|
|
expect(childNode.type).to.equal("ACTIVE_SKILL");
|
|
// Should resolve the full skill object from registry
|
|
expect(childNode.data.name).to.equal("Fireball");
|
|
});
|
|
|
|
describe("30-Node Template Support", () => {
|
|
let fullTemplate;
|
|
let fullClassConfig;
|
|
let fullSkills;
|
|
|
|
beforeEach(() => {
|
|
// Create a mock 30-node template
|
|
fullTemplate = {
|
|
TEMPLATE_STANDARD_30: {
|
|
nodes: {
|
|
NODE_T1_1: {
|
|
tier: 1,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["NODE_T2_1", "NODE_T2_2"],
|
|
req: 1,
|
|
cost: 1,
|
|
},
|
|
NODE_T2_1: {
|
|
tier: 2,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: ["NODE_T3_1"],
|
|
req: 2,
|
|
cost: 1,
|
|
},
|
|
NODE_T2_2: {
|
|
tier: 2,
|
|
type: "SLOT_SKILL_ACTIVE_1",
|
|
children: ["NODE_T3_2"],
|
|
req: 2,
|
|
cost: 1,
|
|
},
|
|
NODE_T3_1: {
|
|
tier: 3,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: [],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
NODE_T3_2: {
|
|
tier: 3,
|
|
type: "SLOT_SKILL_ACTIVE_2",
|
|
children: [],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
fullClassConfig = {
|
|
id: "CLASS_VANGUARD",
|
|
skillTreeData: {
|
|
primary_stat: "health",
|
|
secondary_stat: "defense",
|
|
active_skills: ["SKILL_SHIELD_BASH", "SKILL_TAUNT"],
|
|
passive_skills: ["PASSIVE_IRON_SKIN", "PASSIVE_THORNS"],
|
|
},
|
|
};
|
|
|
|
fullSkills = {
|
|
SKILL_SHIELD_BASH: { id: "SKILL_SHIELD_BASH", name: "Shield Bash" },
|
|
SKILL_TAUNT: { id: "SKILL_TAUNT", name: "Taunt" },
|
|
};
|
|
});
|
|
|
|
it("should generate tree with all nodes from template", () => {
|
|
const fullFactory = new SkillTreeFactory(fullTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// Should have all 5 nodes from template
|
|
expect(Object.keys(tree.nodes)).to.have.length(5);
|
|
expect(tree.nodes).to.have.property("NODE_T1_1");
|
|
expect(tree.nodes).to.have.property("NODE_T2_1");
|
|
expect(tree.nodes).to.have.property("NODE_T2_2");
|
|
expect(tree.nodes).to.have.property("NODE_T3_1");
|
|
expect(tree.nodes).to.have.property("NODE_T3_2");
|
|
});
|
|
|
|
it("should hydrate all stat boost nodes correctly", () => {
|
|
const fullFactory = new SkillTreeFactory(fullTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// Tier 1 primary stat (health) - should be tier * 2 = 2
|
|
const t1Node = tree.nodes["NODE_T1_1"];
|
|
expect(t1Node.type).to.equal("STAT_BOOST");
|
|
expect(t1Node.data.stat).to.equal("health");
|
|
expect(t1Node.data.value).to.equal(2); // Tier 1 * 2
|
|
|
|
// Tier 2 secondary stat (defense) - should be tier = 2
|
|
const t2Node = tree.nodes["NODE_T2_1"];
|
|
expect(t2Node.type).to.equal("STAT_BOOST");
|
|
expect(t2Node.data.stat).to.equal("defense");
|
|
expect(t2Node.data.value).to.equal(2); // Tier 2
|
|
|
|
// Tier 3 primary stat (health) - should be tier * 2 = 6
|
|
const t3Node = tree.nodes["NODE_T3_1"];
|
|
expect(t3Node.type).to.equal("STAT_BOOST");
|
|
expect(t3Node.data.stat).to.equal("health");
|
|
expect(t3Node.data.value).to.equal(6); // Tier 3 * 2
|
|
});
|
|
|
|
it("should hydrate all active skill nodes correctly", () => {
|
|
const fullFactory = new SkillTreeFactory(fullTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// ACTIVE_1 should map to first skill
|
|
const active1Node = tree.nodes["NODE_T2_2"];
|
|
expect(active1Node.type).to.equal("ACTIVE_SKILL");
|
|
expect(active1Node.data.name).to.equal("Shield Bash");
|
|
|
|
// ACTIVE_2 should map to second skill
|
|
const active2Node = tree.nodes["NODE_T3_2"];
|
|
expect(active2Node.type).to.equal("ACTIVE_SKILL");
|
|
expect(active2Node.data.name).to.equal("Taunt");
|
|
});
|
|
|
|
it("should handle missing skills gracefully", () => {
|
|
const classConfigMissingSkills = {
|
|
id: "TEST_CLASS",
|
|
skillTreeData: {
|
|
primary_stat: "attack",
|
|
secondary_stat: "defense",
|
|
active_skills: ["SKILL_EXISTS"], // Only one skill
|
|
passive_skills: [],
|
|
},
|
|
};
|
|
|
|
const skills = {
|
|
SKILL_EXISTS: { id: "SKILL_EXISTS", name: "Existing Skill" },
|
|
};
|
|
|
|
const fullFactory = new SkillTreeFactory(fullTemplate, skills);
|
|
const tree = fullFactory.createTree(classConfigMissingSkills);
|
|
|
|
// ACTIVE_1 should work
|
|
const active1Node = tree.nodes["NODE_T2_2"];
|
|
expect(active1Node.data.name).to.equal("Existing Skill");
|
|
|
|
// ACTIVE_2 should fallback to "Unknown Skill"
|
|
const active2Node = tree.nodes["NODE_T3_2"];
|
|
expect(active2Node.data.name).to.equal("Unknown Skill");
|
|
});
|
|
});
|
|
|
|
describe("Extended Skill Slots", () => {
|
|
let extendedTemplate;
|
|
let extendedClassConfig;
|
|
let extendedSkills;
|
|
|
|
beforeEach(() => {
|
|
extendedTemplate = {
|
|
TEMPLATE_STANDARD_30: {
|
|
nodes: {
|
|
ACTIVE_1: {
|
|
tier: 2,
|
|
type: "SLOT_SKILL_ACTIVE_1",
|
|
children: [],
|
|
req: 2,
|
|
cost: 1,
|
|
},
|
|
ACTIVE_2: {
|
|
tier: 2,
|
|
type: "SLOT_SKILL_ACTIVE_2",
|
|
children: [],
|
|
req: 2,
|
|
cost: 1,
|
|
},
|
|
ACTIVE_3: {
|
|
tier: 3,
|
|
type: "SLOT_SKILL_ACTIVE_3",
|
|
children: [],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
ACTIVE_4: {
|
|
tier: 3,
|
|
type: "SLOT_SKILL_ACTIVE_4",
|
|
children: [],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
PASSIVE_1: {
|
|
tier: 2,
|
|
type: "SLOT_SKILL_PASSIVE_1",
|
|
children: [],
|
|
req: 2,
|
|
cost: 2,
|
|
},
|
|
PASSIVE_2: {
|
|
tier: 3,
|
|
type: "SLOT_SKILL_PASSIVE_2",
|
|
children: [],
|
|
req: 3,
|
|
cost: 2,
|
|
},
|
|
PASSIVE_3: {
|
|
tier: 4,
|
|
type: "SLOT_SKILL_PASSIVE_3",
|
|
children: [],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
PASSIVE_4: {
|
|
tier: 4,
|
|
type: "SLOT_SKILL_PASSIVE_4",
|
|
children: [],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
extendedClassConfig = {
|
|
id: "TEST_CLASS",
|
|
skillTreeData: {
|
|
primary_stat: "attack",
|
|
secondary_stat: "defense",
|
|
active_skills: [
|
|
"SKILL_1",
|
|
"SKILL_2",
|
|
"SKILL_3",
|
|
"SKILL_4",
|
|
],
|
|
passive_skills: [
|
|
"PASSIVE_1",
|
|
"PASSIVE_2",
|
|
"PASSIVE_3",
|
|
"PASSIVE_4",
|
|
],
|
|
},
|
|
};
|
|
|
|
extendedSkills = {
|
|
SKILL_1: { id: "SKILL_1", name: "Skill 1" },
|
|
SKILL_2: { id: "SKILL_2", name: "Skill 2" },
|
|
SKILL_3: { id: "SKILL_3", name: "Skill 3" },
|
|
SKILL_4: { id: "SKILL_4", name: "Skill 4" },
|
|
};
|
|
});
|
|
|
|
it("should hydrate ACTIVE_3 and ACTIVE_4 slots", () => {
|
|
const extendedFactory = new SkillTreeFactory(
|
|
extendedTemplate,
|
|
extendedSkills
|
|
);
|
|
const tree = extendedFactory.createTree(extendedClassConfig);
|
|
|
|
const active3Node = tree.nodes["ACTIVE_3"];
|
|
expect(active3Node.type).to.equal("ACTIVE_SKILL");
|
|
expect(active3Node.data.name).to.equal("Skill 3");
|
|
|
|
const active4Node = tree.nodes["ACTIVE_4"];
|
|
expect(active4Node.type).to.equal("ACTIVE_SKILL");
|
|
expect(active4Node.data.name).to.equal("Skill 4");
|
|
});
|
|
|
|
it("should hydrate PASSIVE_2, PASSIVE_3, and PASSIVE_4 slots", () => {
|
|
const extendedFactory = new SkillTreeFactory(
|
|
extendedTemplate,
|
|
extendedSkills
|
|
);
|
|
const tree = extendedFactory.createTree(extendedClassConfig);
|
|
|
|
const passive2Node = tree.nodes["PASSIVE_2"];
|
|
expect(passive2Node.type).to.equal("PASSIVE_ABILITY");
|
|
expect(passive2Node.data.name).to.equal("PASSIVE_2");
|
|
expect(passive2Node.data.effect_id).to.equal("PASSIVE_2");
|
|
|
|
const passive3Node = tree.nodes["PASSIVE_3"];
|
|
expect(passive3Node.type).to.equal("PASSIVE_ABILITY");
|
|
expect(passive3Node.data.name).to.equal("PASSIVE_3");
|
|
|
|
const passive4Node = tree.nodes["PASSIVE_4"];
|
|
expect(passive4Node.type).to.equal("PASSIVE_ABILITY");
|
|
expect(passive4Node.data.name).to.equal("PASSIVE_4");
|
|
});
|
|
|
|
it("should handle missing extended skills with fallbacks", () => {
|
|
const limitedClassConfig = {
|
|
id: "TEST_CLASS",
|
|
skillTreeData: {
|
|
primary_stat: "attack",
|
|
secondary_stat: "defense",
|
|
active_skills: ["SKILL_1"], // Only one skill
|
|
passive_skills: ["PASSIVE_1"], // Only one passive
|
|
},
|
|
};
|
|
|
|
const limitedFactory = new SkillTreeFactory(
|
|
extendedTemplate,
|
|
extendedSkills
|
|
);
|
|
const tree = limitedFactory.createTree(limitedClassConfig);
|
|
|
|
// ACTIVE_3 should fallback
|
|
const active3Node = tree.nodes["ACTIVE_3"];
|
|
expect(active3Node.data.name).to.equal("Unknown Skill");
|
|
|
|
// PASSIVE_3 should fallback
|
|
const passive3Node = tree.nodes["PASSIVE_3"];
|
|
expect(passive3Node.data.name).to.equal("Unknown Passive");
|
|
});
|
|
});
|
|
|
|
describe("Full 30-Node Template Generation", () => {
|
|
let full30NodeTemplate;
|
|
let fullClassConfig;
|
|
let fullSkills;
|
|
|
|
beforeEach(() => {
|
|
// Create a simplified but representative 30-node template structure
|
|
// This mirrors the actual template_standard_30.json structure
|
|
full30NodeTemplate = {
|
|
TEMPLATE_STANDARD_30: {
|
|
nodes: {
|
|
// Tier 1: 1 node
|
|
NODE_T1_1: {
|
|
tier: 1,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["NODE_T2_1", "NODE_T2_2", "NODE_T2_3"],
|
|
req: 1,
|
|
cost: 1,
|
|
},
|
|
// Tier 2: 3 nodes
|
|
NODE_T2_1: {
|
|
tier: 2,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: ["NODE_T3_1", "NODE_T3_2"],
|
|
req: 2,
|
|
cost: 1,
|
|
},
|
|
NODE_T2_2: {
|
|
tier: 2,
|
|
type: "SLOT_SKILL_ACTIVE_1",
|
|
children: ["NODE_T3_3", "NODE_T3_4"],
|
|
req: 2,
|
|
cost: 1,
|
|
},
|
|
NODE_T2_3: {
|
|
tier: 2,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["NODE_T3_5", "NODE_T3_6"],
|
|
req: 2,
|
|
cost: 1,
|
|
},
|
|
// Tier 3: 6 nodes
|
|
NODE_T3_1: {
|
|
tier: 3,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["NODE_T4_1", "NODE_T4_2"],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
NODE_T3_2: {
|
|
tier: 3,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: ["NODE_T4_3"],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
NODE_T3_3: {
|
|
tier: 3,
|
|
type: "SLOT_SKILL_ACTIVE_2",
|
|
children: ["NODE_T4_4", "NODE_T4_5"],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
NODE_T3_4: {
|
|
tier: 3,
|
|
type: "SLOT_SKILL_PASSIVE_1",
|
|
children: ["NODE_T4_6"],
|
|
req: 3,
|
|
cost: 2,
|
|
},
|
|
NODE_T3_5: {
|
|
tier: 3,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: ["NODE_T4_7"],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
NODE_T3_6: {
|
|
tier: 3,
|
|
type: "SLOT_SKILL_ACTIVE_1",
|
|
children: ["NODE_T4_8", "NODE_T4_9"],
|
|
req: 3,
|
|
cost: 1,
|
|
},
|
|
// Tier 4: 9 nodes
|
|
NODE_T4_1: {
|
|
tier: 4,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["NODE_T5_1", "NODE_T5_2"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_2: {
|
|
tier: 4,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: ["NODE_T5_3"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_3: {
|
|
tier: 4,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["NODE_T5_4"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_4: {
|
|
tier: 4,
|
|
type: "SLOT_SKILL_ACTIVE_3",
|
|
children: ["NODE_T5_5", "NODE_T5_6"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_5: {
|
|
tier: 4,
|
|
type: "SLOT_SKILL_ACTIVE_4",
|
|
children: ["NODE_T5_7"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_6: {
|
|
tier: 4,
|
|
type: "SLOT_SKILL_PASSIVE_2",
|
|
children: ["NODE_T5_8"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_7: {
|
|
tier: 4,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: ["NODE_T5_9"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_8: {
|
|
tier: 4,
|
|
type: "SLOT_SKILL_PASSIVE_3",
|
|
children: ["NODE_T5_10"],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
NODE_T4_9: {
|
|
tier: 4,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: [],
|
|
req: 4,
|
|
cost: 2,
|
|
},
|
|
// Tier 5: 11 nodes (to make 30 total)
|
|
NODE_T5_1: {
|
|
tier: 5,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_2: {
|
|
tier: 5,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_3: {
|
|
tier: 5,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_4: {
|
|
tier: 5,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_5: {
|
|
tier: 5,
|
|
type: "SLOT_SKILL_ACTIVE_3",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_6: {
|
|
tier: 5,
|
|
type: "SLOT_SKILL_ACTIVE_4",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_7: {
|
|
tier: 5,
|
|
type: "SLOT_SKILL_PASSIVE_2",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_8: {
|
|
tier: 5,
|
|
type: "SLOT_SKILL_PASSIVE_4",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_9: {
|
|
tier: 5,
|
|
type: "SLOT_STAT_SECONDARY",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_10: {
|
|
tier: 5,
|
|
type: "SLOT_SKILL_ACTIVE_1",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
NODE_T5_11: {
|
|
tier: 5,
|
|
type: "SLOT_STAT_PRIMARY",
|
|
children: [],
|
|
req: 5,
|
|
cost: 3,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
fullClassConfig = {
|
|
id: "CLASS_VANGUARD",
|
|
skillTreeData: {
|
|
primary_stat: "health",
|
|
secondary_stat: "defense",
|
|
active_skills: [
|
|
"SKILL_SHIELD_BASH",
|
|
"SKILL_TAUNT",
|
|
"SKILL_CHARGE",
|
|
"SKILL_SHIELD_WALL",
|
|
],
|
|
passive_skills: [
|
|
"PASSIVE_IRON_SKIN",
|
|
"PASSIVE_THORNS",
|
|
"PASSIVE_REGEN",
|
|
"PASSIVE_FORTIFY",
|
|
],
|
|
},
|
|
};
|
|
|
|
fullSkills = {
|
|
SKILL_SHIELD_BASH: { id: "SKILL_SHIELD_BASH", name: "Shield Bash" },
|
|
SKILL_TAUNT: { id: "SKILL_TAUNT", name: "Taunt" },
|
|
SKILL_CHARGE: { id: "SKILL_CHARGE", name: "Charge" },
|
|
SKILL_SHIELD_WALL: { id: "SKILL_SHIELD_WALL", name: "Shield Wall" },
|
|
};
|
|
});
|
|
|
|
it("should generate exactly 30 nodes from template", () => {
|
|
const fullFactory = new SkillTreeFactory(full30NodeTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
expect(Object.keys(tree.nodes)).to.have.length(30);
|
|
});
|
|
|
|
it("should maintain all node relationships (children)", () => {
|
|
const fullFactory = new SkillTreeFactory(full30NodeTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// Verify root node has 3 children
|
|
expect(tree.nodes.NODE_T1_1.children).to.have.length(3);
|
|
expect(tree.nodes.NODE_T1_1.children).to.include("NODE_T2_1");
|
|
expect(tree.nodes.NODE_T1_1.children).to.include("NODE_T2_2");
|
|
expect(tree.nodes.NODE_T1_1.children).to.include("NODE_T2_3");
|
|
|
|
// Verify tier 2 nodes have children
|
|
expect(tree.nodes.NODE_T2_1.children).to.have.length(2);
|
|
expect(tree.nodes.NODE_T2_2.children).to.have.length(2);
|
|
|
|
// Verify tier 5 nodes have no children (leaf nodes)
|
|
expect(tree.nodes.NODE_T5_1.children).to.have.length(0);
|
|
expect(tree.nodes.NODE_T5_11.children).to.have.length(0);
|
|
});
|
|
|
|
it("should hydrate all stat boost nodes with correct values", () => {
|
|
const fullFactory = new SkillTreeFactory(full30NodeTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// Tier 1 primary stat: tier * 2 = 2
|
|
expect(tree.nodes.NODE_T1_1.type).to.equal("STAT_BOOST");
|
|
expect(tree.nodes.NODE_T1_1.data.stat).to.equal("health");
|
|
expect(tree.nodes.NODE_T1_1.data.value).to.equal(2);
|
|
|
|
// Tier 2 secondary stat: tier = 2
|
|
expect(tree.nodes.NODE_T2_1.type).to.equal("STAT_BOOST");
|
|
expect(tree.nodes.NODE_T2_1.data.stat).to.equal("defense");
|
|
expect(tree.nodes.NODE_T2_1.data.value).to.equal(2);
|
|
|
|
// Tier 5 primary stat: tier * 2 = 10
|
|
expect(tree.nodes.NODE_T5_1.type).to.equal("STAT_BOOST");
|
|
expect(tree.nodes.NODE_T5_1.data.stat).to.equal("health");
|
|
expect(tree.nodes.NODE_T5_1.data.value).to.equal(10);
|
|
});
|
|
|
|
it("should hydrate all active skill nodes correctly", () => {
|
|
const fullFactory = new SkillTreeFactory(full30NodeTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// ACTIVE_1 should map to first skill
|
|
expect(tree.nodes.NODE_T2_2.type).to.equal("ACTIVE_SKILL");
|
|
expect(tree.nodes.NODE_T2_2.data.name).to.equal("Shield Bash");
|
|
|
|
// ACTIVE_2 should map to second skill
|
|
expect(tree.nodes.NODE_T3_3.type).to.equal("ACTIVE_SKILL");
|
|
expect(tree.nodes.NODE_T3_3.data.name).to.equal("Taunt");
|
|
|
|
// ACTIVE_3 should map to third skill
|
|
expect(tree.nodes.NODE_T4_4.type).to.equal("ACTIVE_SKILL");
|
|
expect(tree.nodes.NODE_T4_4.data.name).to.equal("Charge");
|
|
|
|
// ACTIVE_4 should map to fourth skill
|
|
expect(tree.nodes.NODE_T4_5.type).to.equal("ACTIVE_SKILL");
|
|
expect(tree.nodes.NODE_T4_5.data.name).to.equal("Shield Wall");
|
|
});
|
|
|
|
it("should hydrate all passive skill nodes correctly", () => {
|
|
const fullFactory = new SkillTreeFactory(full30NodeTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// PASSIVE_1
|
|
expect(tree.nodes.NODE_T3_4.type).to.equal("PASSIVE_ABILITY");
|
|
expect(tree.nodes.NODE_T3_4.data.effect_id).to.equal("PASSIVE_IRON_SKIN");
|
|
expect(tree.nodes.NODE_T3_4.data.name).to.equal("PASSIVE_IRON_SKIN");
|
|
|
|
// PASSIVE_2
|
|
expect(tree.nodes.NODE_T4_6.type).to.equal("PASSIVE_ABILITY");
|
|
expect(tree.nodes.NODE_T4_6.data.effect_id).to.equal("PASSIVE_THORNS");
|
|
|
|
// PASSIVE_3
|
|
expect(tree.nodes.NODE_T4_8.type).to.equal("PASSIVE_ABILITY");
|
|
expect(tree.nodes.NODE_T4_8.data.effect_id).to.equal("PASSIVE_REGEN");
|
|
|
|
// PASSIVE_4 (if it exists in tier 5)
|
|
if (tree.nodes.NODE_T5_8) {
|
|
expect(tree.nodes.NODE_T5_8.type).to.equal("PASSIVE_ABILITY");
|
|
expect(tree.nodes.NODE_T5_8.data.effect_id).to.equal("PASSIVE_FORTIFY");
|
|
}
|
|
});
|
|
|
|
it("should preserve tier, req, and cost properties", () => {
|
|
const fullFactory = new SkillTreeFactory(full30NodeTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
// Check tier 1 node
|
|
expect(tree.nodes.NODE_T1_1.tier).to.equal(1);
|
|
expect(tree.nodes.NODE_T1_1.req).to.equal(1);
|
|
expect(tree.nodes.NODE_T1_1.cost).to.equal(1);
|
|
|
|
// Check tier 5 node
|
|
expect(tree.nodes.NODE_T5_1.tier).to.equal(5);
|
|
expect(tree.nodes.NODE_T5_1.req).to.equal(5);
|
|
expect(tree.nodes.NODE_T5_1.cost).to.equal(3);
|
|
});
|
|
|
|
it("should generate tree with correct ID format", () => {
|
|
const fullFactory = new SkillTreeFactory(full30NodeTemplate, fullSkills);
|
|
const tree = fullFactory.createTree(fullClassConfig);
|
|
|
|
expect(tree.id).to.equal("TREE_CLASS_VANGUARD");
|
|
});
|
|
});
|
|
});
|