Files
initiative/apps/web/src/adapters/__tests__/pf2e-bestiary-index-adapter.test.ts
Lukas 1c107a500b
All checks were successful
CI / check (push) Successful in 2m25s
CI / build-image (push) Successful in 23s
Switch PF2e data source from Pf2eTools to Foundry VTT PF2e
Replace the stagnant Pf2eTools bestiary with Foundry VTT PF2e system
data (github.com/foundryvtt/pf2e, v13-dev branch). This gives us 4,355
remaster-era creatures across 49 sources including Monster Core 1+2 and
all adventure paths.

Changes:
- Rewrite index generation script to walk Foundry pack directories
- Rewrite PF2e normalization adapter for Foundry JSON shape (system.*
  fields, items[] for attacks/abilities/spells)
- Add stripFoundryTags utility for Foundry HTML + enrichment syntax
- Implement multi-file source fetching (one request per creature file)
- Add spellcasting section to PF2e stat block (ranked spells + cantrips)
- Add saveConditional and hpDetails to PF2e domain type and stat block
- Add size and rarity to PF2e trait tags
- Filter redundant glossary abilities (healing when in hp.details,
  spell mechanic reminders, allSaves duplicates)
- Add PF2e stat block component tests (22 tests)
- Bump IndexedDB cache version to 5 for clean migration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:05:00 +02:00

104 lines
3.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
const PACK_DIR_PREFIX = /^pathfinder-monster-core\//;
const JSON_EXTENSION = /\.json$/;
import {
getAllPf2eSourceCodes,
getCreaturePathsForSource,
getDefaultPf2eFetchUrl,
getPf2eSourceDisplayName,
loadPf2eBestiaryIndex,
} from "../pf2e-bestiary-index-adapter.js";
describe("loadPf2eBestiaryIndex", () => {
it("returns an object with sources and creatures", () => {
const index = loadPf2eBestiaryIndex();
expect(index.sources).toBeDefined();
expect(index.creatures).toBeDefined();
expect(Array.isArray(index.creatures)).toBe(true);
});
it("creatures have the expected PF2e shape", () => {
const index = loadPf2eBestiaryIndex();
expect(index.creatures.length).toBeGreaterThan(0);
const first = index.creatures[0];
expect(first).toHaveProperty("name");
expect(first).toHaveProperty("source");
expect(first).toHaveProperty("level");
expect(first).toHaveProperty("ac");
expect(first).toHaveProperty("hp");
expect(first).toHaveProperty("perception");
expect(first).toHaveProperty("size");
expect(first).toHaveProperty("type");
});
it("contains a substantial number of creatures", () => {
const index = loadPf2eBestiaryIndex();
expect(index.creatures.length).toBeGreaterThan(2500);
});
it("creatures have size and type populated", () => {
const index = loadPf2eBestiaryIndex();
const withSize = index.creatures.filter((c) => c.size !== "");
const withType = index.creatures.filter((c) => c.type !== "");
expect(withSize.length).toBeGreaterThan(index.creatures.length * 0.9);
expect(withType.length).toBeGreaterThan(index.creatures.length * 0.8);
});
it("returns the same cached instance on subsequent calls", () => {
const a = loadPf2eBestiaryIndex();
const b = loadPf2eBestiaryIndex();
expect(a).toBe(b);
});
});
describe("getAllPf2eSourceCodes", () => {
it("returns all keys from the index sources", () => {
const codes = getAllPf2eSourceCodes();
const index = loadPf2eBestiaryIndex();
expect(codes).toEqual(Object.keys(index.sources));
});
});
describe("getDefaultPf2eFetchUrl", () => {
it("returns Foundry VTT PF2e base URL", () => {
const url = getDefaultPf2eFetchUrl("pathfinder-monster-core");
expect(url).toBe(
"https://raw.githubusercontent.com/foundryvtt/pf2e/v13-dev/packs/pf2e/",
);
});
it("normalizes custom base URL with trailing slash", () => {
const url = getDefaultPf2eFetchUrl(
"pathfinder-monster-core",
"https://example.com/pf2e",
);
expect(url).toBe("https://example.com/pf2e/");
});
});
describe("getPf2eSourceDisplayName", () => {
it("returns display name for a known source", () => {
const name = getPf2eSourceDisplayName("pathfinder-monster-core");
expect(name).toBe("Monster Core");
});
it("falls back to source code for unknown source", () => {
expect(getPf2eSourceDisplayName("UNKNOWN")).toBe("UNKNOWN");
});
});
describe("getCreaturePathsForSource", () => {
it("returns file paths for a known source", () => {
const paths = getCreaturePathsForSource("pathfinder-monster-core");
expect(paths.length).toBeGreaterThan(100);
expect(paths[0]).toMatch(PACK_DIR_PREFIX);
expect(paths[0]).toMatch(JSON_EXTENSION);
});
it("returns empty array for unknown source", () => {
expect(getCreaturePathsForSource("nonexistent")).toEqual([]);
});
});