Validate RoM primary five hybrid stats and pair mechanics
This commit is contained in:
1 parent
8e95480d4f
commit
c8b3a3d95a
18 files changed
+2061
-48
No files matched your search
@@ -0,0 +1,347 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { abilitiesForCharacter, learnedAbilityForBuild, type AbilityDefinition } from "./abilityCatalog";
|
||||
import { PLAYER_AGGRO_ID } from "./aggro";
|
||||
import { useCombatStore } from "./combatStore";
|
||||
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
|
||||
import { romScoutRogueCastEffects, romScoutRogueProcActionSpec } from "./romScoutRogueOverrides";
|
||||
|
||||
const scoutRogue = abilitiesForCharacter("rom-scout", "rom-rogue");
|
||||
const rogueWarrior = abilitiesForCharacter("rom-rogue", "rom-warrior");
|
||||
|
||||
function skill(abilities: readonly AbilityDefinition[], id: string): AbilityDefinition {
|
||||
const result = abilities.find((ability) => ability.id === id);
|
||||
if (!result) throw new Error(`Missing skill ${id}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
function modifiers(ability: AbilityDefinition) {
|
||||
return ability.effects.flatMap((effect) => effect.kind === "apply-aura" ? effect.aura.modifiers ?? [] : []);
|
||||
}
|
||||
|
||||
function workingPassive(ability: AbilityDefinition): boolean {
|
||||
return ability.effects.some((effect) => effect.kind === "apply-aura" && Boolean(
|
||||
effect.aura.modifiers?.length || effect.aura.procs?.length || effect.aura.utilityTags?.length,
|
||||
));
|
||||
}
|
||||
|
||||
function prime(primary: "rom-scout" | "rom-rogue", level: number): void {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: primary,
|
||||
secondaryClassId: primary === "rom-scout" ? "rom-rogue" : "rom-warrior",
|
||||
raceId: primary === "rom-scout" ? "rom-elf" : "rom-human",
|
||||
level,
|
||||
});
|
||||
for (const [id, x, z] of [["target", 3, 0], ["behind", 6, 0], ["nearby", 3, 3]] as const) {
|
||||
useCombatStore.getState().registerMob(id, { name: id, maxHealth: 1_000_000 });
|
||||
setMobPosition(id, [x, 0, z]);
|
||||
}
|
||||
useCombatStore.getState().selectMob("target");
|
||||
}
|
||||
|
||||
describe("accepted RoM Scout/Rogue and Rogue/Warrior pairings", () => {
|
||||
beforeEach(() => clearMobRuntimeRegistry());
|
||||
|
||||
it.each([
|
||||
["rom-scout", "rom-rogue", [491493, 491336, 492342, 491496, 492621, 492916, 493007, 493253, 499534, 499536]],
|
||||
["rom-rogue", "rom-warrior", [491526, 491527, 492349, 491339, 492625, 492920, 493017, 493257, 499476, 499480]],
|
||||
] as const)("learns all ten %s/%s source elites at their primary levels", (primary, secondary, ids) => {
|
||||
const elites = abilitiesForCharacter(primary, secondary).filter((ability) => ability.secondaryClassId);
|
||||
expect(elites.map((ability) => ability.dbcSpellId)).toEqual(ids);
|
||||
expect(elites.map((ability) => ability.unlockLevel)).toEqual([15, 20, 25, 30, 35, 40, 45, 50, 60, 70]);
|
||||
for (const [level, count] of [[10, 0], [30, 4], [60, 9], [79, 10]] as const) {
|
||||
expect(elites.filter((ability) => learnedAbilityForBuild(ability, level, {}) !== null), `level ${level}`).toHaveLength(count);
|
||||
}
|
||||
});
|
||||
|
||||
it.each([10, 30, 60, 79])("deals ordinary single-target damage with both pairings at level %i", (level) => {
|
||||
for (const [primary, id, pool] of [
|
||||
["rom-scout", "rom-scout-primary-1", "focus"],
|
||||
["rom-rogue", "rom-rogue-general-0", "energy"],
|
||||
] as const) {
|
||||
prime(primary, level);
|
||||
const before = useCombatStore.getState().mobs.target!.health;
|
||||
const resource = useCombatStore.getState().resourcePools[pool]!;
|
||||
expect(useCombatStore.getState().castAbility(id, undefined, 1_000), id).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().mobs.target!.health, id).toBeLessThan(before);
|
||||
expect(useCombatStore.getState().resourcePools[pool], id).toBeLessThan(resource);
|
||||
}
|
||||
});
|
||||
|
||||
it("runs Scout and Rogue offensive cooldowns and Rogue's defensive Evasion", () => {
|
||||
prime("rom-scout", 60);
|
||||
expect(useCombatStore.getState().castAbility("rom-scout-primary-0", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.definition.modifiers?.some((modifier) =>
|
||||
modifier.kind === "damage" && modifier.direction === "dealt" && modifier.attackKind === "ranged"))).toBe(true);
|
||||
prime("rom-rogue", 60);
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-primary-0", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.definition.modifiers?.some((modifier) =>
|
||||
modifier.kind === "stat" && modifier.stat === "physical-critical-strike"))).toBe(true);
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-primary-14", undefined, 2_600)).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.definition.modifiers?.some((modifier) =>
|
||||
modifier.kind === "stat" && modifier.stat === "dodge"))).toBe(true);
|
||||
});
|
||||
|
||||
it("Piercing Arrow hits a line without applying an unrelated focus drain (490400)", () => {
|
||||
prime("rom-scout", 60);
|
||||
const cast = useCombatStore.getState().castAbility("rom-scout-primary-5", undefined, 1_000);
|
||||
expect(cast).toMatchObject({ ok: true, affectedMobIds: ["target", "behind"] });
|
||||
expect(useCombatStore.getState().combatEvents.filter((event) => event.abilityId === "rom-scout-primary-5"
|
||||
&& event.kind === "damage" && event.rawAmount > 0).map((event) => event.targetActorId))
|
||||
.toEqual(["target", "behind"]);
|
||||
expect(modifiers(skill(scoutRogue, "rom-scout-primary-5")).some((modifier) =>
|
||||
modifier.kind === "resource" && modifier.resource === "focus")).toBe(false);
|
||||
});
|
||||
|
||||
it("Combo Shot hits three times and Reflected Shot has two weaker bounces (490424, 490457)", () => {
|
||||
expect(skill(scoutRogue, "rom-scout-primary-9").effects.filter((effect) => effect.kind === "damage")).toHaveLength(3);
|
||||
prime("rom-scout", 40);
|
||||
expect(useCombatStore.getState().castAbility("rom-scout-primary-11", undefined, 1_000))
|
||||
.toMatchObject({ ok: true, affectedMobIds: expect.arrayContaining(["target", "behind", "nearby"]) });
|
||||
const rawDamage = useCombatStore.getState().combatEvents.filter((event) => event.abilityId === "rom-scout-primary-11"
|
||||
&& event.kind === "damage").map((event) => event.rawAmount);
|
||||
expect(rawDamage).toHaveLength(3);
|
||||
expect(rawDamage[0]).toBeGreaterThan(rawDamage[1]!);
|
||||
expect(rawDamage[1]).toBeGreaterThan(rawDamage[2]!);
|
||||
});
|
||||
|
||||
it("Poisonous Spit removes bounce falloff and Master Reflected Shot adds two jumps (493253, 499534)", () => {
|
||||
prime("rom-scout", 60);
|
||||
setMobPosition("behind", [4, 0, 0]);
|
||||
setMobPosition("nearby", [5, 0, 0]);
|
||||
for (const [id, x, z] of [["fourth", 6, 0], ["fifth", 7, 0]] as const) {
|
||||
useCombatStore.getState().registerMob(id, { name: id, maxHealth: 1_000_000 });
|
||||
setMobPosition(id, [x, 0, z]);
|
||||
}
|
||||
const cast = useCombatStore.getState().castAbility("rom-scout-primary-11", undefined, 1_000);
|
||||
expect(cast).toMatchObject({ ok: true, affectedMobIds: expect.arrayContaining([
|
||||
"target", "behind", "nearby", "fourth", "fifth",
|
||||
]) });
|
||||
const damage = useCombatStore.getState().combatEvents.filter((event) => event.abilityId === "rom-scout-primary-11"
|
||||
&& event.kind === "damage").map((event) => event.rawAmount);
|
||||
expect(damage).toHaveLength(5);
|
||||
expect(new Set(damage).size).toBe(1);
|
||||
});
|
||||
|
||||
it("Master Reflected Shot applies a labeled 20% hybrid damage bonus", () => {
|
||||
const ability = skill(scoutRogue, "rom-scout-primary-11");
|
||||
const base = ability.effects.find((effect) => effect.kind === "damage");
|
||||
const mastered = romScoutRogueCastEffects(ability, ability.effects,
|
||||
["rom-poisonous-spit", "rom-master-reflected-shot"], [])
|
||||
.find((effect) => effect.kind === "damage");
|
||||
expect(base?.kind).toBe("damage");
|
||||
expect(mastered?.kind).toBe("damage");
|
||||
if (base?.kind !== "damage" || mastered?.kind !== "damage") return;
|
||||
expect(mastered.coefficient).toBeCloseTo(base.coefficient * 1.2);
|
||||
expect(mastered.falloffPerJump).toBe(1);
|
||||
});
|
||||
|
||||
it("proc resolver gates Shot crit vulnerability and preserves source 2% / 12s", () => {
|
||||
expect(romScoutRogueProcActionSpec("rom-exploiting-shot", 490400, false)).toBeNull();
|
||||
expect(romScoutRogueProcActionSpec("rom-exploiting-shot", 490423, false)).toMatchObject({
|
||||
kind: "aura", recipient: "target", aura: {
|
||||
durationMs: 12_000, modifiers: [
|
||||
{ kind: "damage", direction: "taken", school: "physical", operation: "percent", value: 0.02 },
|
||||
],
|
||||
},
|
||||
});
|
||||
const proc = skill(scoutRogue, "rom-scout-rom-rogue-elite-40").effects.find((effect) => effect.kind === "apply-aura");
|
||||
expect(proc?.kind === "apply-aura" ? proc.aura.procs?.[0]?.internalCooldownMs : null).toBe(20_000);
|
||||
});
|
||||
|
||||
it("proc resolver gates Piercing Arrow critical buff to its own hit", () => {
|
||||
expect(romScoutRogueProcActionSpec("rom-deadly-counterattack", 490423, false)).toBeNull();
|
||||
expect(romScoutRogueProcActionSpec("rom-deadly-counterattack", 490400, false)).toMatchObject({
|
||||
kind: "aura", recipient: "caster", provenance: "hybrid-estimate", aura: {
|
||||
durationMs: 5_000,
|
||||
modifiers: [{ kind: "stat", stat: "physical-critical-strike", operation: "flat", value: 20 }],
|
||||
},
|
||||
});
|
||||
const proc = skill(scoutRogue, "rom-scout-rom-rogue-elite-45").effects.find((effect) => effect.kind === "apply-aura");
|
||||
expect(proc?.kind === "apply-aura" ? proc.aura.procs?.[0]?.internalCooldownMs : null).toBe(20_000);
|
||||
});
|
||||
|
||||
it("proc resolver poisons only Reflected or Combo Shot while carrying Poison Powder", () => {
|
||||
for (const sourceId of [490457, 490424]) {
|
||||
expect(romScoutRogueProcActionSpec("rom-poisonous-spit", sourceId, false)).toBeNull();
|
||||
expect(romScoutRogueProcActionSpec("rom-poisonous-spit", sourceId, true)).toMatchObject({
|
||||
kind: "dot", recipient: "target", provenance: "hybrid-estimate",
|
||||
effect: { kind: "dot", ticks: 5, intervalMs: 2_000 },
|
||||
});
|
||||
}
|
||||
expect(romScoutRogueProcActionSpec("rom-poisonous-spit", 490423, true)).toBeNull();
|
||||
const proc = skill(scoutRogue, "rom-scout-rom-rogue-elite-50").effects.find((effect) => effect.kind === "apply-aura");
|
||||
expect(proc?.kind === "apply-aura" ? proc.aura.procs?.[0]?.internalCooldownMs : null).toBe(20_000);
|
||||
});
|
||||
|
||||
it("executes the Scout/Rogue poison rider on a successful reflected hit", () => {
|
||||
prime("rom-scout", 60);
|
||||
useCombatStore.getState().synchronizeActors();
|
||||
const poison = skill(rogueWarrior, "rom-rogue-primary-6");
|
||||
const state = poison.effects
|
||||
.filter((effect): effect is Extract<AbilityDefinition["effects"][number], { kind: "apply-aura" }> => effect.kind === "apply-aura")
|
||||
.find((effect) => effect.aura.utilityTags?.includes("rom-poisonous") === true);
|
||||
expect(state).toBeDefined();
|
||||
expect(useCombatStore.getState().applyActorAura(
|
||||
PLAYER_AGGRO_ID, PLAYER_AGGRO_ID, poison, state!, 1_000,
|
||||
)).toBe(true);
|
||||
expect(useCombatStore.getState().castAbility("rom-scout-primary-11", undefined, 2_600))
|
||||
.toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().effects.some((effect) => effect.tag === "rom-poisonous-spit"
|
||||
&& effect.targetId === "target")).toBe(true);
|
||||
});
|
||||
|
||||
it("proc resolver routes Reclaim Energy by trigger skill", () => {
|
||||
expect(romScoutRogueProcActionSpec("rom-reclaim-energy", 490306, false)).toMatchObject({
|
||||
kind: "resource", resource: "energy", amount: 15, provenance: "hybrid-estimate",
|
||||
});
|
||||
expect(romScoutRogueProcActionSpec("rom-reclaim-energy", 492625, true)).toMatchObject({
|
||||
kind: "resource", resource: "rage", amount: 10, provenance: "hybrid-estimate",
|
||||
});
|
||||
expect(romScoutRogueProcActionSpec("rom-reclaim-energy", 490309, false)).toBeNull();
|
||||
const proc = skill(rogueWarrior, "rom-rogue-rom-warrior-elite-50").effects.find((effect) => effect.kind === "apply-aura");
|
||||
expect(proc?.kind === "apply-aura" ? proc.aura.procs?.[0]?.chance : null).toBe(0.25);
|
||||
});
|
||||
|
||||
it("keeps absent 499477/499481 replacement-skill procs explicitly unresolved", () => {
|
||||
expect(romScoutRogueProcActionSpec("rom-toxic-splash", 499477, true)).toBeNull();
|
||||
expect(romScoutRogueProcActionSpec("rom-poison-battle-technique", 499481, true)).toBeNull();
|
||||
});
|
||||
|
||||
it("Rogue Combo Throw has two hits and Poison grants a proc state (490338, 490329)", () => {
|
||||
expect(skill(rogueWarrior, "rom-rogue-primary-15").effects.filter((effect) => effect.kind === "damage")).toHaveLength(2);
|
||||
prime("rom-rogue", 60);
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-primary-6", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
useCombatStore.getState().tick(0.1, 2_100);
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.definition.utilityTags?.includes("rom-poisonous")
|
||||
&& Boolean(aura.definition.procs?.length))).toBe(true);
|
||||
});
|
||||
|
||||
it("Rogue Vanish drops combat without performing an unrelated heal (490354)", () => {
|
||||
prime("rom-rogue", 60);
|
||||
useCombatStore.getState().damagePlayer(100);
|
||||
const health = useCombatStore.getState().health;
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-primary-11", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().health).toBe(health);
|
||||
expect(skill(rogueWarrior, "rom-rogue-primary-11").effects.some((effect) => effect.kind === "heal")).toBe(false);
|
||||
});
|
||||
|
||||
it("Weak Spot has one hit and a timed critical buff (491493)", () => {
|
||||
const ability = skill(scoutRogue, "rom-scout-rom-rogue-elite-15");
|
||||
expect(ability.effects.filter((effect) => effect.kind === "damage")).toHaveLength(1);
|
||||
expect(ability.effects.some((effect) => effect.kind === "apply-aura" && effect.recipient === "caster"
|
||||
&& effect.aura.durationMs !== null && effect.aura.durationMs > 0
|
||||
&& effect.aura.modifiers?.some((modifier) => modifier.kind === "stat" && modifier.stat === "physical-critical-strike"))).toBe(true);
|
||||
});
|
||||
|
||||
it("Blinding Powder control breaks on damage (491336)", () => {
|
||||
prime("rom-scout", 30);
|
||||
expect(useCombatStore.getState().castAbility("rom-scout-rom-rogue-elite-20", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().mobs.target!.statuses.some((status) => status.kind === "blind")).toBe(true);
|
||||
useCombatStore.getState().damageMob("target", 1, 1_100);
|
||||
expect(useCombatStore.getState().mobs.target!.statuses.some((status) => status.kind === "blind")).toBe(false);
|
||||
});
|
||||
|
||||
it("Lasso Master prevents movement and attacks (492342)", () => {
|
||||
prime("rom-scout", 30);
|
||||
expect(useCombatStore.getState().castAbility("rom-scout-primary-4", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
useCombatStore.getState().tick(0.1, 2_100);
|
||||
expect(useCombatStore.getState().mobs.target!.statuses.map((status) => status.kind))
|
||||
.toEqual(expect.arrayContaining(["root", "disarm"]));
|
||||
});
|
||||
|
||||
it("Sapping Arrow lowers physical and magical defense (491496)", () => {
|
||||
const values = modifiers(skill(scoutRogue, "rom-scout-rom-rogue-elite-30"));
|
||||
expect(values.some((modifier) => modifier.kind === "stat" && modifier.stat === "armor" && modifier.value < 0)).toBe(true);
|
||||
expect(values.some((modifier) => modifier.kind === "stat" && modifier.stat === "magical-defense" && modifier.value < 0)).toBe(true);
|
||||
});
|
||||
|
||||
it("Deadly Poison Bite has one base hit and a conditional bonus (492621)", () => {
|
||||
prime("rom-scout", 60);
|
||||
expect(useCombatStore.getState().castAbility("rom-scout-rom-rogue-elite-35", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
useCombatStore.getState().tick(0.1, 2_100);
|
||||
expect(useCombatStore.getState().combatEvents.filter((event) => event.abilityId === "rom-scout-rom-rogue-elite-35"
|
||||
&& event.kind === "damage" && event.targetActorId === "target")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["rom-scout-rom-rogue-elite-40", 492916],
|
||||
["rom-scout-rom-rogue-elite-45", 493007],
|
||||
] as const)("%s uses an attack-triggered passive (source %i)", (id, sourceId) => {
|
||||
const ability = skill(scoutRogue, id);
|
||||
expect(ability.dbcSpellId).toBe(sourceId);
|
||||
expect(ability.effects.some((effect) => effect.kind === "apply-aura" && Boolean(effect.aura.procs?.length))).toBe(true);
|
||||
expect(ability.effects.some((effect) => effect.kind === "apply-aura" && effect.recipient === "caster"
|
||||
&& effect.aura.modifiers?.some((modifier) => modifier.kind === "damage" && modifier.direction === "taken"))).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["rom-scout-rom-rogue-elite-50", 493253],
|
||||
["rom-scout-rom-rogue-elite-60", 499534],
|
||||
["rom-scout-rom-rogue-elite-70", 499536],
|
||||
] as const)("%s has executable cross-skill effects (source %i)", (id, sourceId) => {
|
||||
const ability = skill(scoutRogue, id);
|
||||
expect(ability.dbcSpellId).toBe(sourceId);
|
||||
expect(workingPassive(ability)).toBe(true);
|
||||
});
|
||||
|
||||
it("Throwing Mastery gives Throw a five-second slow (491526)", () => {
|
||||
prime("rom-rogue", 30);
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-general-1", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().mobs.target!.statuses.some((status) => status.kind === "slow"
|
||||
&& status.endsAt >= 6_000)).toBe(true);
|
||||
});
|
||||
|
||||
it("One-Hand Axe Mastery does not boost dagger damage (491527)", () => {
|
||||
const ability = skill(rogueWarrior, "rom-rogue-rom-warrior-elite-20");
|
||||
expect(ability.effects.some((effect) => effect.kind === "apply-aura" && effect.aura.modifiers?.some((modifier) =>
|
||||
modifier.kind === "damage" && modifier.attackKind === "melee"))).toBe(false);
|
||||
});
|
||||
|
||||
it("Whirlwind Mastery lowers rage cost and cooldown (492349)", () => {
|
||||
prime("rom-rogue", 30);
|
||||
useCombatStore.setState((state) => ({ resourcePools: { ...state.resourcePools, rage: 40 } }));
|
||||
expect(useCombatStore.getState().castAbility("rom-warrior-general-2", undefined, 1_000)).toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().resourcePools.rage).toBe(10);
|
||||
expect(useCombatStore.getState().cooldowns["rom-warrior-general-2"]).toBeLessThanOrEqual(11_000);
|
||||
});
|
||||
|
||||
it("Death's Touch damages and reduces received healing (491339)", () => {
|
||||
const ability = skill(rogueWarrior, "rom-rogue-rom-warrior-elite-30");
|
||||
expect(ability.effects.some((effect) => effect.kind === "damage")).toBe(true);
|
||||
expect(modifiers(ability).some((modifier) => modifier.kind === "healing"
|
||||
&& modifier.direction === "taken" && modifier.value < 0)).toBe(true);
|
||||
});
|
||||
|
||||
it("Poisonous Infection damages and poisons without making the victim immune (492625)", () => {
|
||||
const ability = skill(rogueWarrior, "rom-rogue-rom-warrior-elite-35");
|
||||
expect(ability.effects.some((effect) => effect.kind === "damage")).toBe(true);
|
||||
expect(ability.effects.some((effect) => effect.kind === "dot")).toBe(true);
|
||||
expect(modifiers(ability).some((modifier) => modifier.kind === "damage" && modifier.direction === "taken"
|
||||
&& modifier.school === "physical" && modifier.value <= -1)).toBe(false);
|
||||
});
|
||||
|
||||
it("Poison Spray spreads poison and damage to nearby enemies (492920)", () => {
|
||||
const ability = skill(rogueWarrior, "rom-rogue-rom-warrior-elite-40");
|
||||
expect(ability.radius).toBeGreaterThan(0);
|
||||
expect(ability.effects.some((effect) => effect.kind === "damage")).toBe(true);
|
||||
expect(ability.effects.some((effect) => effect.kind === "dot" || effect.kind === "apply-aura")).toBe(true);
|
||||
});
|
||||
|
||||
it("Decay requires Poisonous and grants timed critical power (493017)", () => {
|
||||
prime("rom-rogue", 60);
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-rom-warrior-elite-45", undefined, 1_000))
|
||||
.toMatchObject({ ok: false, reason: "condition-not-met" });
|
||||
expect(modifiers(skill(rogueWarrior, "rom-rogue-rom-warrior-elite-45")).some((modifier) =>
|
||||
modifier.kind === "stat" && modifier.stat === "physical-critical-strike")).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["rom-rogue-rom-warrior-elite-50", 493257],
|
||||
["rom-rogue-rom-warrior-elite-60", 499476],
|
||||
["rom-rogue-rom-warrior-elite-70", 499480],
|
||||
] as const)("%s has executable triggered effects (source %i)", (id, sourceId) => {
|
||||
const ability = skill(rogueWarrior, id);
|
||||
expect(ability.dbcSpellId).toBe(sourceId);
|
||||
expect(workingPassive(ability)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user