Fix "undefined" in PF2e stat block weaknesses/resistances
All checks were successful
CI / check (push) Successful in 2m23s
CI / build-image (push) Successful in 30s

Some PF2e creatures (e.g. Giant Mining Bee) have qualitative
weaknesses without a numeric amount, causing "undefined" to
render in the stat block. Handle missing amounts gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-04-07 12:06:22 +02:00
parent e62c49434c
commit 8dbff66ce1
2 changed files with 109 additions and 14 deletions

View File

@@ -0,0 +1,91 @@
import { describe, expect, it } from "vitest";
import { normalizePf2eBestiary } from "../pf2e-bestiary-adapter.js";
function minimalCreature(defenses?: Record<string, unknown>) {
return {
name: "Test Creature",
source: "TST",
defenses,
};
}
describe("normalizePf2eBestiary", () => {
describe("weaknesses formatting", () => {
it("formats weakness with numeric amount", () => {
const [creature] = normalizePf2eBestiary({
creature: [
minimalCreature({
weaknesses: [{ name: "fire", amount: 5 }],
}),
],
});
expect(creature.weaknesses).toBe("Fire 5");
});
it("formats weakness without amount (qualitative)", () => {
const [creature] = normalizePf2eBestiary({
creature: [
minimalCreature({
weaknesses: [{ name: "smoke susceptibility" }],
}),
],
});
expect(creature.weaknesses).toBe("Smoke susceptibility");
});
it("formats weakness with note and amount", () => {
const [creature] = normalizePf2eBestiary({
creature: [
minimalCreature({
weaknesses: [
{ name: "cold iron", amount: 5, note: "except daggers" },
],
}),
],
});
expect(creature.weaknesses).toBe("Cold iron 5 (except daggers)");
});
it("formats weakness with note but no amount", () => {
const [creature] = normalizePf2eBestiary({
creature: [
minimalCreature({
weaknesses: [{ name: "smoke susceptibility", note: "see below" }],
}),
],
});
expect(creature.weaknesses).toBe("Smoke susceptibility (see below)");
});
it("returns undefined when no weaknesses", () => {
const [creature] = normalizePf2eBestiary({
creature: [minimalCreature({})],
});
expect(creature.weaknesses).toBeUndefined();
});
});
describe("resistances formatting", () => {
it("formats resistance without amount", () => {
const [creature] = normalizePf2eBestiary({
creature: [
minimalCreature({
resistances: [{ name: "physical" }],
}),
],
});
expect(creature.resistances).toBe("Physical");
});
it("formats resistance with amount", () => {
const [creature] = normalizePf2eBestiary({
creature: [
minimalCreature({
resistances: [{ name: "fire", amount: 10 }],
}),
],
});
expect(creature.resistances).toBe("Fire 10");
});
});
});

View File

@@ -40,8 +40,8 @@ interface RawDefenses {
}; };
hp?: { hp?: number }[]; hp?: { hp?: number }[];
immunities?: (string | { name: string })[]; immunities?: (string | { name: string })[];
resistances?: { amount: number; name: string; note?: string }[]; resistances?: { amount?: number; name: string; note?: string }[];
weaknesses?: { amount: number; name: string; note?: string }[]; weaknesses?: { amount?: number; name: string; note?: string }[];
} }
interface RawAbility { interface RawAbility {
@@ -150,31 +150,35 @@ function formatImmunities(
function formatResistances( function formatResistances(
resistances: resistances:
| readonly { amount: number; name: string; note?: string }[] | readonly { amount?: number; name: string; note?: string }[]
| undefined, | undefined,
): string | undefined { ): string | undefined {
if (!resistances || resistances.length === 0) return undefined; if (!resistances || resistances.length === 0) return undefined;
return resistances return resistances
.map((r) => .map((r) => {
r.note const base =
? `${capitalize(r.name)} ${r.amount} (${r.note})` r.amount == null
: `${capitalize(r.name)} ${r.amount}`, ? capitalize(r.name)
) : `${capitalize(r.name)} ${r.amount}`;
return r.note ? `${base} (${r.note})` : base;
})
.join(", "); .join(", ");
} }
function formatWeaknesses( function formatWeaknesses(
weaknesses: weaknesses:
| readonly { amount: number; name: string; note?: string }[] | readonly { amount?: number; name: string; note?: string }[]
| undefined, | undefined,
): string | undefined { ): string | undefined {
if (!weaknesses || weaknesses.length === 0) return undefined; if (!weaknesses || weaknesses.length === 0) return undefined;
return weaknesses return weaknesses
.map((w) => .map((w) => {
w.note const base =
? `${capitalize(w.name)} ${w.amount} (${w.note})` w.amount == null
: `${capitalize(w.name)} ${w.amount}`, ? capitalize(w.name)
) : `${capitalize(w.name)} ${w.amount}`;
return w.note ? `${base} (${w.note})` : base;
})
.join(", "); .join(", ");
} }