import { describe, expect, it } from "vitest"; import type { AbilityDefinition } from "./abilityCatalog"; import { isRomPriestScoutCuringShot, ROM_PRIEST_SCOUT_CURING_SHOT_COOLDOWN_MS, ROM_PRIEST_SCOUT_CURING_SHOT_DESCRIPTION, ROM_PRIEST_SCOUT_CURING_SHOT_DURATION_MS, ROM_PRIEST_SCOUT_CURING_SHOT_ELEMENTAL_AURA, ROM_PRIEST_SCOUT_CURING_SHOT_ELEMENTAL_DAMAGE_BONUS, ROM_PRIEST_SCOUT_CURING_SHOT_ELEMENTAL_SCHOOLS, ROM_PRIEST_SCOUT_CURING_SHOT_HOT_TICKS, ROM_PRIEST_SCOUT_CURING_SHOT_ID, ROM_PRIEST_SCOUT_CURING_SHOT_IMMEDIATE_HEAL, ROM_PRIEST_SCOUT_CURING_SHOT_RADIUS, ROM_PRIEST_SCOUT_CURING_SHOT_RANGE, ROM_PRIEST_SCOUT_CURING_SHOT_TICK_INTERVAL_MS, romPriestScoutCuringShotCatalogOverride, } from "./romPriestScoutCuringShot"; const localAbility: AbilityDefinition = { id: "rom-priest-rom-scout-elite-35", classId: "rom-priest", secondaryClassId: "rom-scout", dbcSpellId: ROM_PRIEST_SCOUT_CURING_SHOT_ID, name: "Curing Shot", unlockLevel: 35, icon: "/assets/ui/rom-spells/577011.png", sigil: "CS", description: "Local two-second heal row.", target: "friendly", range: { min: 0, max: 20 }, radius: 20, targeting: { shape: "circle", radius: 20, maximumTargets: 5 }, castMode: "cast", castTimeMs: 2_000, cooldownMs: 12_000, gcdMs: 1_500, cost: { resource: "mana", amount: 120 }, costs: [{ resource: "mana", amount: 120 }], effects: [{ kind: "damage", coefficient: 0.2 }], source: "runewaker", romSkillGroup: "elite", ranks: [{ rank: 1, spellId: ROM_PRIEST_SCOUT_CURING_SHOT_ID, level: 35, description: "Local two-second heal row.", castMode: "cast", castTimeMs: 2_000, cooldownMs: 12_000, gcdMs: 1_500, cost: { resource: "mana", amount: 120 }, costs: [{ resource: "mana", amount: 120 }], effects: [{ kind: "damage", coefficient: 0.2 }], }], }; describe("current CoA Priest/Scout Curing Shot catalog overlay", () => { it("uses current CoA cast, cooldown, radius, direct heal, HoT, and elemental-buff metadata", () => { const ability = romPriestScoutCuringShotCatalogOverride(localAbility); expect(ability).toMatchObject({ name: "Curing Shot", target: "friendly", range: { min: 0, max: ROM_PRIEST_SCOUT_CURING_SHOT_RANGE }, radius: ROM_PRIEST_SCOUT_CURING_SHOT_RADIUS, targeting: { shape: "circle", radius: ROM_PRIEST_SCOUT_CURING_SHOT_RADIUS }, castMode: "instant", castTimeMs: 0, cooldownMs: ROM_PRIEST_SCOUT_CURING_SHOT_COOLDOWN_MS, description: ROM_PRIEST_SCOUT_CURING_SHOT_DESCRIPTION, passive: false, }); expect(ability.effects).toEqual([ { kind: "heal", coefficient: 0, basePoints: ROM_PRIEST_SCOUT_CURING_SHOT_IMMEDIATE_HEAL, recipient: "caster", scope: "party", radius: ROM_PRIEST_SCOUT_CURING_SHOT_RADIUS, }, { kind: "hot", coefficient: 0, basePoints: 167.5, ticks: ROM_PRIEST_SCOUT_CURING_SHOT_HOT_TICKS, intervalMs: ROM_PRIEST_SCOUT_CURING_SHOT_TICK_INTERVAL_MS, recipient: "caster", scope: "party", radius: ROM_PRIEST_SCOUT_CURING_SHOT_RADIUS, }, expect.objectContaining({ kind: "apply-aura", recipient: "caster", aura: ROM_PRIEST_SCOUT_CURING_SHOT_ELEMENTAL_AURA, }), ]); expect(ROM_PRIEST_SCOUT_CURING_SHOT_DURATION_MS).toBe(18_000); expect(ROM_PRIEST_SCOUT_CURING_SHOT_ELEMENTAL_DAMAGE_BONUS).toBe(0.1); expect(ROM_PRIEST_SCOUT_CURING_SHOT_ELEMENTAL_SCHOOLS).toEqual([ "frost", ]); expect(ROM_PRIEST_SCOUT_CURING_SHOT_ELEMENTAL_AURA).toMatchObject({ durationMs: 18_000, modifiers: [ { kind: "damage", direction: "dealt", school: "frost", operation: "percent", value: 0.1 }, ], }); }); it("does not carry over the local five-target cap and preserves the unsupported-to-scale local cost", () => { const ability = romPriestScoutCuringShotCatalogOverride(localAbility); expect(ability.targeting).toEqual({ shape: "circle", radius: 10 }); expect(ability.cost).toEqual({ resource: "mana", amount: 120 }); expect(ability.costs).toEqual([{ resource: "mana", amount: 120 }]); expect(ability.ranks?.[0]).toMatchObject({ castMode: "instant", castTimeMs: 0, cooldownMs: 6_000, cost: { resource: "mana", amount: 120 }, costs: [{ resource: "mana", amount: 120 }], targeting: { shape: "circle", radius: 10 }, effects: ability.effects, }); expect(ability.ranks?.[0]?.targeting).not.toHaveProperty("maximumTargets"); }); it("recognizes only the exact Priest-primary/Scout-secondary source identity", () => { expect(isRomPriestScoutCuringShot({ classId: "rom-priest", secondaryClassId: "rom-scout", sourceSkillId: ROM_PRIEST_SCOUT_CURING_SHOT_ID, })).toBe(true); expect(isRomPriestScoutCuringShot({ classId: "rom-scout", secondaryClassId: "rom-priest", sourceSkillId: ROM_PRIEST_SCOUT_CURING_SHOT_ID, })).toBe(false); expect(isRomPriestScoutCuringShot({ classId: "rom-priest", secondaryClassId: "rom-mage", sourceSkillId: ROM_PRIEST_SCOUT_CURING_SHOT_ID, })).toBe(false); expect(isRomPriestScoutCuringShot({ classId: "rom-priest", secondaryClassId: "rom-scout", sourceSkillId: ROM_PRIEST_SCOUT_CURING_SHOT_ID + 1, })).toBe(false); }); it("leaves other rows and non-RuneWaker copies unchanged", () => { const wrongPair = { ...localAbility, secondaryClassId: "rom-mage" as const }; const wrongId = { ...localAbility, dbcSpellId: ROM_PRIEST_SCOUT_CURING_SHOT_ID + 1 }; const wrongSource = { ...localAbility, source: "core" as const }; expect(romPriestScoutCuringShotCatalogOverride(wrongPair)).toBe(wrongPair); expect(romPriestScoutCuringShotCatalogOverride(wrongId)).toBe(wrongId); expect(romPriestScoutCuringShotCatalogOverride(wrongSource)).toBe(wrongSource); }); });