122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
// @vitest-environment jsdom
|
|
import "@testing-library/jest-dom/vitest";
|
|
|
|
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 { AllProviders } from "../../__tests__/test-providers.js";
|
|
import { ActionBar } from "../action-bar.js";
|
|
|
|
// Mock persistence — no localStorage interaction
|
|
vi.mock("../../persistence/encounter-storage.js", () => ({
|
|
loadEncounter: () => null,
|
|
saveEncounter: () => {},
|
|
}));
|
|
|
|
vi.mock("../../persistence/player-character-storage.js", () => ({
|
|
loadPlayerCharacters: () => [],
|
|
savePlayerCharacters: () => {},
|
|
}));
|
|
|
|
// Mock bestiary — no IndexedDB or JSON index
|
|
vi.mock("../../adapters/bestiary-cache.js", () => ({
|
|
loadAllCachedCreatures: () => Promise.resolve(new Map()),
|
|
isSourceCached: () => Promise.resolve(false),
|
|
cacheSource: () => Promise.resolve(),
|
|
getCachedSources: () => Promise.resolve([]),
|
|
clearSource: () => Promise.resolve(),
|
|
clearAll: () => Promise.resolve(),
|
|
}));
|
|
|
|
vi.mock("../../adapters/bestiary-index-adapter.js", () => ({
|
|
loadBestiaryIndex: () => ({ sources: {}, creatures: [] }),
|
|
getAllSourceCodes: () => [],
|
|
getDefaultFetchUrl: () => "",
|
|
getSourceDisplayName: (code: string) => code,
|
|
}));
|
|
|
|
// DOM API stubs — jsdom doesn't implement these
|
|
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(),
|
|
})),
|
|
});
|
|
});
|
|
|
|
afterEach(cleanup);
|
|
|
|
function renderBar(props: Partial<Parameters<typeof ActionBar>[0]> = {}) {
|
|
return render(<ActionBar {...props} />, { wrapper: AllProviders });
|
|
}
|
|
|
|
describe("ActionBar", () => {
|
|
it("renders input with placeholder '+ Add combatants'", () => {
|
|
renderBar();
|
|
expect(screen.getByPlaceholderText("+ Add combatants")).toBeInTheDocument();
|
|
});
|
|
|
|
it("submitting with a name adds a combatant", async () => {
|
|
const user = userEvent.setup();
|
|
renderBar();
|
|
const input = screen.getByPlaceholderText("+ Add combatants");
|
|
await user.type(input, "Goblin");
|
|
// The Add button appears when name >= 2 chars and no suggestions
|
|
const addButton = screen.getByRole("button", { name: "Add" });
|
|
await user.click(addButton);
|
|
// Input is cleared after adding (context handles the state)
|
|
expect(input).toHaveValue("");
|
|
});
|
|
|
|
it("submitting with empty name does nothing", async () => {
|
|
const user = userEvent.setup();
|
|
renderBar();
|
|
// Submit the form directly (Enter on empty input)
|
|
const input = screen.getByPlaceholderText("+ Add combatants");
|
|
await user.type(input, "{Enter}");
|
|
// Input stays empty, no error
|
|
expect(input).toHaveValue("");
|
|
});
|
|
|
|
it("shows custom fields (Init, AC, MaxHP) when name >= 2 chars and no bestiary suggestions", async () => {
|
|
const user = userEvent.setup();
|
|
renderBar();
|
|
const input = screen.getByPlaceholderText("+ Add combatants");
|
|
await user.type(input, "Go");
|
|
expect(screen.getByPlaceholderText("Init")).toBeInTheDocument();
|
|
expect(screen.getByPlaceholderText("AC")).toBeInTheDocument();
|
|
expect(screen.getByPlaceholderText("MaxHP")).toBeInTheDocument();
|
|
});
|
|
|
|
it("shows Add button when name >= 2 chars and no suggestions", async () => {
|
|
const user = userEvent.setup();
|
|
renderBar();
|
|
const input = screen.getByPlaceholderText("+ Add combatants");
|
|
await user.type(input, "Go");
|
|
expect(screen.getByRole("button", { name: "Add" })).toBeInTheDocument();
|
|
});
|
|
|
|
it("does not show roll all initiative button when no creature combatants", () => {
|
|
renderBar();
|
|
expect(
|
|
screen.queryByRole("button", { name: "Roll all initiative" }),
|
|
).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows overflow menu items", () => {
|
|
renderBar({ onManagePlayers: vi.fn() });
|
|
// The overflow menu should be present (it contains Player Characters etc.)
|
|
expect(
|
|
screen.getByRole("button", { name: "More actions" }),
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|