13 new test files for untested components (color-palette, player-management, stat-block, settings-modal, export/import dialogs, bulk-import-prompt, source-fetch-prompt, player-character-section) and hooks (use-long-press, use-swipe-to-dismiss, use-bulk-import, use-initiative-rolls). Expand combatant-row tests with inline editing, HP popover, and condition picker. Component coverage: 59% → 80% lines, 55% → 71% branches Hook coverage: 72% → 83% lines, 55% → 66% branches Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
121 lines
3.5 KiB
TypeScript
121 lines
3.5 KiB
TypeScript
// @vitest-environment jsdom
|
|
import "@testing-library/jest-dom/vitest";
|
|
|
|
import { type PlayerCharacter, playerCharacterId } from "@initiative/domain";
|
|
import { cleanup, render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
|
import { polyfillDialog } from "../../__tests__/polyfill-dialog.js";
|
|
|
|
afterEach(cleanup);
|
|
|
|
const CREATE_FIRST_PC_REGEX = /create your first player character/i;
|
|
const LEVEL_REGEX = /^Lv /;
|
|
|
|
import { PlayerManagement } from "../player-management.js";
|
|
|
|
beforeAll(() => {
|
|
polyfillDialog();
|
|
});
|
|
|
|
const PC_WARRIOR: PlayerCharacter = {
|
|
id: playerCharacterId("pc-1"),
|
|
name: "Thorin",
|
|
ac: 18,
|
|
maxHp: 45,
|
|
color: "red",
|
|
icon: "sword",
|
|
};
|
|
|
|
const PC_WIZARD: PlayerCharacter = {
|
|
id: playerCharacterId("pc-2"),
|
|
name: "Gandalf",
|
|
ac: 12,
|
|
maxHp: 30,
|
|
color: "blue",
|
|
icon: "wand",
|
|
level: 10,
|
|
};
|
|
|
|
function renderManagement(
|
|
overrides: Partial<Parameters<typeof PlayerManagement>[0]> = {},
|
|
) {
|
|
const props = {
|
|
open: true,
|
|
onClose: vi.fn(),
|
|
characters: [] as readonly PlayerCharacter[],
|
|
onEdit: vi.fn(),
|
|
onDelete: vi.fn(),
|
|
onCreate: vi.fn(),
|
|
...overrides,
|
|
};
|
|
return { ...render(<PlayerManagement {...props} />), props };
|
|
}
|
|
|
|
describe("PlayerManagement", () => {
|
|
it("shows empty state when no characters", () => {
|
|
renderManagement();
|
|
expect(screen.getByText("No player characters yet")).toBeInTheDocument();
|
|
});
|
|
|
|
it("shows create button in empty state that calls onCreate", async () => {
|
|
const user = userEvent.setup();
|
|
const { props } = renderManagement();
|
|
|
|
await user.click(
|
|
screen.getByRole("button", {
|
|
name: CREATE_FIRST_PC_REGEX,
|
|
}),
|
|
);
|
|
expect(props.onCreate).toHaveBeenCalled();
|
|
});
|
|
|
|
it("renders each character with name, AC, HP", () => {
|
|
renderManagement({ characters: [PC_WARRIOR, PC_WIZARD] });
|
|
expect(screen.getByText("Thorin")).toBeInTheDocument();
|
|
expect(screen.getByText("Gandalf")).toBeInTheDocument();
|
|
expect(screen.getByText("AC 18")).toBeInTheDocument();
|
|
expect(screen.getByText("HP 45")).toBeInTheDocument();
|
|
expect(screen.getByText("AC 12")).toBeInTheDocument();
|
|
expect(screen.getByText("HP 30")).toBeInTheDocument();
|
|
});
|
|
|
|
it("shows level when present, omits when undefined", () => {
|
|
renderManagement({ characters: [PC_WARRIOR, PC_WIZARD] });
|
|
expect(screen.getByText("Lv 10")).toBeInTheDocument();
|
|
// Thorin has no level — there should be only one "Lv" text
|
|
expect(screen.queryAllByText(LEVEL_REGEX)).toHaveLength(1);
|
|
});
|
|
|
|
it("edit button calls onEdit with the character", async () => {
|
|
const user = userEvent.setup();
|
|
const { props } = renderManagement({ characters: [PC_WARRIOR] });
|
|
|
|
await user.click(screen.getByRole("button", { name: "Edit" }));
|
|
expect(props.onEdit).toHaveBeenCalledWith(PC_WARRIOR);
|
|
});
|
|
|
|
it("delete button calls onDelete after confirmation", async () => {
|
|
const user = userEvent.setup();
|
|
const { props } = renderManagement({ characters: [PC_WARRIOR] });
|
|
|
|
const deleteBtn = screen.getByRole("button", {
|
|
name: "Delete player character",
|
|
});
|
|
await user.click(deleteBtn);
|
|
const confirmBtn = screen.getByRole("button", {
|
|
name: "Confirm delete player character",
|
|
});
|
|
await user.click(confirmBtn);
|
|
expect(props.onDelete).toHaveBeenCalledWith(PC_WARRIOR.id);
|
|
});
|
|
|
|
it("add button calls onCreate", async () => {
|
|
const user = userEvent.setup();
|
|
const { props } = renderManagement({ characters: [PC_WARRIOR] });
|
|
|
|
await user.click(screen.getByRole("button", { name: "Add" }));
|
|
expect(props.onCreate).toHaveBeenCalled();
|
|
});
|
|
});
|