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>
104 lines
3.3 KiB
TypeScript
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([]);
|
|
});
|
|
});
|