Implements PF2e as an alternative game system alongside D&D 5e/5.5e. Settings modal "Game System" selector switches conditions, bestiary, stat block layout, and initiative calculation between systems. - Valued conditions with increment/decrement UX (Clumsy 2, Frightened 3) - 2,502 PF2e creatures from bundled search index (77 sources) - PF2e stat block: level, traits, Perception, Fort/Ref/Will, ability mods - Perception-based initiative rolling - System-scoped source cache (D&D and PF2e sources don't collide) - Backwards-compatible condition rehydration (ConditionId[] → ConditionEntry[]) - Difficulty indicator hidden in PF2e mode (excluded from MVP) Closes dostulata/initiative#19 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { act, renderHook } from "@testing-library/react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { useRulesEdition } from "../use-rules-edition.js";
|
|
|
|
const STORAGE_KEY = "initiative:game-system";
|
|
const OLD_STORAGE_KEY = "initiative:rules-edition";
|
|
|
|
describe("useRulesEdition", () => {
|
|
afterEach(() => {
|
|
// Reset to default
|
|
const { result } = renderHook(() => useRulesEdition());
|
|
act(() => result.current.setEdition("5.5e"));
|
|
localStorage.removeItem(STORAGE_KEY);
|
|
localStorage.removeItem(OLD_STORAGE_KEY);
|
|
});
|
|
|
|
it("defaults to 5.5e", () => {
|
|
const { result } = renderHook(() => useRulesEdition());
|
|
expect(result.current.edition).toBe("5.5e");
|
|
});
|
|
|
|
it("setEdition updates value", () => {
|
|
const { result } = renderHook(() => useRulesEdition());
|
|
|
|
act(() => result.current.setEdition("5e"));
|
|
|
|
expect(result.current.edition).toBe("5e");
|
|
});
|
|
|
|
it("setEdition persists to localStorage", () => {
|
|
const { result } = renderHook(() => useRulesEdition());
|
|
|
|
act(() => result.current.setEdition("5e"));
|
|
|
|
expect(localStorage.getItem(STORAGE_KEY)).toBe("5e");
|
|
});
|
|
|
|
it("multiple hooks stay in sync", () => {
|
|
const { result: r1 } = renderHook(() => useRulesEdition());
|
|
const { result: r2 } = renderHook(() => useRulesEdition());
|
|
|
|
act(() => r1.current.setEdition("5e"));
|
|
|
|
expect(r2.current.edition).toBe("5e");
|
|
});
|
|
|
|
it("accepts pf2e as a valid game system", () => {
|
|
const { result } = renderHook(() => useRulesEdition());
|
|
|
|
act(() => result.current.setEdition("pf2e"));
|
|
|
|
expect(result.current.edition).toBe("pf2e");
|
|
expect(localStorage.getItem(STORAGE_KEY)).toBe("pf2e");
|
|
});
|
|
|
|
it("migrates from old storage key on fresh module load", async () => {
|
|
// Set up old key before re-importing the module
|
|
localStorage.setItem(OLD_STORAGE_KEY, "5e");
|
|
localStorage.removeItem(STORAGE_KEY);
|
|
|
|
// Force a fresh module so loadEdition() re-runs at init time
|
|
vi.resetModules();
|
|
const { useRulesEdition: freshHook } = await import(
|
|
"../use-rules-edition.js"
|
|
);
|
|
|
|
const { result } = renderHook(() => freshHook());
|
|
|
|
expect(result.current.edition).toBe("5e");
|
|
expect(localStorage.getItem(STORAGE_KEY)).toBe("5e");
|
|
expect(localStorage.getItem(OLD_STORAGE_KEY)).toBeNull();
|
|
});
|
|
});
|