466 lines
21 KiB
TypeScript
466 lines
21 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { abilityAtLevel, type AbilityDefinition, type AbilityEffect } from "./abilityCatalog";
|
|
import { aiHintsForAbility, targetingRulesForAbility } from "./abilityRules";
|
|
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<K extends AbilityEffect["kind"]>(
|
|
ability: Pick<AbilityDefinition, "effects">,
|
|
kind: K,
|
|
): readonly Extract<AbilityEffect, { kind: K }>[] {
|
|
return ability.effects.filter(
|
|
(effect): effect is Extract<AbilityEffect, { kind: K }> => effect.kind === kind,
|
|
);
|
|
}
|
|
|
|
describe("WoW 3.3.5 executable effect mapping", () => {
|
|
it("preserves source spells that do not trigger a global cooldown", () => {
|
|
expect(byId("wow335-priest-inner-focus").gcdMs).toBe(0);
|
|
expect(byId("wow335-rogue-cold-blood").gcdMs).toBe(0);
|
|
expect(byId("wow335-warrior-shield-block").gcdMs).toBe(0);
|
|
expect(byId("wow335-priest-power-word-shield").gcdMs).toBe(1_500);
|
|
});
|
|
|
|
it("combines each Holy Nova damage rank with its matching healing payload", () => {
|
|
const nova = byId("wow335-priest-holy-nova");
|
|
expect(nova.ranks).toHaveLength(9);
|
|
for (const rank of nova.ranks ?? []) {
|
|
expect(effectsOfKind(rank, "damage")).toHaveLength(1);
|
|
expect(effectsOfKind(rank, "heal")).toHaveLength(1);
|
|
expect(rank.cost.amount).toBeGreaterThan(0);
|
|
}
|
|
expect(abilityAtLevel(nova, 80)).toMatchObject({
|
|
dbcSpellId: 48078,
|
|
target: "self",
|
|
radius: 10,
|
|
castMode: "instant",
|
|
});
|
|
});
|
|
|
|
it("maps Envenom to Deadly Poison dose damage and its poison-frequency buff", () => {
|
|
const envenom = abilityAtLevel(byId("wow335-rogue-envenom"), 80);
|
|
expect(effectsOfKind(envenom, "finisher-damage")).toEqual([
|
|
expect.objectContaining({
|
|
poison: expect.objectContaining({
|
|
maximumDoses: 5,
|
|
basePointsPerDose: 216,
|
|
attackPowerCoefficientPerDose: 0.09,
|
|
}),
|
|
}),
|
|
]);
|
|
expect(effectsOfKind(envenom, "apply-aura")).toEqual([
|
|
expect.objectContaining({
|
|
recipient: "caster",
|
|
aura: expect.objectContaining({
|
|
utilityTags: expect.arrayContaining(["rogue-envenom-poison-buff"]),
|
|
}),
|
|
}),
|
|
]);
|
|
});
|
|
|
|
it("maps the Ready DPS area actions to executable multi-target shapes", () => {
|
|
const multiShot = abilityAtLevel(byId("wow335-hunter-multi-shot"), 80);
|
|
expect(targetingRulesForAbility(multiShot)).toMatchObject({
|
|
shape: "chain",
|
|
maximumTargets: 3,
|
|
jumpRange: 10,
|
|
});
|
|
expect(aiHintsForAbility(multiShot).categories).toContain("area-damage");
|
|
|
|
const volley = abilityAtLevel(byId("wow335-hunter-volley"), 80);
|
|
expect(volley).toMatchObject({ target: "hostile", castMode: "channel", castTimeMs: 6_000, radius: 8 });
|
|
expect(targetingRulesForAbility(volley).shape).toBe("ground");
|
|
expect(effectsOfKind(volley, "dot")).toEqual([
|
|
expect.objectContaining({ ticks: 6, intervalMs: 1_000, tag: "volley" }),
|
|
]);
|
|
|
|
const fan = abilityAtLevel(byId("wow335-rogue-fan-of-knives"), 80);
|
|
expect(fan).toMatchObject({ target: "self", radius: 8 });
|
|
expect(targetingRulesForAbility(fan).shape).toBe("circle");
|
|
expect(aiHintsForAbility(fan).categories).toContain("area-damage");
|
|
});
|
|
|
|
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 three healing or damaging pulses", () => {
|
|
const penance = byId("wow335-priest-penance");
|
|
expect(penance.target).toBe("any");
|
|
expect(effectsOfKind(penance, "heal")).toEqual([
|
|
expect.objectContaining({ coefficient: 0.6 }),
|
|
]);
|
|
expect(effectsOfKind(penance, "hot")).toEqual([
|
|
expect.objectContaining({ coefficient: 0.6, ticks: 2, intervalMs: 1_000 }),
|
|
]);
|
|
expect(effectsOfKind(penance, "damage")).toEqual([
|
|
expect.objectContaining({ coefficient: 0.6 }),
|
|
]);
|
|
expect(effectsOfKind(penance, "dot")).toEqual([
|
|
expect.objectContaining({ coefficient: 0.6, ticks: 2, intervalMs: 1_000 }),
|
|
]);
|
|
});
|
|
|
|
it("maps the Ready-five missing spell payloads without cast-time damage on self buffs", () => {
|
|
const devastate = abilityAtLevel(byId("wow335-warrior-devastate"), 80);
|
|
expect(effectsOfKind(devastate, "damage")).toEqual([
|
|
expect.objectContaining({ coefficient: 1, bonusPerSunderStack: 202 }),
|
|
]);
|
|
expect(effectsOfKind(devastate, "apply-aura")).toEqual([
|
|
expect.objectContaining({ aura: expect.objectContaining({ id: "wow335:warrior:sunder-armor", maxStacks: 5 }) }),
|
|
]);
|
|
const lastStand = byId("wow335-warrior-last-stand");
|
|
expect(lastStand.target).toBe("self");
|
|
expect(effectsOfKind(lastStand, "heal")).toEqual([]);
|
|
expect(effectsOfKind(lastStand, "apply-aura")).toEqual([
|
|
expect.objectContaining({ aura: expect.objectContaining({
|
|
modifiers: [{ kind: "stat", stat: "maximum-health", operation: "percent", value: 0.3 }],
|
|
}) }),
|
|
]);
|
|
expect(effectsOfKind(byId("wow335-priest-divine-hymn"), "heal")).toEqual([
|
|
expect.objectContaining({ scope: "party", maximumTargets: 3, radius: 40 }),
|
|
]);
|
|
expect(effectsOfKind(byId("wow335-hunter-chimera-shot"), "damage")).toEqual([
|
|
expect.objectContaining({ coefficient: 1.25 }),
|
|
]);
|
|
expect(effectsOfKind(abilityAtLevel(byId("wow335-mage-living-bomb"), 80), "dot")).toHaveLength(1);
|
|
const moltenArmor = abilityAtLevel(byId("wow335-mage-molten-armor"), 80);
|
|
expect(moltenArmor.target).toBe("self");
|
|
expect(effectsOfKind(moltenArmor, "damage")).toEqual([]);
|
|
expect(effectsOfKind(byId("wow335-rogue-shiv"), "combo")).toEqual([{ kind: "combo", amount: 1 }]);
|
|
expect(effectsOfKind(byId("wow335-rogue-cold-blood"), "apply-aura")).toEqual([
|
|
expect.objectContaining({ aura: expect.objectContaining({
|
|
utilityTags: ["next-offensive-critical"],
|
|
modifiers: [{ kind: "stat", stat: "critical-strike", operation: "flat", value: 100 }],
|
|
}) }),
|
|
]);
|
|
});
|
|
|
|
it("maps the remaining Ready-five cooldowns and defensive modifiers", () => {
|
|
expect(effectsOfKind(byId("wow335-warrior-last-stand"), "apply-aura")[0]?.aura.durationMs).toBe(20_000);
|
|
expect(byId("wow335-warrior-enraged-regeneration").target).toBe("self");
|
|
const innerFire = abilityAtLevel(byId("wow335-priest-inner-fire"), 80);
|
|
expect(effectsOfKind(innerFire, "apply-aura")[0]?.aura).toMatchObject({
|
|
maxStacks: 20, initialStacks: 20,
|
|
modifiers: [{ kind: "stat", stat: "armor", operation: "flat", value: 2440, perStack: false }],
|
|
});
|
|
const shadowfiend = byId("wow335-priest-shadowfiend");
|
|
expect(effectsOfKind(shadowfiend, "summon")).toHaveLength(1);
|
|
expect(effectsOfKind(shadowfiend, "resource")).toHaveLength(0);
|
|
expect(effectsOfKind(abilityAtLevel(byId("wow335-hunter-kill-shot"), 80), "damage"))
|
|
.toEqual([{ kind: "damage", coefficient: 2, bonusFlat: 325 }]);
|
|
const combustion = byId("wow335-mage-combustion");
|
|
expect(combustion.target).toBe("self");
|
|
expect(effectsOfKind(combustion, "damage")).toHaveLength(0);
|
|
expect(effectsOfKind(combustion, "apply-aura")[0]?.aura.utilityTags).toContain("fire-crit-stacking");
|
|
expect(effectsOfKind(byId("wow335-mage-mirror-image"), "summon")).toHaveLength(3);
|
|
const molten = abilityAtLevel(byId("wow335-mage-molten-armor"), 80);
|
|
expect(effectsOfKind(molten, "apply-aura").flatMap((effect) => effect.aura.modifiers ?? []))
|
|
.toEqual(expect.arrayContaining([
|
|
expect.objectContaining({ stat: "incoming-critical-strike", value: -5 }),
|
|
expect.objectContaining({ stat: "spirit-to-spell-crit-rating", value: 0.35 }),
|
|
]));
|
|
expect(effectsOfKind(abilityAtLevel(byId("wow335-rogue-evasion"), 80), "apply-aura")
|
|
.flatMap((effect) => effect.aura.modifiers ?? []))
|
|
.toEqual(expect.arrayContaining([expect.objectContaining({ stat: "incoming-ranged-hit", value: -25 })]));
|
|
});
|
|
|
|
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" },
|
|
});
|
|
});
|
|
});
|