Add missing component and hook tests, raise coverage thresholds
13 new test files for untested components (color-palette, player-management, stat-block, settings-modal, export/import dialogs, bulk-import-prompt, source-fetch-prompt, player-character-section) and hooks (use-long-press, use-swipe-to-dismiss, use-bulk-import, use-initiative-rolls). Expand combatant-row tests with inline editing, HP popover, and condition picker. Component coverage: 59% → 80% lines, 55% → 71% branches Hook coverage: 72% → 83% lines, 55% → 66% branches Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
146
apps/web/src/components/__tests__/bulk-import-prompt.test.tsx
Normal file
146
apps/web/src/components/__tests__/bulk-import-prompt.test.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
// @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, describe, expect, it, vi } from "vitest";
|
||||
import { BulkImportPrompt } from "../bulk-import-prompt.js";
|
||||
|
||||
const THREE_SOURCES_REGEX = /3 sources/;
|
||||
const GITHUB_URL_REGEX = /raw\.githubusercontent/;
|
||||
const LOADING_PROGRESS_REGEX = /Loading sources\.\.\. 4\/10/;
|
||||
const SEVEN_OF_TEN_REGEX = /7\/10 sources/;
|
||||
const THREE_FAILED_REGEX = /3 failed/;
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
const mockFetchAndCacheSource = vi.fn();
|
||||
const mockIsSourceCached = vi.fn().mockResolvedValue(false);
|
||||
const mockRefreshCache = vi.fn();
|
||||
const mockStartImport = vi.fn();
|
||||
const mockReset = vi.fn();
|
||||
const mockDismissPanel = vi.fn();
|
||||
|
||||
let mockImportState = {
|
||||
status: "idle" as string,
|
||||
total: 0,
|
||||
completed: 0,
|
||||
failed: 0,
|
||||
};
|
||||
|
||||
vi.mock("../../contexts/bestiary-context.js", () => ({
|
||||
useBestiaryContext: () => ({
|
||||
fetchAndCacheSource: mockFetchAndCacheSource,
|
||||
isSourceCached: mockIsSourceCached,
|
||||
refreshCache: mockRefreshCache,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../../contexts/bulk-import-context.js", () => ({
|
||||
useBulkImportContext: () => ({
|
||||
state: mockImportState,
|
||||
startImport: mockStartImport,
|
||||
reset: mockReset,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../../contexts/side-panel-context.js", () => ({
|
||||
useSidePanelContext: () => ({
|
||||
dismissPanel: mockDismissPanel,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../../adapters/bestiary-index-adapter.js", () => ({
|
||||
getAllSourceCodes: () => ["MM", "VGM", "XGE"],
|
||||
getDefaultFetchUrl: () => "",
|
||||
getSourceDisplayName: (code: string) => code,
|
||||
loadBestiaryIndex: () => ({ sources: {}, creatures: [] }),
|
||||
}));
|
||||
|
||||
describe("BulkImportPrompt", () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockImportState = { status: "idle", total: 0, completed: 0, failed: 0 };
|
||||
});
|
||||
|
||||
it("idle: shows base URL input, source count, Load All button", () => {
|
||||
render(<BulkImportPrompt />);
|
||||
expect(screen.getByText(THREE_SOURCES_REGEX)).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue(GITHUB_URL_REGEX)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "Load All" }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("idle: clearing URL disables the button", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<BulkImportPrompt />);
|
||||
|
||||
const input = screen.getByDisplayValue(GITHUB_URL_REGEX);
|
||||
await user.clear(input);
|
||||
expect(screen.getByRole("button", { name: "Load All" })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("idle: clicking Load All calls startImport with URL", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<BulkImportPrompt />);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Load All" }));
|
||||
expect(mockStartImport).toHaveBeenCalledWith(
|
||||
expect.stringContaining("raw.githubusercontent"),
|
||||
mockFetchAndCacheSource,
|
||||
mockIsSourceCached,
|
||||
mockRefreshCache,
|
||||
);
|
||||
});
|
||||
|
||||
it("loading: shows progress text and progress bar", () => {
|
||||
mockImportState = {
|
||||
status: "loading",
|
||||
total: 10,
|
||||
completed: 3,
|
||||
failed: 1,
|
||||
};
|
||||
render(<BulkImportPrompt />);
|
||||
expect(screen.getByText(LOADING_PROGRESS_REGEX)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("complete: shows success message and Done button", () => {
|
||||
mockImportState = {
|
||||
status: "complete",
|
||||
total: 10,
|
||||
completed: 10,
|
||||
failed: 0,
|
||||
};
|
||||
render(<BulkImportPrompt />);
|
||||
expect(screen.getByText("All sources loaded")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Done" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("complete: Done calls dismissPanel and reset", async () => {
|
||||
mockImportState = {
|
||||
status: "complete",
|
||||
total: 10,
|
||||
completed: 10,
|
||||
failed: 0,
|
||||
};
|
||||
const user = userEvent.setup();
|
||||
render(<BulkImportPrompt />);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Done" }));
|
||||
expect(mockDismissPanel).toHaveBeenCalled();
|
||||
expect(mockReset).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("partial-failure: shows loaded/failed counts", () => {
|
||||
mockImportState = {
|
||||
status: "partial-failure",
|
||||
total: 10,
|
||||
completed: 7,
|
||||
failed: 3,
|
||||
};
|
||||
render(<BulkImportPrompt />);
|
||||
expect(screen.getByText(SEVEN_OF_TEN_REGEX)).toBeInTheDocument();
|
||||
expect(screen.getByText(THREE_FAILED_REGEX)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user