Files
healer-man/src/ui/AbilityTooltip.test.ts
T
2026-09-14 13:20:20 -04:00

75 lines
3.5 KiB
TypeScript

import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import {
AbilityTooltipContent, AbilityTooltipTarget, abilityTooltipFacts, abilityTooltipPosition,
type AbilityTooltipData,
} from "./AbilityTooltip";
const ability: AbilityTooltipData = {
name: "Healing Wave", description: "Heals a friendly target for 120 health.",
resourceCost: 15, resourceLabel: "mana", cooldownSeconds: 6,
castSeconds: 1.5, range: 40, target: "friendly",
};
describe("ability tooltips", () => {
it("shows the ability's description, binding, and gameplay facts", () => {
const markup = renderToStaticMarkup(createElement(AbilityTooltipContent, { ability, bindingLabel: "Shift+1" }));
for (const text of [ability.name, ability.description!, "Shift+1", "15 mana", "1.5 sec cast", "6 sec cooldown", "40 m range", "Selected ally or self"]) {
expect(markup).toContain(text);
}
});
it("explains free, instant abilities and distinguishes no cooldown from missing data", () => {
expect(abilityTooltipFacts({ name: "Free", resourceCost: 0, castSeconds: 0, cooldownSeconds: 0 }))
.toEqual(["No cost", "Instant", "No cooldown"]);
expect(abilityTooltipFacts({ name: "Unknown" })).toEqual([]);
});
it("shows unlock and talent requirements without hiding the description", () => {
const markup = renderToStaticMarkup(createElement(AbilityTooltipContent, {
ability: { ...ability, lockedUntilLevel: 20, talentLocked: true },
}));
expect(markup).toContain(ability.description);
expect(markup).toContain("Unlocks at level 20");
expect(markup).toContain("Learn in talents");
});
it("does not present active cast facts for passive abilities", () => {
expect(abilityTooltipFacts({ ...ability, passive: true, rank: 2 })).toEqual(["Passive", "Rank 2"]);
});
it("preserves inspectable unavailable controls and creates no layout wrapper", () => {
const markup = renderToStaticMarkup(createElement(AbilityTooltipTarget, {
ability,
children: createElement("button", { "aria-disabled": true, title: "Old browser tooltip" }, "Healing Wave"),
}));
expect(markup).toBe('<button aria-disabled="true">Healing Wave</button>');
});
it("keeps the empty-slot browser hint without creating an ability tooltip", () => {
const markup = renderToStaticMarkup(createElement(AbilityTooltipTarget, {
ability: null,
children: createElement("button", { title: "Empty binding" }, "Empty"),
}));
expect(markup).toBe('<button title="Empty binding">Empty</button>');
});
it("places a bottom-bar tooltip above its control", () => {
expect(abilityTooltipPosition({ left: 400, right: 440, top: 700, bottom: 740 }, { width: 300, height: 180 }, { width: 1000, height: 800 }))
.toEqual({ left: 270, top: 512 });
});
it("flips below top-edge controls and clamps to either horizontal edge", () => {
expect(abilityTooltipPosition({ left: 0, right: 40, top: 20, bottom: 60 }, { width: 300, height: 180 }, { width: 1000, height: 800 }))
.toEqual({ left: 12, top: 68 });
expect(abilityTooltipPosition({ left: 960, right: 1000, top: 20, bottom: 60 }, { width: 300, height: 180 }, { width: 1000, height: 800 }))
.toEqual({ left: 688, top: 68 });
});
it("keeps a tall tooltip inside a narrow viewport", () => {
expect(abilityTooltipPosition({ left: 200, right: 240, top: 120, bottom: 160 }, { width: 296, height: 400 }, { width: 320, height: 480 }))
.toEqual({ left: 12, top: 68 });
});
});