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

@@ -8,11 +8,7 @@ import {
normalizeBestiary,
setSourceDisplayNames,
} from "../adapters/bestiary-adapter.js";
import * as bestiaryCache from "../adapters/bestiary-cache.js";
import {
getSourceDisplayName,
loadBestiaryIndex,
} from "../adapters/bestiary-index-adapter.js";
import { useAdapters } from "../contexts/adapter-context.js";
export interface SearchResult extends BestiaryIndexEntry {
readonly sourceDisplayName: string;
@@ -32,13 +28,14 @@ interface BestiaryHook {
}
export function useBestiary(): BestiaryHook {
const { bestiaryCache, bestiaryIndex } = useAdapters();
const [isLoaded, setIsLoaded] = useState(false);
const [creatureMap, setCreatureMap] = useState(
() => new Map<CreatureId, Creature>(),
);
useEffect(() => {
const index = loadBestiaryIndex();
const index = bestiaryIndex.loadIndex();
setSourceDisplayNames(index.sources as Record<string, string>);
if (index.creatures.length > 0) {
setIsLoaded(true);
@@ -47,21 +44,24 @@ export function useBestiary(): BestiaryHook {
void bestiaryCache.loadAllCachedCreatures().then((map) => {
setCreatureMap(map);
});
}, []);
}, [bestiaryCache, bestiaryIndex]);
const search = useCallback((query: string): SearchResult[] => {
if (query.length < 2) return [];
const lower = query.toLowerCase();
const index = loadBestiaryIndex();
return index.creatures
.filter((c) => c.name.toLowerCase().includes(lower))
.sort((a, b) => a.name.localeCompare(b.name))
.slice(0, 10)
.map((c) => ({
...c,
sourceDisplayName: getSourceDisplayName(c.source),
}));
}, []);
const search = useCallback(
(query: string): SearchResult[] => {
if (query.length < 2) return [];
const lower = query.toLowerCase();
const index = bestiaryIndex.loadIndex();
return index.creatures
.filter((c) => c.name.toLowerCase().includes(lower))
.sort((a, b) => a.name.localeCompare(b.name))
.slice(0, 10)
.map((c) => ({
...c,
sourceDisplayName: bestiaryIndex.getSourceDisplayName(c.source),
}));
},
[bestiaryIndex],
);
const getCreature = useCallback(
(id: CreatureId): Creature | undefined => {
@@ -74,7 +74,7 @@ export function useBestiary(): BestiaryHook {
(sourceCode: string): Promise<boolean> => {
return bestiaryCache.isSourceCached(sourceCode);
},
[],
[bestiaryCache],
);
const fetchAndCacheSource = useCallback(
@@ -87,7 +87,7 @@ export function useBestiary(): BestiaryHook {
}
const json = await response.json();
const creatures = normalizeBestiary(json);
const displayName = getSourceDisplayName(sourceCode);
const displayName = bestiaryIndex.getSourceDisplayName(sourceCode);
await bestiaryCache.cacheSource(sourceCode, displayName, creatures);
setCreatureMap((prev) => {
const next = new Map(prev);
@@ -97,14 +97,14 @@ export function useBestiary(): BestiaryHook {
return next;
});
},
[],
[bestiaryCache, bestiaryIndex],
);
const uploadAndCacheSource = useCallback(
async (sourceCode: string, jsonData: unknown): Promise<void> => {
// biome-ignore lint/suspicious/noExplicitAny: raw JSON shape varies
const creatures = normalizeBestiary(jsonData as any);
const displayName = getSourceDisplayName(sourceCode);
const displayName = bestiaryIndex.getSourceDisplayName(sourceCode);
await bestiaryCache.cacheSource(sourceCode, displayName, creatures);
setCreatureMap((prev) => {
const next = new Map(prev);
@@ -114,13 +114,13 @@ export function useBestiary(): BestiaryHook {
return next;
});
},
[],
[bestiaryCache, bestiaryIndex],
);
const refreshCache = useCallback(async (): Promise<void> => {
const map = await bestiaryCache.loadAllCachedCreatures();
setCreatureMap(map);
}, []);
}, [bestiaryCache]);
return {
search,