import { describe, expect, it } from "vitest"; import { abilityAtLevel, type AbilityDefinition, type AbilityEffect } from "./abilityCatalog"; import { WOW335_ABILITIES_BY_CLASS } from "./wow335AbilityCatalog"; function byId(id: string): AbilityDefinition { const ability = Object.values(WOW335_ABILITIES_BY_CLASS) .flat() .find((candidate) => candidate.id === id); expect(ability, id).toBeDefined(); return ability!; } function effectsOfKind( ability: Pick, kind: K, ): readonly Extract[] { return ability.effects.filter( (effect): effect is Extract => effect.kind === kind, ); } describe("WoW 3.3.5 executable effect mapping", () => { it("gives every imported classic rank executable or explicit utility semantics", () => { const effects = Object.values(WOW335_ABILITIES_BY_CLASS) .flat() .flatMap((ability) => ability.ranks?.flatMap((rank) => rank.effects) ?? ability.effects); expect(effects.length).toBeGreaterThan(1_000); expect(effectsOfKind({ effects }, "scripted")).toEqual([]); expect(effectsOfKind({ effects }, "unsupported")).toEqual([]); for (const effect of effectsOfKind({ effects }, "utility")) { expect(effect.action).toMatch(/^(?:create-item|open-lock|portal|weapon-enchant|pickpocket|farsight|class-script)$/); } for (const ability of Object.values(WOW335_ABILITIES_BY_CLASS).flat()) { if (ability.passive) continue; for (const rank of ability.ranks ?? []) { expect(rank.effects.length, `${ability.name} rank ${rank.rank}`).toBeGreaterThan(0); const hasExecutableSemantics = rank.effects.some((effect) => { if (["trigger-spell", "scripted", "unsupported"].includes(effect.kind)) return false; if (effect.kind !== "apply-aura") return true; return Boolean( effect.control || effect.aura.modifiers?.length || effect.aura.absorbs?.length || effect.aura.procs?.length || effect.aura.utilityTags?.length || effect.aura.controlImmunities?.length || effect.aura.reflectSpellChance || effect.aura.redirectDamagePercent, ); }); if (!hasExecutableSemantics && ability.talentNodeId) { expect(rank.effects.some((effect) => effect.kind !== "scripted" && effect.kind !== "unsupported"), `${ability.name} rank ${rank.rank}`).toBe(true); } else { expect(hasExecutableSemantics, `${ability.name} rank ${rank.rank}`).toBe(true); } } } }); it("does not promote triggered rank-zero child spells into player ranks", () => { expect(byId("wow335-warrior-execute").ranks?.map((rank) => rank.spellId)).not.toContain(20647); expect(byId("wow335-priest-divine-hymn").ranks?.map((rank) => rank.spellId)).toEqual([64843]); expect(byId("wow335-death-knight-death-coil").ranks?.map((rank) => rank.spellId)).not.toContain(47632); expect(byId("wow335-death-knight-death-coil")).toMatchObject({ target: "hostile", effects: expect.arrayContaining([expect.objectContaining({ kind: "damage" })]), }); }); it("freezes mana costs per learned rank while keeping form resources source-correct", () => { expect(byId("wow335-mage-frostbolt").cost).toEqual({ resource: "mana", amount: 17 }); const pain = byId("wow335-priest-shadow-word-pain"); expect(abilityAtLevel(pain, 26)).toMatchObject({ dbcSpellId: 992, cost: { resource: "mana", amount: 133 }, }); expect(abilityAtLevel(pain, 32)).toMatchObject({ dbcSpellId: 992, cost: { resource: "mana", amount: 133 }, }); expect(abilityAtLevel(pain, 34)).toMatchObject({ dbcSpellId: 2767, cost: { resource: "mana", amount: 188 }, }); expect(byId("wow335-druid-claw").cost).toMatchObject({ resource: "energy", amount: 45 }); expect(byId("wow335-druid-maul").cost).toMatchObject({ resource: "rage", amount: 15 }); }); it("never leaves an imported player mana cost tied to maximum mana", () => { const manaRanks = Object.values(WOW335_ABILITIES_BY_CLASS) .flat() .flatMap((ability) => ability.ranks ?? []) .filter((rank) => rank.cost.resource === "mana"); expect(manaRanks.length).toBeGreaterThan(100); for (const rank of manaRanks) { expect(rank.cost.percentage, `spell ${rank.spellId} rank ${rank.rank}`).not.toBe(true); expect(Number.isFinite(rank.cost.amount), `spell ${rank.spellId} rank ${rank.rank}`).toBe(true); } }); it("provides mechanics for previously silent class scripts and hidden proc spells", () => { expect(effectsOfKind(byId("wow335-warrior-slam"), "damage")).not.toEqual([]); expect(effectsOfKind(byId("wow335-warrior-bloodthirst"), "damage")).toContainEqual( expect.objectContaining({ coefficient: 1.2 }), ); expect(effectsOfKind(byId("wow335-warrior-bloodrage"), "resource")).toEqual([ { kind: "resource", resource: "rage", amount: 20 }, ]); expect(effectsOfKind(byId("wow335-warrior-bloodrage"), "heal")).toEqual([]); expect(effectsOfKind(byId("wow335-warrior-execute"), "execute")).toContainEqual( expect.objectContaining({ coefficient: 1.8 }), ); expect(effectsOfKind(byId("wow335-warrior-execute"), "heal")).toEqual([]); expect(effectsOfKind(byId("wow335-warrior-bladestorm"), "dot")).toEqual([ expect.objectContaining({ coefficient: 0.6, ticks: 6, intervalMs: 1_000 }), ]); expect(byId("wow335-warrior-bladestorm")).toMatchObject({ target: "self", radius: 8 }); expect(effectsOfKind(byId("wow335-paladin-hammer-of-the-righteous"), "damage")).toEqual([ expect.objectContaining({ coefficient: 1.2 }), ]); expect(effectsOfKind(byId("wow335-druid-swiftmend"), "heal")).toEqual([ expect.objectContaining({ coefficient: 2.2 }), ]); expect(effectsOfKind(byId("wow335-rogue-mutilate"), "damage")).toContainEqual( expect.objectContaining({ coefficient: 2 }), ); expect(effectsOfKind(byId("wow335-rogue-mutilate"), "combo")).not.toEqual([]); expect(effectsOfKind(byId("wow335-rogue-killing-spree"), "dot")).toContainEqual( expect.objectContaining({ coefficient: 0.5, ticks: 5 }), ); expect(effectsOfKind(byId("wow335-warlock-conflagrate"), "damage")).toEqual([ expect.objectContaining({ coefficient: 1.4 }), ]); expect(effectsOfKind(byId("wow335-death-knight-death-grip"), "pull")).not.toEqual([]); expect(effectsOfKind(byId("wow335-shaman-windfury-weapon"), "apply-aura")[0]?.aura.procs).not.toEqual([]); expect(effectsOfKind(byId("wow335-shaman-lightning-shield"), "apply-aura")[0]?.aura.procs).toEqual(expect.arrayContaining([ expect.objectContaining({ triggers: "damage-taken", charges: 3 }), ])); }); it("models Penance as the Discipline rotation's friendly heal", () => { const penance = byId("wow335-priest-penance"); expect(penance.target).toBe("friendly"); expect(effectsOfKind(penance, "heal")).toEqual([ expect.objectContaining({ coefficient: 1.8 }), ]); expect(effectsOfKind(penance, "damage")).toEqual([]); }); it("keeps triggered shield damage children out of learned Shaman ranks", () => { const lightningShield = abilityAtLevel(byId("wow335-shaman-lightning-shield"), 80); expect(lightningShield.dbcSpellId).toBe(49281); expect(effectsOfKind(lightningShield, "damage")).toEqual([]); expect(effectsOfKind(lightningShield, "apply-aura")[0]?.aura).toMatchObject({ exclusiveGroup: "shaman:elemental-shield", procs: expect.arrayContaining([expect.objectContaining({ triggers: "damage-taken", charges: 3 })]), }); const waterShield = abilityAtLevel(byId("wow335-shaman-water-shield"), 80); expect(waterShield.dbcSpellId).toBe(57960); expect(effectsOfKind(waterShield, "apply-aura").every((effect) => ( effect.aura.exclusiveGroup === "shaman:elemental-shield" ))).toBe(true); const earthShield = byId("wow335-shaman-earth-shield"); const earthProc = effectsOfKind(earthShield, "apply-aura") .flatMap((effect) => effect.aura.procs ?? []) .find((proc) => proc.id.endsWith(":earth-shield")); expect(earthProc).toMatchObject({ triggers: "damage-taken", charges: 9, internalCooldownMs: 3_000, action: { kind: "heal", target: "aura-target" }, }); expect(effectsOfKind(earthShield, "apply-aura").every((effect) => ( effect.aura.exclusiveGroup === "shaman:earth-shield" ))).toBe(true); }); it("models both offensive Spirit Wolves instead of the inert DBC summon marker", () => { expect(effectsOfKind(byId("wow335-shaman-feral-spirit"), "summon")).toEqual([ expect.objectContaining({ creatureId: 29264, coefficient: 0.35, durationMs: 45_000 }), expect.objectContaining({ creatureId: 29264, coefficient: 0.35, durationMs: 45_000 }), ]); }); it("creates source-named stat and avoidance auras", () => { const fortitude = effectsOfKind(byId("wow335-priest-power-word-fortitude"), "apply-aura")[0]; expect(fortitude).toMatchObject({ sourceEffectType: 6, sourceAuraType: 29, aura: { id: "wow335:1243:effect:0:aura:29", name: "Power Word: Fortitude (DBC 1243, Aura 29)", disposition: "buff", dispelCategory: "magic", modifiers: [{ kind: "stat", stat: "stamina", operation: "flat", value: 3 }], }, }); const evasion = effectsOfKind(byId("wow335-rogue-evasion"), "apply-aura")[0]; expect(evasion.aura.modifiers).toContainEqual({ kind: "stat", stat: "dodge", operation: "percent", value: 0.5, }); }); it("preserves permanent DBC toggles as non-expiring caster auras", () => { const devotion = effectsOfKind(byId("wow335-paladin-devotion-aura"), "apply-aura")[0]; expect(devotion).toMatchObject({ recipient: "caster", aura: { disposition: "buff", durationMs: null, exclusiveGroup: "paladin:aura", }, }); }); it("represents absorbs as named aura capacity instead of a generic shield", () => { const shield = byId("wow335-priest-power-word-shield"); const aura = effectsOfKind(shield, "apply-aura").find((effect) => effect.sourceAuraType === 69); expect(aura?.aura).toMatchObject({ disposition: "buff", durationMs: 30_000, absorbs: [{ amount: 44, perStack: false }], }); expect(shield.effects.some((effect) => effect.kind === "shield")).toBe(false); }); it("maps Paladin emergency cooldowns to their source-backed mechanics", () => { const layOnHands = byId("wow335-paladin-lay-on-hands"); expect(effectsOfKind(layOnHands, "heal")).toContainEqual(expect.objectContaining({ coefficient: 0, percentMaxHealth: 1, })); const handOfProtection = byId("wow335-paladin-hand-of-protection"); const immunity = effectsOfKind(handOfProtection, "apply-aura") .find((effect) => effect.sourceAuraType === 39); expect(immunity?.aura).toMatchObject({ durationMs: 6_000, modifiers: [{ kind: "damage", direction: "taken", school: "physical", operation: "percent", value: -1, }], }); }); it("keeps periodic damage and healing auras out of the generic HUD strip", () => { const pain = byId("wow335-priest-shadow-word-pain"); const painAura = effectsOfKind(pain, "apply-aura") .find((effect) => effect.sourceAuraType === 3); const renew = byId("wow335-priest-renew"); const renewAura = effectsOfKind(renew, "apply-aura") .find((effect) => effect.sourceAuraType === 8); expect(painAura?.aura).toMatchObject({ disposition: "debuff", hideFromAuraStrip: true, }); expect(renewAura?.aura).toMatchObject({ disposition: "buff", hideFromAuraStrip: true, }); expect(effectsOfKind(pain, "dot")).toHaveLength(1); expect(effectsOfKind(renew, "hot")).toHaveLength(1); }); it("maps cleanse, purge, and dual-purpose dispel categories", () => { expect(effectsOfKind(byId("wow335-paladin-cleanse"), "dispel")).toEqual(expect.arrayContaining([ expect.objectContaining({ mode: "cleanse", relationship: "friendly", categories: ["magic"] }), expect.objectContaining({ mode: "cleanse", relationship: "friendly", categories: ["disease"] }), expect.objectContaining({ mode: "cleanse", relationship: "friendly", categories: ["poison"] }), ])); expect(effectsOfKind(byId("wow335-shaman-purge"), "dispel")).toContainEqual( expect.objectContaining({ mode: "purge", relationship: "hostile", categories: ["magic"] }), ); expect(effectsOfKind(byId("wow335-priest-dispel-magic"), "dispel")).toContainEqual( expect.objectContaining({ relationship: "target", categories: ["magic"] }), ); }); it("maps interrupts, combo-point awards, and source-backed proc triggers", () => { expect(effectsOfKind(byId("wow335-rogue-kick"), "interrupt")).toEqual([ expect.objectContaining({ lockoutMs: 5_000 }), ]); expect(effectsOfKind(byId("wow335-rogue-sinister-strike"), "combo")).toEqual([ { kind: "combo", amount: 1 }, ]); const seal = effectsOfKind(byId("wow335-paladin-seal-of-justice"), "apply-aura") .find((effect) => effect.sourceAuraType === 42); expect(seal?.aura.procs).toEqual([ expect.objectContaining({ triggers: "hit", action: { kind: "custom", id: "classic-status", data: { status: "stun", durationMs: 2_000 }, }, }), ]); }); it("keeps fear as a named, dispellable control aura", () => { const fear = effectsOfKind(byId("wow335-warlock-fear"), "apply-aura") .find((effect) => effect.control?.kind === "fear"); expect(fear).toMatchObject({ control: { kind: "fear" }, aura: { disposition: "debuff", dispelCategory: "magic" }, }); }); });