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>
138 lines
3.3 KiB
TypeScript
138 lines
3.3 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { playerCharacterId } from "@initiative/domain";
|
|
import { act, renderHook } from "@testing-library/react";
|
|
import type { ReactNode } from "react";
|
|
import { beforeAll, describe, expect, it, vi } from "vitest";
|
|
import { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js";
|
|
import { AllProviders } from "../../__tests__/test-providers.js";
|
|
import { usePlayerCharacters } from "../use-player-characters.js";
|
|
|
|
beforeAll(() => {
|
|
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(),
|
|
})),
|
|
});
|
|
});
|
|
|
|
function wrapper({ children }: { children: ReactNode }) {
|
|
return <AllProviders>{children}</AllProviders>;
|
|
}
|
|
|
|
describe("usePlayerCharacters", () => {
|
|
it("initializes with characters from persistence", () => {
|
|
const stored = [
|
|
{
|
|
id: playerCharacterId("pc-1"),
|
|
name: "Aria",
|
|
ac: 16,
|
|
maxHp: 30,
|
|
color: undefined,
|
|
icon: undefined,
|
|
},
|
|
];
|
|
const adapters = createTestAdapters({ playerCharacters: stored });
|
|
|
|
const { result } = renderHook(() => usePlayerCharacters(), {
|
|
wrapper: ({ children }: { children: ReactNode }) => (
|
|
<AllProviders adapters={adapters}>{children}</AllProviders>
|
|
),
|
|
});
|
|
|
|
expect(result.current.characters).toEqual(stored);
|
|
});
|
|
|
|
it("createCharacter adds a character and persists", () => {
|
|
const { result } = renderHook(() => usePlayerCharacters(), { wrapper });
|
|
|
|
act(() => {
|
|
result.current.createCharacter(
|
|
"Vex",
|
|
15,
|
|
28,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
expect(result.current.characters).toHaveLength(1);
|
|
expect(result.current.characters[0].name).toBe("Vex");
|
|
expect(result.current.characters[0].ac).toBe(15);
|
|
expect(result.current.characters[0].maxHp).toBe(28);
|
|
});
|
|
|
|
it("createCharacter returns domain error for empty name", () => {
|
|
const { result } = renderHook(() => usePlayerCharacters(), { wrapper });
|
|
|
|
let error: unknown;
|
|
act(() => {
|
|
error = result.current.createCharacter(
|
|
"",
|
|
15,
|
|
28,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
expect(error).toMatchObject({ kind: "domain-error" });
|
|
expect(result.current.characters).toHaveLength(0);
|
|
});
|
|
|
|
it("editCharacter updates character and persists", () => {
|
|
const { result } = renderHook(() => usePlayerCharacters(), { wrapper });
|
|
|
|
act(() => {
|
|
result.current.createCharacter(
|
|
"Vex",
|
|
15,
|
|
28,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
const id = result.current.characters[0].id;
|
|
|
|
act(() => {
|
|
result.current.editCharacter(id, { name: "Vex'ahlia" });
|
|
});
|
|
|
|
expect(result.current.characters[0].name).toBe("Vex'ahlia");
|
|
});
|
|
|
|
it("deleteCharacter removes character and persists", () => {
|
|
const { result } = renderHook(() => usePlayerCharacters(), { wrapper });
|
|
|
|
act(() => {
|
|
result.current.createCharacter(
|
|
"Vex",
|
|
15,
|
|
28,
|
|
undefined,
|
|
undefined,
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
const id = result.current.characters[0].id;
|
|
|
|
act(() => {
|
|
result.current.deleteCharacter(id);
|
|
});
|
|
|
|
expect(result.current.characters).toHaveLength(0);
|
|
});
|
|
});
|