Files
text-adventure-game/tests/unit/systems/skillSystem.test.js
Claude cb412544e9 Initial commit: Text Adventure Game
Features:
- Combat system with AP/EP hit calculation and three-layer defense
- Auto-combat/farming mode
- Item system with stacking support
- Skill system with levels, milestones, and parent skill sync
- Shop system with dynamic pricing
- Inventory management with bulk selling
- Event system
- Game loop with offline earnings
- Save/Load system

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:13:51 +08:00

404 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 技能系统单元测试
* 测试纯函数,不依赖框架
*/
describe('技能系统 - 纯函数测试', () => {
describe('getNextLevelExp - 下一级经验计算', () => {
test('应该正确计算下一级所需经验(默认公式)', () => {
// 默认公式: (currentLevel + 1) * 100
const getNextLevelExp = (skillId, currentLevel) => {
// 假设使用默认配置
return (currentLevel + 1) * 100
}
expect(getNextLevelExp('sword', 0)).toBe(100) // 1级需要100
expect(getNextLevelExp('sword', 1)).toBe(200) // 2级需要200
expect(getNextLevelExp('sword', 5)).toBe(600) // 6级需要600
expect(getNextLevelExp('sword', 9)).toBe(1000) // 10级需要1000
})
test('应该正确处理自定义经验公式', () => {
// 自定义公式: level * level * 50
const customExpPerLevel = (level) => level * level * 50
const getNextLevelExp = (skillId, currentLevel) => {
return customExpPerLevel(currentLevel + 1)
}
expect(getNextLevelExp('magic', 0)).toBe(50) // 1级: 1*1*50
expect(getNextLevelExp('magic', 1)).toBe(200) // 2级: 2*2*50
expect(getNextLevelExp('magic', 4)).toBe(1250) // 5级: 5*5*50
})
test('边界值测试', () => {
const getNextLevelExp = (skillId, currentLevel) => {
return (currentLevel + 1) * 100
}
expect(getNextLevelExp('test', 0)).toBe(100)
expect(getNextLevelExp('test', 99)).toBe(10000)
})
})
describe('技能升级逻辑', () => {
test('应该正确添加经验并升级', () => {
// 模拟升级逻辑
const addSkillExp = (skill, amount, expPerLevel) => {
skill.exp += amount
let leveledUp = false
while (skill.exp >= expPerLevel(skill.level + 1)) {
skill.exp -= expPerLevel(skill.level + 1)
skill.level++
leveledUp = true
}
return { leveledUp, newLevel: skill.level }
}
const skill = { level: 1, exp: 0 }
// 2级需要 200 经验
const expPerLevel = (level) => level * 100
// 添加200经验正好升级到2级
let result = addSkillExp(skill, 200, expPerLevel)
expect(result.leveledUp).toBe(true)
expect(result.newLevel).toBe(2)
expect(skill.exp).toBe(0)
// 再添加300经验升级到3级需要300
result = addSkillExp(skill, 300, expPerLevel)
expect(result.leveledUp).toBe(true)
expect(result.newLevel).toBe(3)
expect(skill.exp).toBe(0)
})
test('应该正确处理连续升级', () => {
const skill = { level: 1, exp: 0 }
const expPerLevel = (level) => level * 100
// 添加500经验应该从1级升到3级200+300
const addSkillExp = (skill, amount, expPerLevel) => {
skill.exp += amount
let leveledUp = false
while (skill.exp >= expPerLevel(skill.level + 1)) {
skill.exp -= expPerLevel(skill.level + 1)
skill.level++
leveledUp = true
}
return { leveledUp, newLevel: skill.level }
}
const result = addSkillExp(skill, 500, expPerLevel)
expect(result.leveledUp).toBe(true)
expect(result.newLevel).toBe(3)
expect(skill.exp).toBe(0)
})
test('经验不足时不应该升级', () => {
const skill = { level: 1, exp: 0 }
const expPerLevel = (level) => level * 100
const addSkillExp = (skill, amount, expPerLevel) => {
skill.exp += amount
let leveledUp = false
while (skill.exp >= expPerLevel(skill.level + 1)) {
skill.exp -= expPerLevel(skill.level + 1)
skill.level++
leveledUp = true
}
return { leveledUp, newLevel: skill.level }
}
const result = addSkillExp(skill, 50, expPerLevel)
expect(result.leveledUp).toBe(false)
expect(result.newLevel).toBe(1)
expect(skill.exp).toBe(50)
})
test('部分成功时经验应该减半', () => {
const EXP_PARTIAL_SUCCESS = 0.5
const calculatePartialExp = (amount) => {
return Math.floor(amount * EXP_PARTIAL_SUCCESS)
}
expect(calculatePartialExp(100)).toBe(50)
expect(calculatePartialExp(150)).toBe(75)
expect(calculatePartialExp(99)).toBe(49) // 向下取整
})
})
describe('技能等级上限', () => {
test('应该正确计算等级上限(玩家等级限制)', () => {
// 技能上限 = 玩家等级 * 2
const playerLevel = 5
const maxSkillLevel = playerLevel * 2
expect(maxSkillLevel).toBe(10)
const skillLevel = 10
const canLevelUp = skillLevel < maxSkillLevel
expect(canLevelUp).toBe(false)
})
test('应该正确计算等级上限(技能配置限制)', () => {
const playerLevel = 10
const skillMaxLevel = 5
// 取最小值
const effectiveMaxLevel = Math.min(playerLevel * 2, skillMaxLevel)
expect(effectiveMaxLevel).toBe(5)
})
test('达到等级上限时不再升级', () => {
const skill = { level: 5, exp: 0 }
const maxLevel = 5
const addSkillExp = (skill, amount, maxLevel) => {
if (skill.level >= maxLevel) {
return { leveledUp: false, newLevel: skill.level, capped: true }
}
skill.exp += amount
return { leveledUp: false, newLevel: skill.level, capped: false }
}
const result = addSkillExp(skill, 100, maxLevel)
expect(result.leveledUp).toBe(false)
expect(result.capped).toBe(true)
expect(result.newLevel).toBe(5)
})
})
describe('里程碑奖励', () => {
test('应该正确检测里程碑等级', () => {
const milestones = {
5: { effect: { damage: 5 } },
10: { effect: { damage: 10 } }
}
const hasMilestone = (level) => {
return !!milestones[level]
}
expect(hasMilestone(5)).toBe(true)
expect(hasMilestone(10)).toBe(true)
expect(hasMilestone(3)).toBe(false)
})
test('应该正确应用里程碑奖励', () => {
const skill = { level: 4 }
const milestones = {
5: { effect: { strength: 2 } },
10: { effect: { strength: 5 } }
}
const applyMilestone = (skill, milestones) => {
if (milestones[skill.level]) {
return milestones[skill.level].effect
}
return {}
}
// 5级时应用
skill.level = 5
const effect = applyMilestone(skill, milestones)
expect(effect).toEqual({ strength: 2 })
// 10级时应用
skill.level = 10
const effect2 = applyMilestone(skill, milestones)
expect(effect2).toEqual({ strength: 5 })
})
test('里程碑奖励应该只应用一次', () => {
const appliedMilestones = new Set()
const milestoneKey = 'sword_5'
const canApplyMilestone = (key) => {
return !appliedMilestones.has(key)
}
const applyMilestone = (key) => {
if (canApplyMilestone(key)) {
appliedMilestones.add(key)
return true
}
return false
}
expect(applyMilestone(milestoneKey)).toBe(true)
expect(applyMilestone(milestoneKey)).toBe(false)
expect(appliedMilestones.has(milestoneKey)).toBe(true)
})
})
describe('父技能系统', () => {
test('父技能等级应该等于所有子技能的最高等级', () => {
const childSkills = {
sword: { level: 5 },
axe: { level: 3 },
spear: { level: 7 }
}
const calculateParentLevel = (childSkills) => {
return Math.max(...Object.values(childSkills).map(s => s.level))
}
expect(calculateParentLevel(childSkills)).toBe(7)
})
test('子技能升级时应该更新父技能', () => {
const skills = {
sword: { level: 5 },
axe: { level: 3 },
combat: { level: 5 } // 父技能
}
const updateParentSkill = (skillId) => {
const childLevels = [skills.sword.level, skills.axe.level]
skills.combat.level = Math.max(...childLevels)
}
// sword 升到 7
skills.sword.level = 7
updateParentSkill('sword')
expect(skills.combat.level).toBe(7)
})
test('父技能应该自动初始化', () => {
const skills = {
sword: { level: 3, unlocked: true },
axe: { level: 0, unlocked: false }
}
const initParentSkill = () => {
if (!skills.combat) {
skills.combat = { level: 0, unlocked: true }
}
}
initParentSkill()
expect(skills.combat).toBeDefined()
expect(skills.combat.unlocked).toBe(true)
})
})
describe('经验倍率计算', () => {
test('应该正确计算基础经验倍率', () => {
const globalBonus = 0.2 // 20%全局加成
const milestoneBonus = 0.1 // 10%里程碑加成
const totalBonus = globalBonus + milestoneBonus
expect(totalBonus).toBeCloseTo(0.3, 1)
})
test('父技能高于子技能时应该有1.5倍加成', () => {
const parentLevel = 10
const childLevel = 5
const hasParentBonus = parentLevel > childLevel
const expMultiplier = hasParentBonus ? 1.5 : 1.0
expect(expMultiplier).toBe(1.5)
})
test('应该正确应用多个加成', () => {
const baseExp = 100
const globalBonus = 1.2 // +20%
const parentBonus = 1.5 // +50%
const milestoneBonus = 1.1 // +10%
const finalExp = baseExp * globalBonus * parentBonus * milestoneBonus
expect(finalExp).toBeCloseTo(198, 0) // 100 * 1.2 * 1.5 * 1.1 = 198
})
})
describe('技能解锁条件', () => {
test('应该正确检查物品条件', () => {
const playerInventory = ['stick', 'sword']
const requiredItem = 'stick'
const hasRequiredItem = (inventory, item) => {
return inventory.includes(item)
}
expect(hasRequiredItem(playerInventory, requiredItem)).toBe(true)
expect(hasRequiredItem(playerInventory, 'axe')).toBe(false)
})
test('应该正确检查位置条件', () => {
const playerLocation = 'camp'
const requiredLocation = 'camp'
const isInLocation = (current, required) => {
return current === required
}
expect(isInLocation(playerLocation, requiredLocation)).toBe(true)
expect(isInLocation(playerLocation, 'wild1')).toBe(false)
})
test('应该正确检查前置技能等级', () => {
const skills = {
basic: { level: 5, unlocked: true },
advanced: { level: 0, unlocked: false }
}
const checkPrerequisite = (skillId, requiredLevel) => {
const skill = skills[skillId]
return skill && skill.level >= requiredLevel
}
expect(checkPrerequisite('basic', 5)).toBe(true)
expect(checkPrerequisite('basic', 6)).toBe(false)
})
})
describe('边界条件', () => {
test('经验不能为负数', () => {
const skill = { level: 1, exp: 50 }
const addExpSafe = (skill, amount) => {
skill.exp = Math.max(0, skill.exp + amount)
return skill.exp
}
expect(addExpSafe(skill, -30)).toBe(20)
expect(addExpSafe(skill, -100)).toBe(0)
})
test('等级不能为负数', () => {
const skill = { level: 1, exp: 0 }
const levelUpSafe = (skill) => {
skill.level = Math.max(0, skill.level + 1)
return skill.level
}
expect(levelUpSafe(skill)).toBe(2)
})
test('空技能对象应该有默认值', () => {
const createSkill = () => ({
level: 0,
exp: 0,
unlocked: false
})
const skill = createSkill()
expect(skill.level).toBe(0)
expect(skill.exp).toBe(0)
expect(skill.unlocked).toBe(false)
})
})
})