import { describe, it, expect } from 'vitest'; import { natureMultiplier, statAt, calcDamage } from '../src/lib/damage-calc.js'; describe('natureMultiplier', () => { it('boosts, cuts, and no-ops', () => { expect(natureMultiplier('Adamant', 'atk')).toBe(1.1); expect(natureMultiplier('Adamant', 'spa')).toBe(0.9); expect(natureMultiplier('Adamant', 'spe')).toBe(1); expect(natureMultiplier('Hardy', 'atk')).toBe(1); // neutral nature expect(natureMultiplier('Nonsense', 'atk')).toBe(1); // unknown nature }); }); describe('statAt (Gen 3+ formula)', () => { it('HP and a normal stat at level 100, 31 IV / 0 EV / neutral', () => { // Garchomp: 108 base HP -> 357 ; 130 base Atk -> 296 expect(statAt(108, 100, { statKey: 'hp' })).toBe(357); expect(statAt(130, 100, { statKey: 'atk' })).toBe(296); }); it('applies nature to the raw stat', () => { expect(statAt(130, 100, { statKey: 'atk', nature: 'Adamant' })).toBe(325); // floor(296 * 1.1) expect(statAt(130, 100, { statKey: 'atk', nature: 'Modest' })).toBe(266); // floor(296 * 0.9) }); it('Shedinja HP is always 1', () => { expect(statAt(1, 100, { statKey: 'hp' })).toBe(1); }); }); describe('calcDamage', () => { it('returns null for status / zero-power moves', () => { expect(calcDamage({ power: 0, level: 50 })).toBeNull(); expect(calcDamage({ power: null, level: 50 })).toBeNull(); }); it('STAB + super effective, with the 85–100% roll', () => { const r = calcDamage({ level: 100, power: 80, moveType: 'water', atkTypes: ['water'], defTypes: ['ground', 'rock'], // 4x atkStat: 200, defStat: 100, gen: 9, }); expect(r.stab).toBe(true); expect(r.eff).toBe(4); expect(r.max).toBeGreaterThan(r.min); expect(r.min).toBeGreaterThanOrEqual(1); }); it('immunity short-circuits to 0', () => { const r = calcDamage({ level: 100, power: 100, moveType: 'ground', atkTypes: ['fire'], // no STAB, to keep the shape unambiguous defTypes: ['flying'], atkStat: 200, defStat: 100, gen: 9, }); expect(r).toEqual({ min: 0, max: 0, stab: false, eff: 0 }); }); it('crit multiplier differs pre/post Gen 6', () => { const base = { level: 100, power: 100, moveType: 'normal', atkTypes: ['normal'], defTypes: ['normal'], atkStat: 200, defStat: 100, crit: true, }; const g5 = calcDamage({ ...base, gen: 5 }); const g6 = calcDamage({ ...base, gen: 6 }); expect(g5.max).toBeGreaterThan(g6.max); // 2x vs 1.5x }); });