151 lines
4.1 KiB
TypeScript
151 lines
4.1 KiB
TypeScript
// @vitest-environment jsdom
|
|
import "@testing-library/jest-dom/vitest";
|
|
|
|
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../../adapters/bestiary-cache.js", () => ({
|
|
getCachedSources: vi.fn(),
|
|
clearSource: vi.fn(),
|
|
clearAll: vi.fn(),
|
|
}));
|
|
|
|
// Mock the context module
|
|
vi.mock("../../contexts/bestiary-context.js", () => ({
|
|
useBestiaryContext: vi.fn(),
|
|
}));
|
|
|
|
import * as bestiaryCache from "../../adapters/bestiary-cache.js";
|
|
import { useBestiaryContext } from "../../contexts/bestiary-context.js";
|
|
import { SourceManager } from "../source-manager.js";
|
|
|
|
const mockGetCachedSources = vi.mocked(bestiaryCache.getCachedSources);
|
|
const mockClearSource = vi.mocked(bestiaryCache.clearSource);
|
|
const mockClearAll = vi.mocked(bestiaryCache.clearAll);
|
|
const mockUseBestiaryContext = vi.mocked(useBestiaryContext);
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
function setupMockContext() {
|
|
const refreshCache = vi.fn().mockResolvedValue(undefined);
|
|
mockUseBestiaryContext.mockReturnValue({
|
|
refreshCache,
|
|
search: vi.fn().mockReturnValue([]),
|
|
getCreature: vi.fn(),
|
|
isLoaded: true,
|
|
isSourceCached: vi.fn().mockResolvedValue(false),
|
|
fetchAndCacheSource: vi.fn(),
|
|
uploadAndCacheSource: vi.fn(),
|
|
} as ReturnType<typeof useBestiaryContext>);
|
|
return { refreshCache };
|
|
}
|
|
|
|
describe("SourceManager", () => {
|
|
it("shows 'No cached sources' empty state when no sources", async () => {
|
|
setupMockContext();
|
|
mockGetCachedSources.mockResolvedValue([]);
|
|
render(<SourceManager />);
|
|
await waitFor(() => {
|
|
expect(screen.getByText("No cached sources")).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it("lists cached sources with display name and creature count", async () => {
|
|
setupMockContext();
|
|
mockGetCachedSources.mockResolvedValue([
|
|
{
|
|
sourceCode: "mm",
|
|
displayName: "Monster Manual",
|
|
creatureCount: 300,
|
|
cachedAt: Date.now(),
|
|
},
|
|
{
|
|
sourceCode: "vgm",
|
|
displayName: "Volo's Guide",
|
|
creatureCount: 100,
|
|
cachedAt: Date.now(),
|
|
},
|
|
]);
|
|
render(<SourceManager />);
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Monster Manual")).toBeInTheDocument();
|
|
});
|
|
expect(screen.getByText("300 creatures")).toBeInTheDocument();
|
|
expect(screen.getByText("Volo's Guide")).toBeInTheDocument();
|
|
expect(screen.getByText("100 creatures")).toBeInTheDocument();
|
|
});
|
|
|
|
it("Clear All button calls cache clear and refreshCache", async () => {
|
|
const user = userEvent.setup();
|
|
const { refreshCache } = setupMockContext();
|
|
mockGetCachedSources
|
|
.mockResolvedValueOnce([
|
|
{
|
|
sourceCode: "mm",
|
|
displayName: "Monster Manual",
|
|
creatureCount: 300,
|
|
cachedAt: Date.now(),
|
|
},
|
|
])
|
|
.mockResolvedValue([]);
|
|
mockClearAll.mockResolvedValue(undefined);
|
|
render(<SourceManager />);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Monster Manual")).toBeInTheDocument();
|
|
});
|
|
|
|
await user.click(screen.getByRole("button", { name: "Clear All" }));
|
|
await waitFor(() => {
|
|
expect(mockClearAll).toHaveBeenCalled();
|
|
});
|
|
expect(refreshCache).toHaveBeenCalled();
|
|
});
|
|
|
|
it("individual source delete button calls clear for that source", async () => {
|
|
const user = userEvent.setup();
|
|
const { refreshCache } = setupMockContext();
|
|
mockGetCachedSources
|
|
.mockResolvedValueOnce([
|
|
{
|
|
sourceCode: "mm",
|
|
displayName: "Monster Manual",
|
|
creatureCount: 300,
|
|
cachedAt: Date.now(),
|
|
},
|
|
{
|
|
sourceCode: "vgm",
|
|
displayName: "Volo's Guide",
|
|
creatureCount: 100,
|
|
cachedAt: Date.now(),
|
|
},
|
|
])
|
|
.mockResolvedValue([
|
|
{
|
|
sourceCode: "vgm",
|
|
displayName: "Volo's Guide",
|
|
creatureCount: 100,
|
|
cachedAt: Date.now(),
|
|
},
|
|
]);
|
|
mockClearSource.mockResolvedValue(undefined);
|
|
|
|
render(<SourceManager />);
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Monster Manual")).toBeInTheDocument();
|
|
});
|
|
|
|
await user.click(
|
|
screen.getByRole("button", { name: "Remove Monster Manual" }),
|
|
);
|
|
await waitFor(() => {
|
|
expect(mockClearSource).toHaveBeenCalledWith("mm");
|
|
});
|
|
expect(refreshCache).toHaveBeenCalled();
|
|
});
|
|
});
|