404 lines
12 KiB
JavaScript
404 lines
12 KiB
JavaScript
|
|
/**
|
|||
|
|
* 技能系统单元测试
|
|||
|
|
* 测试纯函数,不依赖框架
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
})
|