import { describe, expect, it } from "vitest"; import { abilityAtLevel, abilityById } from "./abilityCatalog"; import { WOW335_ABILITIES_BY_CLASS } from "./wow335AbilityCatalog"; import { WOW335_MAGE_FROST_WARDING_ARMOR_ABILITIES, WOW335_MAGE_FROST_WARDING_ARMOR_TAG, WOW335_MAGE_FROST_WARDING_TALENT_ID, WOW335_MAGE_FROST_WARDING_WARD_ABILITIES, wow335MageFrostWardingArmorAuraEffect, wow335MageFrostWardingProcPlan, wow335MageFrostWardingProfile, } from "./wow335MageFrostWardingRuntime"; const procEvent = (overrides: Partial[1]> = {}) => ({ wardOwnerClassId: "mage", eventKind: "spell-damage", wardAbilityId: WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.fire.abilityId, wardRankSpellId: 43010, wardAuraActive: true, damageSchool: "fire", spellDamage: 200, roll: 0, ...overrides, }); describe("WoW 3.3.5a Mage Frost Warding", () => { it("reads the generated rank chance, reduction, Mana refund, and armor/resistance values", () => { expect(WOW335_MAGE_FROST_WARDING_TALENT_ID).toBe("talent-70"); expect([1, 2].map(wow335MageFrostWardingProfile)).toEqual([ { rank: 1, rankSpellId: 11189, armorAndResistanceBonusPercent: 25, wardProcChancePercent: 25, wardDamageReductionPercent: 50, manaRefundPerDamageReduced: 1, }, { rank: 2, rankSpellId: 28332, armorAndResistanceBonusPercent: 50, wardProcChancePercent: 50, wardDamageReductionPercent: 50, manaRefundPerDamageReduced: 1, }, ]); expect(wow335MageFrostWardingProfile(0)).toBeNull(); expect(wow335MageFrostWardingProfile(3)).toBeNull(); }); it.each([ { armor: "frost" as const, rank: 1, spellId: 7301, expected: { armor: 250 }, }, { armor: "frost" as const, rank: 2, spellId: 7301, expected: { armor: 300 }, }, { armor: "ice" as const, rank: 1, spellId: 43008, expected: { armor: 1162.5, "resistance:frost": 50 }, }, { armor: "ice" as const, rank: 2, spellId: 43008, expected: { armor: 1395, "resistance:frost": 60 }, }, ])("scales only the exact local $armor armor aura at Frost Warding rank $rank", ({ armor, rank, spellId, expected }) => { const row = WOW335_MAGE_FROST_WARDING_ARMOR_ABILITIES[armor]; const baseAbility = abilityById(row.abilityId); if (!baseAbility) throw new Error(`Missing local ${row.name} ability.`); const ability = abilityAtLevel(baseAbility, 80); expect(ability.dbcSpellId).toBe(spellId); const armorEffects = ability.effects.filter((effect) => effect.kind === "apply-aura" && effect.sourceAuraType === 22); expect(armorEffects.length).toBeGreaterThan(0); const actual: Record = {}; for (const effect of armorEffects) { if (effect.kind !== "apply-aura") continue; const applied = wow335MageFrostWardingArmorAuraEffect(ability, rank, effect); expect(applied.aura.utilityTags).toContain(WOW335_MAGE_FROST_WARDING_ARMOR_TAG); for (const modifier of applied.aura.modifiers ?? []) { if (modifier.kind === "stat" && (modifier.stat === "armor" || modifier.stat.startsWith("resistance:"))) { actual[modifier.stat] = (actual[modifier.stat] ?? 0) + modifier.value; } } } expect(actual).toEqual(expected); }); it("requires a learned rank and the exact local Mage Frost/Ice Armor source aura", () => { const baseAbility = abilityById(WOW335_MAGE_FROST_WARDING_ARMOR_ABILITIES.ice.abilityId)!; const ability = abilityAtLevel(baseAbility, 80); const effect = ability.effects.find((candidate) => candidate.kind === "apply-aura" && candidate.sourceAuraType === 22)!; if (effect.kind !== "apply-aura") throw new Error("Ice Armor has no stat aura."); expect(wow335MageFrostWardingArmorAuraEffect(ability, 0, effect)).toBe(effect); expect(wow335MageFrostWardingArmorAuraEffect({ ...ability, classId: "warlock" }, 2, effect)).toBe(effect); expect(wow335MageFrostWardingArmorAuraEffect({ ...ability, dbcSpellId: 12345 }, 2, effect)).toBe(effect); const wrongAuraType = { ...effect, sourceAuraType: 42 }; expect(wow335MageFrostWardingArmorAuraEffect(ability, 2, wrongAuraType)).toBe(wrongAuraType); expect(wow335MageFrostWardingArmorAuraEffect(ability, 2, { ...effect, sourceAuraType: 42, })).toMatchObject({ sourceAuraType: 42 }); expect(wow335MageFrostWardingArmorAuraEffect(ability, 2, { ...effect, aura: { ...effect.aura, modifiers: [ { kind: "stat", stat: "armor", operation: "flat", value: 0 }, { kind: "stat", stat: "resistance:frost", operation: "flat", value: -5 }, { kind: "stat", stat: "spell-power", operation: "flat", value: 25 }, ], }, })).toMatchObject({ aura: { modifiers: [ { kind: "stat", stat: "armor", value: 0 }, { kind: "stat", stat: "resistance:frost", value: -5 }, { kind: "stat", stat: "spell-power", value: 25 }, ] } }); }); it("keeps the ward school selector separate from rank-specific chance", () => { expect(WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.fire.school).toBe("fire"); expect(WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.frost.school).toBe("frost"); expect(wow335MageFrostWardingProfile(1)?.wardProcChancePercent).toBe(25); expect(wow335MageFrostWardingProfile(2)?.wardProcChancePercent).toBe(50); const frostEvent = procEvent({ wardAbilityId: WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.frost.abilityId, wardRankSpellId: 43012, damageSchool: "frost", roll: 0.249, }); expect(wow335MageFrostWardingProcPlan(1, frostEvent)?.wardAbilityId) .toBe(WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.frost.abilityId); expect(wow335MageFrostWardingProcPlan(1, { ...frostEvent, damageSchool: "fire" })).toBeNull(); }); it("binds its supported Fire and Frost Wards to local Mage Spell.dbc ability rows", () => { const mageRows = new Map(WOW335_ABILITIES_BY_CLASS.mage.map((ability) => [ability.id, ability])); expect(mageRows.get(WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.fire.abilityId)?.name).toBe("Fire Ward"); expect(mageRows.get(WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.frost.abilityId)?.name).toBe("Frost Ward"); for (const ward of Object.values(WOW335_MAGE_FROST_WARDING_WARD_ABILITIES)) { const row = mageRows.get(ward.abilityId); expect(row?.source).toBe("wow335-progression"); expect(row?.ranks?.map((rank) => rank.spellId)).toEqual(ward.rankSpellIds); } const otherClassRows = new Map(WOW335_ABILITIES_BY_CLASS.warlock.map((ability) => [ability.name, ability])); expect(otherClassRows.get("Shadow Ward")?.id).toBe("wow335-warlock-shadow-ward"); expect(mageRows.get("wow335-mage-arcane-ward")).toBeUndefined(); }); it.each([ { rank: 1, event: procEvent({ roll: 0.249 }), expectedRankSpellId: 11189, }, { rank: 2, event: procEvent({ roll: 0.499 }), expectedRankSpellId: 28332, }, { rank: 2, event: procEvent({ wardAbilityId: WOW335_MAGE_FROST_WARDING_WARD_ABILITIES.frost.abilityId, wardRankSpellId: 43012, damageSchool: "Frost", roll: 0.2, }), expectedRankSpellId: 28332, }, ])("plans a matching ward proc at the generated rank chance", ({ rank, event, expectedRankSpellId }) => { expect(wow335MageFrostWardingProcPlan(rank, event)).toEqual({ rankSpellId: expectedRankSpellId, wardAbilityId: event.wardAbilityId, wardDamageReductionPercent: 50, manaRefundPerDamageReduced: 1, }); }); it("uses an exclusive upper roll boundary for 25% and 50% proc chances", () => { expect(wow335MageFrostWardingProcPlan(1, procEvent({ roll: 0.25 }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ roll: 0.5 }))).toBeNull(); }); it("requires the Mage owner, a learned rank, an active matching ward, and positive spell damage", () => { expect(wow335MageFrostWardingProcPlan(2, procEvent({ wardOwnerClassId: "warlock" }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(0, procEvent())).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ wardAuraActive: false }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ wardRankSpellId: 43012 }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ damageSchool: "frost" }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ eventKind: "weapon-damage" }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ spellDamage: 0 }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ spellDamage: -1 }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ roll: Number.NaN }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ roll: 1 }))).toBeNull(); }); it("does not synthesize support for the tooltip's absent Mage Arcane/Shadow Ward rows", () => { expect(wow335MageFrostWardingProcPlan(2, procEvent({ wardAbilityId: "wow335-warlock-shadow-ward", wardRankSpellId: 47891, damageSchool: "shadow", }))).toBeNull(); expect(wow335MageFrostWardingProcPlan(2, procEvent({ wardAbilityId: "wow335-mage-arcane-ward", wardRankSpellId: 43020, damageSchool: "arcane", }))).toBeNull(); }); });