Files
initiative/apps/web/src/components/__tests__/player-character-section.test.tsx
Lukas 2c643cc98b
All checks were successful
CI / check (push) Successful in 2m13s
CI / build-image (push) Has been skipped
Introduce adapter injection and migrate test suite
Replace direct adapter/persistence imports with context-based injection
(AdapterContext + useAdapters) so tests use in-memory implementations
instead of vi.mock. Migrate component tests from context mocking to
AllProviders with real hooks. Extract export/import logic from ActionBar
into useEncounterExportImport hook. Add bestiary-cache and
bestiary-index-adapter test suites. Raise adapter coverage thresholds
(68→80 lines, 56→62 branches).

77 test files, 891 tests, all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:55:45 +02:00

109 lines
3.1 KiB
TypeScript

// @vitest-environment jsdom
import "@testing-library/jest-dom/vitest";
import { act, cleanup, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { createRef } from "react";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { polyfillDialog } from "../../__tests__/polyfill-dialog.js";
import { AllProviders } from "../../__tests__/test-providers.js";
import {
PlayerCharacterSection,
type PlayerCharacterSectionHandle,
} from "../player-character-section.js";
const CREATE_FIRST_PC_REGEX = /create your first player character/i;
beforeAll(() => {
polyfillDialog();
Object.defineProperty(globalThis, "matchMedia", {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
});
afterEach(cleanup);
function renderSection() {
const ref = createRef<PlayerCharacterSectionHandle>();
const result = render(<PlayerCharacterSection ref={ref} />, {
wrapper: AllProviders,
});
return { ...result, ref };
}
describe("PlayerCharacterSection", () => {
it("openManagement ref handle opens the management dialog", async () => {
const { ref } = renderSection();
const handle = ref.current;
if (!handle) throw new Error("ref not set");
act(() => handle.openManagement());
// Management dialog should now be open with its title visible
await waitFor(() => {
const dialogs = document.querySelectorAll("dialog");
const managementDialog = Array.from(dialogs).find((d) =>
d.textContent?.includes("Player Characters"),
);
expect(managementDialog).toHaveAttribute("open");
});
});
it("creating a character from management opens create modal", async () => {
const user = userEvent.setup();
const { ref } = renderSection();
const handle = ref.current;
if (!handle) throw new Error("ref not set");
act(() => handle.openManagement());
await user.click(
screen.getByRole("button", {
name: CREATE_FIRST_PC_REGEX,
}),
);
// Create modal should now be visible
await waitFor(() => {
expect(screen.getByPlaceholderText("Character name")).toBeInTheDocument();
});
});
it("saving a new character and returning to management", async () => {
const user = userEvent.setup();
const { ref } = renderSection();
const handle = ref.current;
if (!handle) throw new Error("ref not set");
act(() => handle.openManagement());
await user.click(
screen.getByRole("button", {
name: CREATE_FIRST_PC_REGEX,
}),
);
// Fill in the create form
await user.type(screen.getByPlaceholderText("Character name"), "Aria");
await user.type(screen.getByPlaceholderText("AC"), "16");
await user.type(screen.getByPlaceholderText("Max HP"), "30");
await user.click(screen.getByRole("button", { name: "Create" }));
// Should return to management dialog showing the new character
await waitFor(() => {
expect(screen.getByText("Aria")).toBeInTheDocument();
});
});
});