160 lines
6.2 KiB
TypeScript
160 lines
6.2 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'vitest'
|
|
import { SkillHotkeysHud, DEFAULT_SORCERESS_SKILLS, LEFT_SKILL_BOUNDS, RIGHT_SKILL_BOUNDS } from '../src/ui/hotkeys.ts'
|
|
import { HudManager } from '../src/ui/hud-manager.ts'
|
|
import { CLASS_DEFAULT_SKILLS, createStarterProfileForClass } from '../src/game/class-starter-profiles.ts'
|
|
import { SKILLS_BY_ID } from '../src/data/skills-catalog.ts'
|
|
|
|
describe('Issue Fix: Glacial Spike (#55) and All Active Skills on Quickbar Speedbars', () => {
|
|
let hud: SkillHotkeysHud
|
|
|
|
beforeEach(() => {
|
|
hud = new SkillHotkeysHud()
|
|
})
|
|
|
|
describe('1. Sorceress Right-Click & Left-Click Quickbar includes Glacial Spike', () => {
|
|
it('has Glacial Spike (55) in DEFAULT_SORCERESS_SKILLS with level 10 and authentic mana', () => {
|
|
const entry = DEFAULT_SORCERESS_SKILLS.find(s => s.skillId === 55)
|
|
expect(entry).toBeDefined()
|
|
expect(entry!.name).toBe('Glacial Spike')
|
|
expect(entry!.nameZh).toBe('冰尖柱')
|
|
expect(entry!.level).toBe(10)
|
|
expect(entry!.manaCost).toBeCloseTo(14.5, 1)
|
|
expect(entry!.leftUsable).toBe(true)
|
|
expect(entry!.rightUsable).toBe(true)
|
|
})
|
|
|
|
it('shows Glacial Spike in right-click speedbar popup cells', () => {
|
|
const rightCells = hud.getSpeedbarSkills('right')
|
|
const spikeCell = rightCells.find(c => c.skill.skillId === 55)
|
|
expect(spikeCell).toBeDefined()
|
|
expect(spikeCell!.skill.name).toBe('Glacial Spike')
|
|
expect(spikeCell!.skill.nameZh).toBe('冰尖柱')
|
|
})
|
|
|
|
it('shows Glacial Spike in left-click speedbar popup cells', () => {
|
|
const leftCells = hud.getSpeedbarSkills('left')
|
|
const spikeCell = leftCells.find(c => c.skill.skillId === 55)
|
|
expect(spikeCell).toBeDefined()
|
|
expect(spikeCell!.skill.name).toBe('Glacial Spike')
|
|
})
|
|
|
|
it('assigns Glacial Spike to right-click slot when clicked in right speedbar', () => {
|
|
hud.openSpeedbar('right')
|
|
expect(hud.selectionOpen).toBe('right')
|
|
|
|
const rightCells = hud.getSpeedbarSkills('right')
|
|
const spikeCell = rightCells.find(c => c.skill.skillId === 55)!
|
|
expect(spikeCell).toBeDefined()
|
|
|
|
const handled = hud.handleClick(spikeCell.x + 10, spikeCell.y + 10)
|
|
expect(handled).toBe(true)
|
|
expect(hud.rightSkillId).toBe(55)
|
|
expect(hud.selectionOpen).toBeNull()
|
|
})
|
|
|
|
it('assigns Glacial Spike to left-click slot when clicked in left speedbar', () => {
|
|
hud.openSpeedbar('left')
|
|
expect(hud.selectionOpen).toBe('left')
|
|
|
|
const leftCells = hud.getSpeedbarSkills('left')
|
|
const spikeCell = leftCells.find(c => c.skill.skillId === 55)!
|
|
expect(spikeCell).toBeDefined()
|
|
|
|
const handled = hud.handleClick(spikeCell.x + 10, spikeCell.y + 10)
|
|
expect(handled).toBe(true)
|
|
expect(hud.leftSkillId).toBe(55)
|
|
expect(hud.selectionOpen).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('2. All 7 Classes Starter Profiles include all Active Skills', () => {
|
|
it('provides all 26 active skills on Sorceress right-click speedbar', () => {
|
|
const sorProfile = createStarterProfileForClass('sor')
|
|
const rightSkills = sorProfile.availableSkills.filter(s => s.rightUsable)
|
|
// 3 universal (Attack 0, Throw 2, Unsummon 3) + 26 Sorceress active = 29
|
|
expect(rightSkills.length).toBe(29)
|
|
|
|
// Glacial Spike is explicitly present
|
|
expect(rightSkills.some(s => s.skillId === 55)).toBe(true)
|
|
})
|
|
|
|
it('provides all active skills for all 7 classes without passives', () => {
|
|
const classes = ['ama', 'sor', 'nec', 'pal', 'bar', 'dru', 'ass'] as const
|
|
const expectedActiveCounts: Record<string, number> = {
|
|
ama: 27, // 3 universal + 24 active
|
|
sor: 29, // 3 universal + 26 active
|
|
nec: 30, // 3 universal + 27 active
|
|
pal: 33, // 3 universal + 30 active
|
|
bar: 23, // 3 universal + 20 active
|
|
dru: 32, // 3 universal + 29 active
|
|
ass: 31, // 3 universal + 28 active
|
|
}
|
|
|
|
for (const cls of classes) {
|
|
const skills = CLASS_DEFAULT_SKILLS[cls]
|
|
expect(skills.length).toBe(expectedActiveCounts[cls])
|
|
// None should be passive
|
|
for (const s of skills) {
|
|
expect(s.isPassive).toBeFalsy()
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('3. HudManager Skill Tree Allocation dynamically updates Available Skills', () => {
|
|
const mockCanvas = {
|
|
width: 800,
|
|
height: 600,
|
|
style: { cursor: '' },
|
|
addEventListener: () => {},
|
|
removeEventListener: () => {},
|
|
getContext: () => null,
|
|
} as unknown as HTMLCanvasElement
|
|
|
|
function createMockHud() {
|
|
const hud = new HudManager(mockCanvas, {
|
|
onToggleAutomap: () => {},
|
|
onWaypointTeleport: () => {},
|
|
})
|
|
hud.switchClass('sor')
|
|
return hud
|
|
}
|
|
|
|
it('initializes HudManager with Glacial Spike available in Sorceress profile', () => {
|
|
const hudManager = createMockHud()
|
|
expect(hudManager.hotkeys.availableSkills.some(s => s.skillId === 55)).toBe(true)
|
|
})
|
|
|
|
it('allocates new skill in skill tree and adds it to hotkeys.availableSkills', () => {
|
|
const hudManager = createMockHud()
|
|
// Open Skill tree panel
|
|
hudManager.rightPanel = 'skill'
|
|
hudManager.skillTree.visible = true
|
|
hudManager.skillTree.unspentSkillPoints = 5
|
|
|
|
// Select lightning tab (tab 1)
|
|
hudManager.skillTree.activeTab = 1
|
|
|
|
// Find Nova (48) which requires level 12 and has reqskills: Static Field (42) which is already allocated in starter
|
|
const nova = hudManager.hotkeys.availableSkills.find(s => s.skillId === 48)
|
|
expect(nova).toBeDefined()
|
|
|
|
// Allocate point into Nova by clicking on it
|
|
const node = hudManager.skillTree.getSkillTree().find(n => n.skillId === 48)!
|
|
expect(node).toBeDefined()
|
|
|
|
const prevPoints = hudManager.skillTree.hardPoints.get(48) ?? 0
|
|
const allocated = hudManager.skillTree.allocateSkill(48)
|
|
expect(allocated).toBe(true)
|
|
|
|
// Test handleSkillAllocated
|
|
const privateMethod = (hudManager as any).handleSkillAllocated.bind(hudManager)
|
|
privateMethod(48)
|
|
|
|
const updatedNova = hudManager.hotkeys.availableSkills.find(s => s.skillId === 48)
|
|
expect(updatedNova).toBeDefined()
|
|
expect(updatedNova!.level).toBe(prevPoints + 1)
|
|
})
|
|
})
|
|
})
|