86 lines
3.7 KiB
TypeScript
86 lines
3.7 KiB
TypeScript
// @vitest-environment happy-dom
|
|
|
|
import { act, createElement, type ComponentProps } from "react";
|
|
import { createRoot, type Root } from "react-dom/client";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { WeaponComparisonCard } from "@/components/weapon-comparison-card";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { useManualSave } from "@/hooks/use-manual-save";
|
|
import { WeaponEditor } from "./weapon-editor";
|
|
|
|
vi.mock("@/app/admin/actions", () => ({ saveWeaponSection: vi.fn() }));
|
|
vi.mock("@/hooks/use-manual-save", () => ({
|
|
useManualSave: vi.fn(() => ({ state: "saved", canSave: false })),
|
|
}));
|
|
|
|
let root: Root;
|
|
let container: HTMLDivElement;
|
|
|
|
beforeEach(() => {
|
|
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
vi.clearAllMocks();
|
|
container = document.createElement("div");
|
|
document.body.append(container);
|
|
root = createRoot(container);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await act(async () => root.unmount());
|
|
container.remove();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
async function render(value: string) {
|
|
const props = {
|
|
guide: { id: "guide", characterKey: "character", version: 1 },
|
|
characters: [{ key: "character", weaponType: "sword" }],
|
|
weapons: [{ key: "weapon", name: "Weapon", weaponType: "sword", rarity: 5, imageUrl: "/weapon.png" }],
|
|
initial: { sectionNote: null, weaponGroups: [{
|
|
id: "condition", name: null, note: null, rows: [{
|
|
id: "row", weaponKey: "weapon", refinement: "1", overallPercent: value, personalPercent: null, note: null,
|
|
}],
|
|
}] },
|
|
} as unknown as ComponentProps<typeof WeaponEditor>;
|
|
await act(async () => root.render(createElement(WeaponEditor, props)));
|
|
return container.querySelector<HTMLInputElement>("#weapon-overall-row")!;
|
|
}
|
|
|
|
describe("weapon damage editing", () => {
|
|
it("uses the public curve and damage typography with more room for inputs", async () => {
|
|
const input = await render("120.00");
|
|
const publicCard = document.createElement("div");
|
|
publicCard.innerHTML = renderToStaticMarkup(createElement(WeaponComparisonCard, {
|
|
weapon: { key: "weapon", name: "Weapon", rarity: 5, imageUrl: "/weapon.png" },
|
|
rank: 1, refinement: "1", note: null, overallPercent: "120.00", personalPercent: null,
|
|
weaponHref: "https://lunaris.moe/weapon/weapon",
|
|
}));
|
|
const publicContent = publicCard.querySelector('[data-slot="card-content"]')!;
|
|
const adminContent = input.closest('[data-slot="card-content"]')!;
|
|
expect(adminContent.className.replace(/md:grid-cols-\S+/u, ""))
|
|
.toBe(publicContent.className.replace(/md:grid-cols-\S+/u, ""));
|
|
expect(adminContent.className).toContain("md:grid-cols-[minmax(0,5fr)_minmax(0,2fr)]");
|
|
expect(adminContent.firstElementChild!.className.replace(" relative", ""))
|
|
.toBe(publicContent.firstElementChild!.className);
|
|
const publicValue = publicContent.lastElementChild!.querySelector("p")!;
|
|
expect(input.parentElement!.className).toBe(publicValue.className);
|
|
expect(input.value).toBe(publicValue.textContent);
|
|
expect(adminContent.lastElementChild!.className).toBe(publicContent.lastElementChild!.className);
|
|
});
|
|
|
|
it.each([
|
|
["0.00", "-"],
|
|
["120.00", "120%"],
|
|
["120.125", "120.13%"],
|
|
["", ""],
|
|
])("displays %s as %s while preserving its raw draft value", async (raw, display) => {
|
|
const input = await render(raw);
|
|
expect(input.value).toBe(display);
|
|
await act(async () => input.focus());
|
|
expect(input.value).toBe(raw);
|
|
await act(async () => input.blur());
|
|
expect(input.value).toBe(display);
|
|
const options = vi.mocked(useManualSave).mock.lastCall![0];
|
|
expect(options.value).toMatchObject({ groups: [{ rows: [{ overallPercent: raw === "" ? null : Number(raw) }] }] });
|
|
});
|
|
});
|