Files
healer-man/src/game/wow335ShamanHealingStreamRuntime.test.ts
T
2026-09-28 22:46:45 -04:00

85 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { beforeEach, describe, expect, it } from "vitest";
import { abilitiesForClass, abilityAtLevel } from "./abilityCatalog";
import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry";
import { useCombatStore } from "./combatStore";
import { clearPartyRuntimeRegistry, registerPartyRuntimePosition } from "./partyRuntimeRegistry";
import { usePartyStore } from "./partyStore";
const HEALING_STREAM_RANKS = [
{ level: 20, spellId: 5394, heal: 6 },
{ level: 30, spellId: 6375, heal: 8 },
{ level: 40, spellId: 6377, heal: 10 },
{ level: 50, spellId: 10462, heal: 12 },
{ level: 60, spellId: 10463, heal: 14 },
{ level: 69, spellId: 25567, heal: 18 },
{ level: 71, spellId: 58755, heal: 20 },
{ level: 76, spellId: 58756, heal: 23 },
{ level: 80, spellId: 58757, heal: 25 },
] as const;
function healingStreamAbility() {
const ability = abilitiesForClass("shaman").find((candidate) => candidate.name === "Healing Stream Totem");
expect(ability, "Healing Stream Totem").toBeDefined();
return ability!;
}
describe("WoW Shaman Healing Stream Totem", () => {
beforeEach(() => {
clearMobRuntimeRegistry();
clearPartyRuntimeRegistry();
useCombatStore.getState().initializeCharacter({ classId: "shaman", level: 80, xp: 0 });
useCombatStore.getState().setPlayerPosition([0, 0, 0]);
usePartyStore.getState().initializeParty("wow-healing-stream-test", 80, useCombatStore.getState().health);
});
it("uses each source rank’s 2-second heal amount", () => {
const base = healingStreamAbility();
for (const expected of HEALING_STREAM_RANKS) {
const ranked = abilityAtLevel(base, expected.level);
const summon = ranked.effects.find((effect) => effect.kind === "summon");
expect(ranked.dbcSpellId).toBe(expected.spellId);
expect(summon?.kind).toBe("summon");
if (summon?.kind !== "summon") continue;
expect(summon.healingPulse).toEqual({ amount: expected.heal, intervalMs: 2_000, radius: 10 });
expect(summon.durationMs).toBe(300_000);
}
});
it("heals every nearby party member and never attacks the selected enemy", () => {
const state = useCombatStore.getState();
const members = usePartyStore.getState().members;
useCombatStore.setState((current) => ({ health: current.maxHealth - 25 }));
usePartyStore.setState((current) => ({
members: current.members.map((member) => ({ ...member, health: member.maxHealth - 25 })),
}));
members.forEach((member, index) => {
registerPartyRuntimePosition(member.id, index === 0 ? [5, 0, 0] : index === 1 ? [9, 0, 0] : [10.1 + index, 0, 0]);
});
useCombatStore.getState().registerMob("healing-stream-nearby-enemy", { name: "Nearby enemy", maxHealth: 10_000 });
setMobPosition("healing-stream-nearby-enemy", [4, 0, 0]);
useCombatStore.getState().selectMob("healing-stream-nearby-enemy");
const cast = useCombatStore.getState().castAbility(healingStreamAbility().id, undefined, 1_000);
expect(cast.ok, JSON.stringify(cast)).toBe(true);
const totem = useCombatStore.getState().summons.find((summon) => (
summon.sourceAbilityId === healingStreamAbility().id
));
expect(totem).toBeDefined();
expect(totem?.kind).toBe("totem");
expect(totem?.maxHealth).toBe(5);
expect(totem?.attackDamage).toBe(0);
expect(totem?.expiresAt).toBe(301_000);
useCombatStore.getState().tick(0, 3_000);
const after = useCombatStore.getState();
expect(after.health).toBe(after.maxHealth);
expect(usePartyStore.getState().members[0]?.health).toBe(usePartyStore.getState().members[0]?.maxHealth);
expect(usePartyStore.getState().members[1]?.health).toBe(usePartyStore.getState().members[1]?.maxHealth);
expect(usePartyStore.getState().members[2]?.health).toBe((usePartyStore.getState().members[2]?.maxHealth ?? 0) - 25);
expect(after.mobs["healing-stream-nearby-enemy"]?.health).toBe(10_000);
expect(after.combatEvents.filter((event) => event.kind === "healing"
&& event.abilityId === healingStreamAbility().id)).toHaveLength(3);
});
});