75 lines
3.5 KiB
TypeScript
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 });
|
|
});
|
|
});
|