import { describe, expect, it } from "vitest"; import { applyAura, resolveDamageValue, resolveStatValue } from "./combatAuras"; import { WOW335_WARLOCK_METAMORPHOSIS_AURA_TAG, WOW335_WARLOCK_METAMORPHOSIS_FORM_ID, WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS, warlockMetamorphosisCanCastAbilityWhileActive, warlockMetamorphosisAbilityEligible, warlockMetamorphosisFormActionReady, wow335WarlockImmolationAttackPowerAura, wow335WarlockImmolationAttackPowerReduction, wow335WarlockMetamorphosisAuraForRank, wow335WarlockMetamorphosisControlDuration, wow335WarlockMetamorphosisEffectsForRank, } from "./wow335WarlockMetamorphosisRuntime"; describe("WoW Warlock Metamorphosis profile", () => { it("uses the generated 3.3.5a rank values and leaves unlearned ranks inactive", () => { expect(wow335WarlockMetamorphosisEffectsForRank(0)).toBeNull(); expect(wow335WarlockMetamorphosisEffectsForRank(1)).toEqual({ rank: 1, durationMs: 30_000, cooldownMs: 180_000, armorContributionPercent: 130, damagePercent: 15, meleeCritChanceTakenReductionPercent: 6, stunSnareDurationReductionPercent: 25, allowedSchools: ["fire", "shadow"], pvpDamageBonusPercent: null, }); expect(wow335WarlockMetamorphosisEffectsForRank(Number.NaN)).toBeNull(); }); it("allows only Fire/Shadow or explicitly shapeshift-usable abilities while active", () => { expect(warlockMetamorphosisAbilityEligible(false, {})).toBe(true); expect(warlockMetamorphosisAbilityEligible(true, { school: "fire" })).toBe(true); expect(warlockMetamorphosisAbilityEligible(true, { school: "shadow" })).toBe(true); expect(warlockMetamorphosisAbilityEligible(true, { schools: ["holy", "shadow"] })).toBe(true); expect(warlockMetamorphosisAbilityEligible(true, { school: "arcane", usableWhileShapeshifted: true, })).toBe(true); expect(warlockMetamorphosisAbilityEligible(true, { school: "arcane" })).toBe(false); expect(warlockMetamorphosisAbilityEligible(true, { school: "physical" })).toBe(false); expect(warlockMetamorphosisAbilityEligible(true, {})).toBe(false); expect(warlockMetamorphosisCanCastAbilityWhileActive({ id: "physical-strike", description: "Strikes for physical damage.", effects: [{ kind: "damage", coefficient: 1, school: "physical" }], } as Parameters[0])).toBe(false); expect(warlockMetamorphosisCanCastAbilityWhileActive({ id: "explicit-shadow", description: "Deals Shadow damage.", effects: [{ kind: "damage", coefficient: 1 }], } as Parameters[0])).toBe(true); expect(warlockMetamorphosisCanCastAbilityWhileActive({ id: "physical-melee", description: "Usable while shapeshifted.", effects: [{ kind: "damage", coefficient: 1, school: "physical" }], } as Parameters[0])).toBe(true); expect(warlockMetamorphosisFormActionReady(WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[0], false)).toBe(false); expect(warlockMetamorphosisFormActionReady(WOW335_WARLOCK_METAMORPHOSIS_FORM_ABILITY_IDS[0], true)).toBe(true); }); it("creates a timed source-scoped aura with only supported armor and damage modifiers", () => { const definition = wow335WarlockMetamorphosisAuraForRank(1); expect(definition).toMatchObject({ id: "wow335:warlock:metamorphosis", durationMs: 30_000, uniqueBySource: true, utilityTags: ["shapeshift", WOW335_WARLOCK_METAMORPHOSIS_AURA_TAG], }); expect(wow335WarlockMetamorphosisAuraForRank(0)).toBeNull(); expect(WOW335_WARLOCK_METAMORPHOSIS_FORM_ID).toBe(22); const active = applyAura([], { definition: definition!, sourceId: "warlock-a", targetId: "warlock-a", now: 10_000, }).aura; expect(active.expiresAt).toBe(40_000); expect(resolveStatValue(100, [active], "armor")).toBeCloseTo(230); expect(resolveDamageValue(100, [active], { direction: "dealt", school: "shadow", attackKind: "spell", })).toBeCloseTo(115); expect(resolveDamageValue(100, [active], { direction: "dealt", school: "physical", attackKind: "melee", })).toBeCloseTo(115); expect(definition?.modifiers).toHaveLength(3); expect(resolveStatValue(100, [active], "incoming-melee-critical-strike")).toBe(94); }); it("keeps two Warlocks' form auras source-scoped", () => { const definition = wow335WarlockMetamorphosisAuraForRank(1)!; const first = applyAura([], { definition, sourceId: "warlock-a", targetId: "warlock-a", now: 1_000, }); const second = applyAura(first.auras, { definition, sourceId: "warlock-b", targetId: "warlock-b", now: 1_500, }); expect(second.auras).toHaveLength(2); expect(second.auras.map((aura) => aura.sourceId)).toEqual(["warlock-a", "warlock-b"]); }); it("uses the Immolation AP formula and reduces only sourced stun, root, and slow durations", () => { expect(wow335WarlockImmolationAttackPowerReduction(1)).toBe(2); expect(wow335WarlockImmolationAttackPowerReduction(80)).toBe(357); expect(wow335WarlockImmolationAttackPowerAura(80).modifiers).toEqual([ { kind: "stat", stat: "attack-power", operation: "flat", value: -357 }, ]); for (const mechanic of ["stun", "root", "slow"]) { expect(wow335WarlockMetamorphosisControlDuration(10_000, mechanic, true)).toBe(7_500); } expect(wow335WarlockMetamorphosisControlDuration(10_000, "fear", true)).toBe(10_000); expect(wow335WarlockMetamorphosisControlDuration(10_000, "stun", false)).toBe(10_000); }); });