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>
109 lines
3.1 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|