Introduce adapter injection and migrate test suite
All checks were successful
CI / check (push) Successful in 2m13s
CI / build-image (push) Has been skipped

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>
This commit is contained in:
Lukas
2026-04-01 23:55:45 +02:00
parent 228c1c667f
commit 2c643cc98b
42 changed files with 1879 additions and 1190 deletions

View File

@@ -4,6 +4,8 @@ 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 { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js";
import { AdapterProvider } from "../../contexts/adapter-context.js";
import { BulkImportPrompt } from "../bulk-import-prompt.js";
const THREE_SOURCES_REGEX = /3 sources/;
@@ -28,6 +30,10 @@ let mockImportState = {
failed: 0,
};
// Uses context mocks because the bulk import state machine (idle → loading →
// complete → partial-failure) is impractical to drive through user interactions
// without real network calls. Consider migrating if adapter injection expands
// to cover these state transitions.
vi.mock("../../contexts/bestiary-context.js", () => ({
useBestiaryContext: () => ({
fetchAndCacheSource: mockFetchAndCacheSource,
@@ -50,12 +56,23 @@ vi.mock("../../contexts/side-panel-context.js", () => ({
}),
}));
vi.mock("../../adapters/bestiary-index-adapter.js", () => ({
getAllSourceCodes: () => ["MM", "VGM", "XGE"],
getDefaultFetchUrl: () => "",
getSourceDisplayName: (code: string) => code,
loadBestiaryIndex: () => ({ sources: {}, creatures: [] }),
}));
function createAdaptersWithSources() {
const adapters = createTestAdapters();
adapters.bestiaryIndex = {
...adapters.bestiaryIndex,
getAllSourceCodes: () => ["MM", "VGM", "XGE"],
};
return adapters;
}
function renderWithAdapters() {
const adapters = createAdaptersWithSources();
return render(
<AdapterProvider adapters={adapters}>
<BulkImportPrompt />
</AdapterProvider>,
);
}
describe("BulkImportPrompt", () => {
afterEach(() => {
@@ -64,7 +81,7 @@ describe("BulkImportPrompt", () => {
});
it("idle: shows base URL input, source count, Load All button", () => {
render(<BulkImportPrompt />);
renderWithAdapters();
expect(screen.getByText(THREE_SOURCES_REGEX)).toBeInTheDocument();
expect(screen.getByDisplayValue(GITHUB_URL_REGEX)).toBeInTheDocument();
expect(
@@ -74,7 +91,7 @@ describe("BulkImportPrompt", () => {
it("idle: clearing URL disables the button", async () => {
const user = userEvent.setup();
render(<BulkImportPrompt />);
renderWithAdapters();
const input = screen.getByDisplayValue(GITHUB_URL_REGEX);
await user.clear(input);
@@ -83,7 +100,7 @@ describe("BulkImportPrompt", () => {
it("idle: clicking Load All calls startImport with URL", async () => {
const user = userEvent.setup();
render(<BulkImportPrompt />);
renderWithAdapters();
await user.click(screen.getByRole("button", { name: "Load All" }));
expect(mockStartImport).toHaveBeenCalledWith(
@@ -101,7 +118,7 @@ describe("BulkImportPrompt", () => {
completed: 3,
failed: 1,
};
render(<BulkImportPrompt />);
renderWithAdapters();
expect(screen.getByText(LOADING_PROGRESS_REGEX)).toBeInTheDocument();
});
@@ -112,7 +129,7 @@ describe("BulkImportPrompt", () => {
completed: 10,
failed: 0,
};
render(<BulkImportPrompt />);
renderWithAdapters();
expect(screen.getByText("All sources loaded")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Done" })).toBeInTheDocument();
});
@@ -125,7 +142,7 @@ describe("BulkImportPrompt", () => {
failed: 0,
};
const user = userEvent.setup();
render(<BulkImportPrompt />);
renderWithAdapters();
await user.click(screen.getByRole("button", { name: "Done" }));
expect(mockDismissPanel).toHaveBeenCalled();
@@ -139,7 +156,7 @@ describe("BulkImportPrompt", () => {
completed: 7,
failed: 3,
};
render(<BulkImportPrompt />);
renderWithAdapters();
expect(screen.getByText(SEVEN_OF_TEN_REGEX)).toBeInTheDocument();
expect(screen.getByText(THREE_FAILED_REGEX)).toBeInTheDocument();
});