import { describe, expect, it } from "vitest"; import { WOW335_ABILITIES_BY_CLASS } from "./wow335AbilityCatalog"; import { WOW335_MAGE_MISSILE_BARRAGE_AURA, WOW335_MAGE_MISSILE_BARRAGE_REACHABLE_CONSUMER_SPELL_NAMES, WOW335_MAGE_MISSILE_BARRAGE_REACHABLE_TRIGGER_SPELL_NAMES, WOW335_MAGE_MISSILE_BARRAGE_TAG, WOW335_MAGE_MISSILE_BARRAGE_TOOLTIP_CONSUMER_SPELL_NAMES, WOW335_MAGE_MISSILE_BARRAGE_TOOLTIP_TRIGGER_SPELL_NAMES, WOW335_MAGE_MISSILE_BARRAGE_UNAVAILABLE_CONSUMER_SPELL_NAMES, WOW335_MAGE_MISSILE_BARRAGE_UNAVAILABLE_TRIGGER_SPELL_NAMES, WOW335_MAGE_MISSILE_BARRAGE_TALENT_ID, wow335MageMissileBarrageConsumerEligible, wow335MageMissileBarrageProfileForRank, wow335MageMissileBarrageReachableProcChancePercentForSpell, wow335MageMissileBarrageTooltipProcChancePercentForSpell, } from "./wow335MageMissileBarrageRuntime"; import { WOW335_TALENT_NODES } from "./wow335Talents.generated"; describe("WoW 3.3.5 Mage Missile Barrage profile", () => { it("uses the generated per-rank chance groups instead of the generic rank proc chance", () => { const talent = WOW335_TALENT_NODES.find((candidate) => candidate.id === WOW335_MAGE_MISSILE_BARRAGE_TALENT_ID); expect(talent).toMatchObject({ name: "Missile Barrage", rankSpellIds: [44404, 54486, 54488], rankDescriptions: expect.arrayContaining([expect.any(String)]), }); expect(talent!.rankDescriptions).toHaveLength(3); const expectedByRank = [ { primary: 14, secondary: 7, orb: 33 }, { primary: 27, secondary: 14, orb: 66 }, { primary: 40, secondary: 20, orb: 100 }, ]; const groups = [ ["Arcane Blast", "Starfire", "Starsurge"], ["Arcane Barrage", "Fireball", "Frostbolt", "Slow", "Moonfire"], ["Arcane Orb"], ]; expectedByRank.forEach((expected, index) => { const rank = index + 1; for (const spellName of groups[0]!) { expect(wow335MageMissileBarrageTooltipProcChancePercentForSpell(rank, spellName)).toBe(expected.primary); } for (const spellName of groups[1]!) { expect(wow335MageMissileBarrageTooltipProcChancePercentForSpell(rank, spellName)).toBe(expected.secondary); } expect(wow335MageMissileBarrageTooltipProcChancePercentForSpell(rank, "Arcane Orb")).toBe(expected.orb); }); // The existing generated DBC rank procChance values are 7/14/20; those // values must not flatten the custom primary-trigger and Arcane Orb groups. expect(talent!.rankEffects.map((effect) => effect.procChance)).toEqual([7, 14, 20]); expect(wow335MageMissileBarrageTooltipProcChancePercentForSpell(3, "Arcane Blast")).toBe(40); expect(wow335MageMissileBarrageTooltipProcChancePercentForSpell(3, "Arcane Orb")).toBe(100); }); it("separates tooltip-listed trigger and consumer spells from current Mage catalog rows", () => { const mageAbilityNames = new Set(WOW335_ABILITIES_BY_CLASS.mage.map((ability) => ability.name)); expect([...WOW335_MAGE_MISSILE_BARRAGE_TOOLTIP_TRIGGER_SPELL_NAMES].sort()).toEqual([ "Arcane Barrage", "Arcane Blast", "Arcane Orb", "Fireball", "Frostbolt", "Moonfire", "Slow", "Starfire", "Starsurge", ]); expect([...WOW335_MAGE_MISSILE_BARRAGE_REACHABLE_TRIGGER_SPELL_NAMES].sort()).toEqual([ "Arcane Barrage", "Arcane Blast", "Fireball", "Frostbolt", "Slow", ]); expect([...WOW335_MAGE_MISSILE_BARRAGE_UNAVAILABLE_TRIGGER_SPELL_NAMES].sort()).toEqual([ "Arcane Orb", "Moonfire", "Starfire", "Starsurge", ]); expect([...WOW335_MAGE_MISSILE_BARRAGE_TOOLTIP_CONSUMER_SPELL_NAMES]).toEqual(["Arcane Missiles"]); expect([...WOW335_MAGE_MISSILE_BARRAGE_REACHABLE_CONSUMER_SPELL_NAMES]).toEqual(["Arcane Missiles"]); expect([...WOW335_MAGE_MISSILE_BARRAGE_UNAVAILABLE_CONSUMER_SPELL_NAMES]).toEqual([]); for (const name of WOW335_MAGE_MISSILE_BARRAGE_REACHABLE_TRIGGER_SPELL_NAMES) { expect(mageAbilityNames.has(name), name).toBe(true); } for (const name of WOW335_MAGE_MISSILE_BARRAGE_UNAVAILABLE_TRIGGER_SPELL_NAMES) { expect(mageAbilityNames.has(name), name).toBe(false); } expect(wow335MageMissileBarrageConsumerEligible("Arcane Missiles")).toBe(true); expect(wow335MageMissileBarrageConsumerEligible("Fireball")).toBe(false); expect(wow335MageMissileBarrageConsumerEligible("Arcane Orb")).toBe(false); }); it("does not expose proc chances for unavailable spells or spells omitted by the generated tooltip", () => { expect(wow335MageMissileBarrageReachableProcChancePercentForSpell(3, "Arcane Blast")).toBe(40); expect(wow335MageMissileBarrageReachableProcChancePercentForSpell(3, "Fireball")).toBe(20); expect(wow335MageMissileBarrageReachableProcChancePercentForSpell(3, "Arcane Orb")).toBe(0); expect(wow335MageMissileBarrageTooltipProcChancePercentForSpell(3, "Frostfire Bolt")).toBe(0); expect(wow335MageMissileBarrageReachableProcChancePercentForSpell(3, "Frostfire Bolt")).toBe(0); expect(WOW335_MAGE_MISSILE_BARRAGE_TOOLTIP_TRIGGER_SPELL_NAMES.has("Frostfire Bolt")).toBe(false); }); it("normalizes ranks and exposes the missing duration and generated effect magnitudes", () => { expect(wow335MageMissileBarrageProfileForRank(0)).toMatchObject({ rank: 0, auraDurationMs: 15_000, channelDurationMultiplier: 0.5, missileIntervalMultiplier: 0.5, manaCostReductionPercent: 100, tooltipProcChancePercentBySpellName: {}, }); expect(wow335MageMissileBarrageProfileForRank(1.9).rank).toBe(1); expect(wow335MageMissileBarrageProfileForRank(-2).rank).toBe(0); expect(wow335MageMissileBarrageProfileForRank(9).rank).toBe(3); expect(wow335MageMissileBarrageProfileForRank(Number.NaN).rank).toBe(0); expect(wow335MageMissileBarrageProfileForRank(Number.POSITIVE_INFINITY).rank).toBe(0); expect(WOW335_MAGE_MISSILE_BARRAGE_AURA).toMatchObject({ id: "wow335:mage:missile-barrage", name: "Missile Barrage", durationMs: 15_000, maxStacks: 1, utilityTags: [WOW335_MAGE_MISSILE_BARRAGE_TAG], }); }); });