aether-shards/test/ui/character-sheet.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

905 lines
28 KiB
JavaScript

import { expect } from "@esm-bundle/chai";
import { CharacterSheet } from "../../src/ui/components/CharacterSheet.js";
import { Explorer } from "../../src/units/Explorer.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", () => {
let element;
let container;
let testUnit;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
element = document.createElement("character-sheet");
container.appendChild(element);
// Create a 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;
});
afterEach(() => {
if (container.parentNode) {
container.parentNode.removeChild(container);
}
});
// Helper to wait for LitElement update
async function waitForUpdate() {
await element.updateComplete;
// Give a small delay for DOM updates
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: Stat Rendering", () => {
it("should render stats with effective values", async () => {
element.unit = testUnit;
await waitForUpdate();
// Check that stat values are displayed
const statValues = queryShadowAll('.stat-value');
expect(statValues.length).to.be.greaterThan(0);
});
it("should show stat breakdown in tooltip on hover", async () => {
element.unit = testUnit;
await waitForUpdate();
const statItem = queryShadow(".stat-item");
expect(statItem).to.exist;
const tooltip = statItem.querySelector(".tooltip");
expect(tooltip).to.exist;
});
it("should display debuffed stats in red", async () => {
testUnit.statusEffects = [
{
id: "WEAKNESS",
name: "Weakness",
statModifiers: { attack: -5 },
},
];
element.unit = testUnit;
await waitForUpdate();
const attackStat = Array.from(queryShadowAll(".stat-item")).find((item) =>
item.textContent.includes("Attack")
);
expect(attackStat).to.exist;
expect(attackStat.classList.contains("debuffed")).to.be.true;
const statValue = attackStat.querySelector(".stat-value");
expect(statValue.classList.contains("debuffed")).to.be.true;
});
it("should display buffed stats in green", async () => {
testUnit.statusEffects = [
{
id: "STRENGTH",
name: "Strength",
statModifiers: { attack: +5 },
},
];
element.unit = testUnit;
await waitForUpdate();
const attackStat = Array.from(queryShadowAll(".stat-item")).find((item) =>
item.textContent.includes("Attack")
);
expect(attackStat).to.exist;
expect(attackStat.classList.contains("buffed")).to.be.true;
const statValue = attackStat.querySelector(".stat-value");
expect(statValue.classList.contains("buffed")).to.be.true;
});
it("should calculate effective stats from base + equipment + buffs", async () => {
const weapon = new Item({
id: "ITEM_TEST_SWORD",
name: "Test Sword",
type: "WEAPON",
stats: { attack: 10 },
});
// Reset to level 1 to get base stats
testUnit.classMastery["CLASS_VANGUARD"].level = 1;
testUnit.recalculateBaseStats(vanguardDef);
testUnit.equipment.weapon = weapon;
testUnit.statusEffects = [
{
id: "BUFF",
name: "Power Boost",
statModifiers: { attack: 3 },
},
];
element.unit = testUnit;
await waitForUpdate();
// Base attack from vanguard level 1 is 12, +10 from weapon, +3 from buff = 25
const attackStat = Array.from(queryShadowAll(".stat-item")).find((item) =>
item.textContent.includes("Attack")
);
const statValue = attackStat.querySelector(".stat-value");
const totalValue = parseInt(statValue.textContent.trim());
expect(totalValue).to.equal(25);
});
it("should display health bar with current/max values", async () => {
testUnit.currentHealth = 80;
testUnit.maxHealth = 120;
element.unit = testUnit;
await waitForUpdate();
const healthBar = queryShadow(".health-bar-container");
expect(healthBar).to.exist;
const healthLabel = queryShadow(".health-label");
expect(healthLabel.textContent).to.include("80");
expect(healthLabel.textContent).to.include("120");
});
it("should display AP icons based on speed", async () => {
// Speed = 8, so AP = 3 + floor(8/5) = 3 + 1 = 4
testUnit.baseStats.speed = 8;
testUnit.currentAP = 3;
element.unit = testUnit;
await waitForUpdate();
// Find the AP stat item (which shows AP icons)
const apStat = Array.from(queryShadowAll(".stat-item")).find((item) =>
item.textContent.includes("AP")
);
expect(apStat).to.exist;
const apIcons = apStat.querySelectorAll(".ap-icon");
expect(apIcons.length).to.equal(4); // Max AP
const emptyIcons = apStat.querySelectorAll(".ap-icon.empty");
expect(emptyIcons.length).to.equal(1); // One empty (4 total - 3 used)
});
});
describe("CoA 2: Equipment Swapping", () => {
it("should show equipment slots in paper doll", async () => {
element.unit = testUnit;
await waitForUpdate();
// Use the new class names (mainHand, offHand, body, accessory)
const weaponSlot = queryShadow(".equipment-slot.mainHand");
const armorSlot = queryShadow(".equipment-slot.body");
const relicSlot = queryShadow(".equipment-slot.accessory");
const utilitySlot = queryShadow(".equipment-slot.offHand");
expect(weaponSlot).to.exist;
expect(armorSlot).to.exist;
expect(relicSlot).to.exist;
expect(utilitySlot).to.exist;
});
it("should show ghost icon for empty slots", async () => {
element.unit = testUnit;
await waitForUpdate();
const weaponSlot = queryShadow(".equipment-slot.mainHand");
const slotIcon = weaponSlot.querySelector(".slot-icon");
expect(slotIcon).to.exist;
});
it("should show item icon for equipped items", async () => {
// Use the new loadout system
testUnit.loadout.mainHand = {
uid: "ITEM_TEST_SWORD_1",
defId: "ITEM_TEST_SWORD",
isNew: false,
quantity: 1,
};
// Mock inventoryManager with item registry
const mockItemRegistry = new Map();
mockItemRegistry.set("ITEM_TEST_SWORD", {
id: "ITEM_TEST_SWORD",
name: "Test Sword",
type: "WEAPON",
icon: "⚔️",
});
element.unit = testUnit;
element.inventoryManager = {
itemRegistry: mockItemRegistry,
};
await waitForUpdate();
const weaponSlot = queryShadow(".equipment-slot.mainHand");
const itemIcon = weaponSlot.querySelector(".item-icon");
expect(itemIcon).to.exist;
});
it("should switch to inventory tab when slot is clicked", async () => {
element.unit = testUnit;
element.inventory = [];
await waitForUpdate();
const weaponSlot = queryShadow(".equipment-slot.mainHand");
weaponSlot.click();
await waitForUpdate();
expect(element.activeTab).to.equal("INVENTORY");
expect(element.selectedSlot).to.equal("MAIN_HAND");
});
it("should filter inventory by slot type when slot is selected", async () => {
const weapon1 = new Item({
id: "ITEM_SWORD",
name: "Sword",
type: "WEAPON",
});
const weapon2 = new Item({
id: "ITEM_AXE",
name: "Axe",
type: "WEAPON",
});
const armor = new Item({
id: "ITEM_PLATE",
name: "Plate",
type: "ARMOR",
});
element.unit = testUnit;
element.inventory = [weapon1, weapon2, armor];
element.selectedSlot = "WEAPON";
await waitForUpdate();
const itemCards = queryShadowAll(".item-card");
expect(itemCards.length).to.equal(2); // Only weapons
});
it("should equip item when clicked in inventory", async () => {
const weapon = new Item({
id: "ITEM_SWORD",
name: "Sword",
type: "WEAPON",
stats: { attack: 10 },
});
const oldWeapon = new Item({
id: "ITEM_OLD_SWORD",
name: "Old Sword",
type: "WEAPON",
});
testUnit.equipment.weapon = oldWeapon;
element.unit = testUnit;
element.inventory = [weapon];
element.selectedSlot = "WEAPON";
let equipEventFired = false;
let equipEventDetail = null;
element.addEventListener("equip-item", (e) => {
equipEventFired = true;
equipEventDetail = e.detail;
});
await waitForUpdate();
const itemCard = queryShadow(".item-card");
itemCard.click();
await waitForUpdate();
expect(equipEventFired).to.be.true;
expect(equipEventDetail.unitId).to.equal(testUnit.id);
expect(equipEventDetail.slot).to.equal("WEAPON");
expect(equipEventDetail.item.id).to.equal("ITEM_SWORD");
expect(equipEventDetail.oldItem.id).to.equal("ITEM_OLD_SWORD");
// Old item should be in inventory
expect(element.inventory.some((item) => item.id === "ITEM_OLD_SWORD")).to.be
.true;
// New item should be equipped
expect(testUnit.equipment.weapon.id).to.equal("ITEM_SWORD");
});
it("should update stats immediately after equipping", async () => {
const weapon = new Item({
id: "ITEM_SWORD",
name: "Sword",
type: "WEAPON",
stats: { attack: 15 },
});
element.unit = testUnit;
element.inventory = [weapon];
element.selectedSlot = "WEAPON";
await waitForUpdate();
const initialAttack = Array.from(queryShadowAll(".stat-item")).find((item) =>
item.textContent.includes("Attack")
);
const initialValue = parseInt(
initialAttack.querySelector(".stat-value").textContent.trim()
);
const itemCard = queryShadow(".item-card");
itemCard.click();
await waitForUpdate();
const updatedAttack = Array.from(queryShadowAll(".stat-item")).find((item) =>
item.textContent.includes("Attack")
);
const updatedValue = parseInt(
updatedAttack.querySelector(".stat-value").textContent.trim()
);
expect(updatedValue).to.equal(initialValue + 15);
});
it("should not allow equipping in read-only mode", async () => {
element.unit = testUnit;
element.readOnly = true;
element.inventory = [
new Item({
id: "ITEM_SWORD",
name: "Sword",
type: "WEAPON",
}),
];
element.selectedSlot = "MAIN_HAND";
await waitForUpdate();
const weaponSlot = queryShadow(".equipment-slot.mainHand");
expect(weaponSlot.hasAttribute("disabled")).to.be.true;
const itemCard = queryShadow(".item-card");
const initialWeapon = testUnit.equipment.weapon;
itemCard.click();
await waitForUpdate();
// Equipment should not have changed
expect(testUnit.equipment.weapon).to.equal(initialWeapon);
});
});
describe("CoA 3: Skill Interaction", () => {
it("should display skill tree tab", async () => {
element.unit = testUnit;
element.activeTab = "SKILLS";
await waitForUpdate();
const skillsTab = Array.from(queryShadowAll(".tab-button")).find((btn) =>
btn.textContent.includes("Skills")
);
expect(skillsTab).to.exist;
const skillsContainer = queryShadow(".skills-container");
expect(skillsContainer).to.exist;
});
it("should embed skill-tree-ui component", async () => {
element.unit = testUnit;
element.activeTab = "SKILLS";
await waitForUpdate();
const skillTree = queryShadow("skill-tree-ui");
expect(skillTree).to.exist;
expect(skillTree.unit).to.equal(testUnit);
});
it("should display SP badge when skill points are available", async () => {
testUnit.classMastery["CLASS_VANGUARD"].skillPoints = 3;
element.unit = testUnit;
await waitForUpdate();
const spBadge = queryShadow(".sp-badge");
expect(spBadge).to.exist;
expect(spBadge.textContent).to.include("SP: 3");
});
it("should not display SP badge when no skill points", async () => {
testUnit.classMastery["CLASS_VANGUARD"].skillPoints = 0;
element.unit = testUnit;
await waitForUpdate();
const spBadge = queryShadow(".sp-badge");
expect(spBadge).to.be.null;
});
it("should handle unlock-request and update unit stats", async () => {
// Set up unit with skill points and mock recalculateStats
testUnit.classMastery["CLASS_VANGUARD"].skillPoints = 2;
testUnit.classMastery["CLASS_VANGUARD"].unlockedNodes = [];
testUnit.maxHealth = 100;
testUnit.currentHealth = 100;
let recalculateStatsCalled = false;
let recalculateStatsArgs = null;
testUnit.recalculateStats = (itemRegistry, treeDef) => {
recalculateStatsCalled = true;
recalculateStatsArgs = { itemRegistry, treeDef };
// Simulate stat boost application
testUnit.maxHealth = 110; // Base 100 + 10 from health boost
};
element.unit = testUnit;
element.activeTab = "SKILLS";
await waitForUpdate();
// Create mock tree definition
const mockTreeDef = {
id: "TREE_TEST",
nodes: {
ROOT: {
id: "ROOT",
tier: 1,
type: "STAT_BOOST",
data: { stat: "health", value: 10 },
req: 1,
cost: 1,
},
},
};
element.treeDef = mockTreeDef;
await waitForUpdate();
// Call the handler directly (since it's a private method, we'll simulate the event)
const unlockEvent = new CustomEvent("unlock-request", {
detail: { nodeId: "ROOT", cost: 1 },
bubbles: true,
composed: true,
});
// Simulate the event being handled by calling the method directly
element._handleUnlockRequest(unlockEvent);
await waitForUpdate();
// Verify node was unlocked
expect(testUnit.classMastery["CLASS_VANGUARD"].unlockedNodes).to.include("ROOT");
expect(testUnit.classMastery["CLASS_VANGUARD"].skillPoints).to.equal(1);
// Verify recalculateStats was called with correct args
expect(recalculateStatsCalled).to.be.true;
expect(recalculateStatsArgs.treeDef).to.exist;
});
it("should dispatch skill-unlocked event after unlocking", async () => {
testUnit.classMastery["CLASS_VANGUARD"].skillPoints = 2;
testUnit.classMastery["CLASS_VANGUARD"].unlockedNodes = [];
testUnit.recalculateStats = () => {}; // Mock function
element.unit = testUnit;
await waitForUpdate();
let skillUnlockedEventFired = false;
let skillUnlockedEventDetail = null;
element.addEventListener("skill-unlocked", (e) => {
skillUnlockedEventFired = true;
skillUnlockedEventDetail = e.detail;
});
const unlockEvent = new CustomEvent("unlock-request", {
detail: { nodeId: "ROOT", cost: 1 },
bubbles: true,
composed: true,
});
// Call the handler directly
element._handleUnlockRequest(unlockEvent);
await waitForUpdate();
expect(skillUnlockedEventFired).to.be.true;
expect(skillUnlockedEventDetail.unitId).to.equal(testUnit.id);
expect(skillUnlockedEventDetail.nodeId).to.equal("ROOT");
expect(skillUnlockedEventDetail.cost).to.equal(1);
});
it("should update SkillTreeUI after unlocking", async () => {
testUnit.classMastery["CLASS_VANGUARD"].skillPoints = 2;
testUnit.classMastery["CLASS_VANGUARD"].unlockedNodes = [];
testUnit.recalculateStats = () => {}; // Mock function
element.unit = testUnit;
element.activeTab = "SKILLS";
await waitForUpdate();
const skillTree = queryShadow("skill-tree-ui");
expect(skillTree).to.exist;
const initialUpdateTrigger = skillTree.updateTrigger || 0;
const unlockEvent = new CustomEvent("unlock-request", {
detail: { nodeId: "ROOT", cost: 1 },
bubbles: true,
composed: true,
});
// Call the handler directly
element._handleUnlockRequest(unlockEvent);
// Wait for setTimeout to execute
await new Promise((resolve) => setTimeout(resolve, 20));
await waitForUpdate();
// Verify updateTrigger was incremented
expect(skillTree.updateTrigger).to.be.greaterThan(initialUpdateTrigger);
});
it("should include skill tree stat boosts in stat breakdown", async () => {
testUnit.classMastery["CLASS_VANGUARD"].unlockedNodes = ["ROOT"];
testUnit.baseStats.health = 100;
testUnit.maxHealth = 100;
// Create mock tree definition with health boost
const mockTreeDef = {
id: "TREE_TEST",
nodes: {
ROOT: {
id: "ROOT",
tier: 1,
type: "STAT_BOOST",
data: { stat: "health", value: 10 },
req: 1,
cost: 1,
},
},
};
element.unit = testUnit;
element.treeDef = mockTreeDef;
await waitForUpdate();
// Get health stat breakdown - health uses a health bar, not stat-value
const healthStat = Array.from(queryShadowAll(".stat-item")).find((item) =>
item.textContent.includes("Health")
);
expect(healthStat).to.exist;
// Health shows as "current / max" in the health label
const healthLabel = healthStat.querySelector(".health-label");
if (healthLabel) {
// Health bar shows current/max, so we check the max value
const healthText = healthLabel.textContent;
const match = healthText.match(/\/(\d+)/);
if (match) {
const maxHealth = parseInt(match[1]);
// Should be 100 base + 10 boost = 110
expect(maxHealth).to.equal(110);
}
} else {
// Fallback: check if the breakdown tooltip would show the boost
// This test verifies the calculation happens, even if we can't easily test the UI
const { total } = element._getEffectiveStat("health");
expect(total).to.equal(110);
}
});
describe("30-Node Skill Tree Integration", () => {
it("should receive and pass full 30-node tree to SkillTreeUI", async () => {
// Create a mock 30-node tree (simplified version)
const mock30NodeTree = {
id: "TREE_CLASS_VANGUARD",
nodes: {},
};
// Generate 30 node IDs
for (let i = 1; i <= 30; i++) {
mock30NodeTree.nodes[`NODE_${i}`] = {
id: `NODE_${i}`,
tier: Math.ceil(i / 6),
type: i % 3 === 0 ? "STAT_BOOST" : "ACTIVE_SKILL",
data: i % 3 === 0
? { stat: "health", value: i }
: { id: `SKILL_${i}`, name: `Skill ${i}` },
req: Math.ceil(i / 6),
cost: Math.ceil(i / 10),
children: i < 30 ? [`NODE_${i + 1}`] : [],
};
}
element.unit = testUnit;
element.treeDef = mock30NodeTree;
element.activeTab = "SKILLS";
await waitForUpdate();
const skillTree = queryShadow("skill-tree-ui");
expect(skillTree).to.exist;
expect(skillTree.treeDef).to.exist;
expect(skillTree.treeDef.id).to.equal("TREE_CLASS_VANGUARD");
expect(Object.keys(skillTree.treeDef.nodes)).to.have.length(30);
});
it("should handle treeDef with all node types from template", async () => {
const mockFullTree = {
id: "TREE_TEST",
nodes: {
NODE_T1_1: {
tier: 1,
type: "STAT_BOOST",
data: { stat: "health", value: 2 },
req: 1,
cost: 1,
children: ["NODE_T2_1", "NODE_T2_2"],
},
NODE_T2_1: {
tier: 2,
type: "STAT_BOOST",
data: { stat: "defense", value: 2 },
req: 2,
cost: 1,
children: ["NODE_T3_1"],
},
NODE_T2_2: {
tier: 2,
type: "ACTIVE_SKILL",
data: { id: "SKILL_1", name: "Shield Bash" },
req: 2,
cost: 1,
children: ["NODE_T3_2"],
},
NODE_T3_1: {
tier: 3,
type: "STAT_BOOST",
data: { stat: "health", value: 6 },
req: 3,
cost: 1,
children: [],
},
NODE_T3_2: {
tier: 3,
type: "ACTIVE_SKILL",
data: { id: "SKILL_2", name: "Taunt" },
req: 3,
cost: 1,
children: [],
},
NODE_T4_1: {
tier: 4,
type: "ACTIVE_SKILL",
data: { id: "SKILL_3", name: "Skill 3" },
req: 4,
cost: 2,
children: [],
},
NODE_T4_2: {
tier: 4,
type: "PASSIVE_ABILITY",
data: { effect_id: "PASSIVE_1", name: "Passive 1" },
req: 4,
cost: 2,
children: [],
},
},
};
element.unit = testUnit;
element.treeDef = mockFullTree;
element.activeTab = "SKILLS";
await waitForUpdate();
const skillTree = queryShadow("skill-tree-ui");
expect(skillTree).to.exist;
expect(skillTree.treeDef).to.equal(mockFullTree);
// Verify all node types are present
const nodes = skillTree.treeDef.nodes;
expect(nodes.NODE_T1_1.type).to.equal("STAT_BOOST");
expect(nodes.NODE_T2_2.type).to.equal("ACTIVE_SKILL");
expect(nodes.NODE_T4_2.type).to.equal("PASSIVE_ABILITY");
});
it("should use treeDef in _getTreeDefinition method", async () => {
const mockTree = {
id: "TREE_TEST",
nodes: {
NODE_1: {
tier: 1,
type: "STAT_BOOST",
data: { stat: "health", value: 2 },
req: 1,
cost: 1,
children: [],
},
},
};
element.unit = testUnit;
element.treeDef = mockTree;
await waitForUpdate();
const treeDef = element._getTreeDefinition();
expect(treeDef).to.equal(mockTree);
expect(treeDef.id).to.equal("TREE_TEST");
});
it("should pass treeDef to recalculateStats when unlocking nodes", async () => {
testUnit.classMastery["CLASS_VANGUARD"].skillPoints = 2;
testUnit.classMastery["CLASS_VANGUARD"].unlockedNodes = [];
testUnit.recalculateStats = () => {}; // Mock function
const mockTree = {
id: "TREE_TEST",
nodes: {
NODE_1: {
tier: 1,
type: "STAT_BOOST",
data: { stat: "health", value: 10 },
req: 1,
cost: 1,
children: [],
},
},
};
let recalculateStatsCalledWith = null;
testUnit.recalculateStats = (itemRegistry, treeDef) => {
recalculateStatsCalledWith = { itemRegistry, treeDef };
};
element.unit = testUnit;
element.treeDef = mockTree;
await waitForUpdate();
const unlockEvent = new CustomEvent("unlock-request", {
detail: { nodeId: "NODE_1", cost: 1 },
bubbles: true,
composed: true,
});
element._handleUnlockRequest(unlockEvent);
await waitForUpdate();
expect(recalculateStatsCalledWith).to.exist;
expect(recalculateStatsCalledWith.treeDef).to.equal(mockTree);
});
});
});
describe("CoA 4: Context Awareness", () => {
it("should display inventory tab", async () => {
element.unit = testUnit;
element.inventory = [];
element.activeTab = "INVENTORY";
await waitForUpdate();
const inventoryGrid = queryShadow(".inventory-grid");
expect(inventoryGrid).to.exist;
});
it("should show empty message when inventory is empty", async () => {
element.unit = testUnit;
element.inventory = [];
element.activeTab = "INVENTORY";
await waitForUpdate();
const emptyMessage = queryShadow(".inventory-grid p");
expect(emptyMessage).to.exist;
expect(emptyMessage.textContent).to.include("No items available");
});
it("should display mastery tab", async () => {
element.unit = testUnit;
element.activeTab = "MASTERY";
await waitForUpdate();
const masteryContainer = queryShadow(".mastery-container");
expect(masteryContainer).to.exist;
});
it("should show mastery progress for all classes", async () => {
testUnit.classMastery["CLASS_VANGUARD"] = {
level: 5,
xp: 250,
skillPoints: 2,
unlockedNodes: [],
};
testUnit.classMastery["CLASS_WEAVER"] = {
level: 2,
xp: 50,
skillPoints: 0,
unlockedNodes: [],
};
element.unit = testUnit;
element.activeTab = "MASTERY";
await waitForUpdate();
const masteryClasses = queryShadowAll(".mastery-class");
expect(masteryClasses.length).to.equal(2);
});
});
describe("Header Rendering", () => {
it("should display unit name, class, and level", async () => {
element.unit = testUnit;
await waitForUpdate();
const name = queryShadow(".name");
expect(name.textContent).to.include("Test Vanguard");
const classTitle = queryShadow(".class-title");
expect(classTitle.textContent).to.include("Vanguard");
const level = queryShadow(".level");
expect(level.textContent).to.include("Level 5");
});
it("should display XP bar", async () => {
element.unit = testUnit;
await waitForUpdate();
const xpBar = queryShadow(".xp-bar-container");
expect(xpBar).to.exist;
const xpLabel = queryShadow(".xp-label");
expect(xpLabel.textContent).to.include("250");
});
it("should display close button", async () => {
element.unit = testUnit;
await waitForUpdate();
const closeButton = queryShadow(".close-button");
expect(closeButton).to.exist;
});
it("should dispatch close event when close button is clicked", async () => {
element.unit = testUnit;
await waitForUpdate();
let closeEventFired = false;
element.addEventListener("close", () => {
closeEventFired = true;
});
const closeButton = queryShadow(".close-button");
closeButton.click();
expect(closeEventFired).to.be.true;
});
});
describe("Tab Switching", () => {
it("should switch between tabs", async () => {
element.unit = testUnit;
await waitForUpdate();
const inventoryTab = queryShadowAll(".tab-button")[0];
const skillsTab = queryShadowAll(".tab-button")[1];
const masteryTab = queryShadowAll(".tab-button")[2];
expect(inventoryTab.classList.contains("active")).to.be.true;
skillsTab.click();
await waitForUpdate();
expect(element.activeTab).to.equal("SKILLS");
expect(skillsTab.classList.contains("active")).to.be.true;
masteryTab.click();
await waitForUpdate();
expect(element.activeTab).to.equal("MASTERY");
expect(masteryTab.classList.contains("active")).to.be.true;
});
});
});