// @vitest-environment jsdom import "@testing-library/jest-dom/vitest"; import { cleanup, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { afterEach, describe, expect, it, vi } from "vitest"; import { HpAdjustPopover } from "../hp-adjust-popover"; afterEach(cleanup); function renderPopover( overrides: Partial<{ onAdjust: (delta: number) => void; onSetTempHp: (value: number) => void; onClose: () => void; }> = {}, ) { const onAdjust = overrides.onAdjust ?? vi.fn(); const onSetTempHp = overrides.onSetTempHp ?? vi.fn(); const onClose = overrides.onClose ?? vi.fn(); const result = render( , ); return { ...result, onAdjust, onSetTempHp, onClose }; } describe("HpAdjustPopover", () => { it("renders input with placeholder 'HP'", () => { renderPopover(); expect(screen.getByPlaceholderText("HP")).toBeInTheDocument(); }); it("damage and heal buttons are disabled when input is empty", () => { renderPopover(); expect(screen.getByRole("button", { name: "Apply damage" })).toBeDisabled(); expect( screen.getByRole("button", { name: "Apply healing" }), ).toBeDisabled(); }); it("damage and heal buttons are disabled when input is '0'", async () => { const user = userEvent.setup(); renderPopover(); await user.type(screen.getByPlaceholderText("HP"), "0"); expect(screen.getByRole("button", { name: "Apply damage" })).toBeDisabled(); expect( screen.getByRole("button", { name: "Apply healing" }), ).toBeDisabled(); }); it("typing a valid number enables both buttons", async () => { const user = userEvent.setup(); renderPopover(); await user.type(screen.getByPlaceholderText("HP"), "5"); expect( screen.getByRole("button", { name: "Apply damage" }), ).not.toBeDisabled(); expect( screen.getByRole("button", { name: "Apply healing" }), ).not.toBeDisabled(); }); it("clicking damage button calls onAdjust with negative value and onClose", async () => { const user = userEvent.setup(); const { onAdjust, onClose } = renderPopover(); await user.type(screen.getByPlaceholderText("HP"), "7"); await user.click(screen.getByRole("button", { name: "Apply damage" })); expect(onAdjust).toHaveBeenCalledWith(-7); expect(onClose).toHaveBeenCalled(); }); it("clicking heal button calls onAdjust with positive value and onClose", async () => { const user = userEvent.setup(); const { onAdjust, onClose } = renderPopover(); await user.type(screen.getByPlaceholderText("HP"), "3"); await user.click(screen.getByRole("button", { name: "Apply healing" })); expect(onAdjust).toHaveBeenCalledWith(3); expect(onClose).toHaveBeenCalled(); }); it("Enter key applies damage (negative)", async () => { const user = userEvent.setup(); const { onAdjust, onClose } = renderPopover(); const input = screen.getByPlaceholderText("HP"); await user.type(input, "4"); await user.keyboard("{Enter}"); expect(onAdjust).toHaveBeenCalledWith(-4); expect(onClose).toHaveBeenCalled(); }); it("Shift+Enter applies healing (positive)", async () => { const user = userEvent.setup(); const { onAdjust, onClose } = renderPopover(); const input = screen.getByPlaceholderText("HP"); await user.type(input, "6"); await user.keyboard("{Shift>}{Enter}{/Shift}"); expect(onAdjust).toHaveBeenCalledWith(6); expect(onClose).toHaveBeenCalled(); }); it("Escape key calls onClose", async () => { const user = userEvent.setup(); const { onClose } = renderPopover(); const input = screen.getByPlaceholderText("HP"); await user.type(input, "2"); await user.keyboard("{Escape}"); expect(onClose).toHaveBeenCalled(); }); it("only accepts digit characters in input", async () => { const user = userEvent.setup(); renderPopover(); const input = screen.getByPlaceholderText("HP"); await user.type(input, "12abc34"); expect(input).toHaveValue("1234"); }); describe("temp HP", () => { it("shield button calls onSetTempHp with entered value and closes", async () => { const user = userEvent.setup(); const { onSetTempHp, onClose } = renderPopover(); await user.type(screen.getByPlaceholderText("HP"), "8"); await user.click(screen.getByRole("button", { name: "Set temp HP" })); expect(onSetTempHp).toHaveBeenCalledWith(8); expect(onClose).toHaveBeenCalled(); }); it("shield button is disabled when input is empty", () => { renderPopover(); expect( screen.getByRole("button", { name: "Set temp HP" }), ).toBeDisabled(); }); it("shield button is disabled when input is '0'", async () => { const user = userEvent.setup(); renderPopover(); await user.type(screen.getByPlaceholderText("HP"), "0"); expect( screen.getByRole("button", { name: "Set temp HP" }), ).toBeDisabled(); }); }); });