Files
healer-man/src/app/characterCatalog.test.ts
T

121 lines
5.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
CLASS_ICON_BASENAME_BY_ID,
CLASSES,
COA_CLASS_IDS,
PLAYABLE_RACES,
RACES,
classIconUrl,
classesForRace,
clampAppearance,
contentCategoryForClass,
normalizeCharacterName,
randomAppearance,
supportedGenders,
totalRaceClassCombinations,
validateCharacterName,
} from "./characterCatalog";
import { CONTENT_CATEGORIES } from "./contentCategories";
describe("wow335a character catalog", () => {
it("limits normal play to the live-verified race roster", () => {
expect(PLAYABLE_RACES.filter((race) => race.categoryId !== "rom").map((race) => race.id)).toEqual([
"human",
"orc",
"dwarf",
"night-elf",
"undead",
"tauren",
"gnome",
"troll",
"blood-elf",
"draenei",
"goblin",
]);
});
it("matches the extracted Ascension playable catalog", () => {
const wowRaces = RACES.filter((race) => race.categoryId !== "rom");
const azerothClasses = CLASSES.filter((characterClass) => characterClass.categoryId !== "rom");
const wowClasses = CLASSES.filter((characterClass) => contentCategoryForClass(characterClass.id) === "wow");
const coaClasses = CLASSES.filter((characterClass) => contentCategoryForClass(characterClass.id) === "coa");
expect(wowRaces).toHaveLength(27);
expect(azerothClasses).toHaveLength(31);
expect(wowClasses).toHaveLength(10);
expect(coaClasses).toHaveLength(21);
expect(COA_CLASS_IDS).toHaveLength(21);
expect(wowRaces.reduce((total, race) => total + classesForRace(race.id).length, 0)).toBe(740);
expect(totalRaceClassCombinations()).toBe(758);
expect(wowRaces.map((race) => race.name)).toEqual([
"Human", "Orc", "Dwarf", "Night Elf", "Undead",
"Tauren", "Gnome", "Troll", "Blood Elf", "Draenei",
"Goblin", "Zandalari Troll", "Kul Tiran", "Vulpera", "Pandaren",
"Thin Human", "Fel Orc", "Broken", "Skeleton", "Vrykul", "Tuskarr",
"Forest Troll", "Taunka", "Northrend Skeleton", "Ice Troll", "Earthen",
"Human Cultist",
]);
expect(COA_CLASS_IDS.map((id) => coaClasses.find((entry) => entry.id === id)?.name)).toEqual([
"Barbarian", "Witch Doctor", "Felsworn", "Witch Hunter", "Stormbringer",
"Knight of Xoroth", "Guardian", "Templar", "Bloodmage", "Ranger",
"Chronomancer", "Necromancer", "Pyromancer", "Cultist", "Starcaller",
"Sun Cleric", "Tinker", "Venomancer", "Reaper", "Primalist", "Runemaster",
]);
});
it("separates default WoW, custom CoA, and RoM character creation categories", () => {
expect(CONTENT_CATEGORIES.map((category) => category.id)).toEqual(["wow", "rom", "coa"]);
expect(classesForRace("human", "wow").map((entry) => entry.id)).toContain("priest");
expect(classesForRace("human", "wow").map((entry) => entry.id)).not.toContain("barbarian");
expect(classesForRace("human", "coa").map((entry) => entry.id)).toContain("barbarian");
expect(classesForRace("human", "coa").map((entry) => entry.id)).not.toContain("priest");
expect(classesForRace("rom-human", "rom").map((entry) => entry.id)).toContain("rom-priest");
});
it("applies the race eligibility rows from CharBaseInfo.dbc", () => {
expect(classesForRace("human", "coa").map((entry) => entry.id)).toContain("barbarian");
expect(classesForRace("human", "coa").map((entry) => entry.id)).not.toContain("felsworn");
expect(classesForRace("draenei", "coa").map((entry) => entry.id)).toContain("felsworn");
expect(classesForRace("draenei", "coa").map((entry) => entry.id)).toContain("knight-of-xoroth");
expect(classesForRace("troll", "coa").map((entry) => entry.id)).toContain("venomancer");
expect(classesForRace("troll", "coa").map((entry) => entry.id)).not.toContain("knight-of-xoroth");
expect(classesForRace("goblin", "wow")).toHaveLength(10);
expect(classesForRace("goblin", "coa")).toHaveLength(21);
expect(classesForRace("human-cultist")).toHaveLength(31);
expect(supportedGenders("vrykul")).toEqual(["male"]);
expect(supportedGenders("earthen")).toEqual(["male", "female"]);
});
it("maps every class to its extracted Ascension class icon", () => {
const wowClasses = CLASSES.filter((characterClass) => characterClass.categoryId !== "rom");
expect(new Set(wowClasses.map((definition) => CLASS_ICON_BASENAME_BY_ID[definition.id])).size)
.toBe(wowClasses.length);
expect(CLASS_ICON_BASENAME_BY_ID.felsworn).toBe("classicon128_demonhunter");
expect(CLASS_ICON_BASENAME_BY_ID["knight-of-xoroth"]).toBe("classicon128_fleshwarden");
expect(CLASS_ICON_BASENAME_BY_ID.venomancer).toBe("classicon128_prophet");
expect(classIconUrl("runemaster")).toBe("/assets/ui/spells/classicon128_spiritmage.png");
});
it("preserves race and gender-specific customization counts", () => {
const tauren = RACES.find((race) => race.id === "tauren")!;
expect(tauren.labels.male.hairStyle).toBe("Horn Style");
expect(tauren.counts.male).toMatchObject({ skinColor: 19, hairColor: 3, feature: 7 });
expect(RACES.find((race) => race.id === "night-elf")!.labels.female.feature).toBe("Markings");
});
it("keeps generated and clamped appearance indexes within the selected model ranges", () => {
expect(randomAppearance("human", "female", () => 0.999)).toEqual({
skinColor: 9, face: 14, hairStyle: 23, hairColor: 9, feature: 6,
});
expect(clampAppearance("gnome", "male", {
skinColor: -2, face: 99, hairStyle: 4.7, hairColor: 20, feature: 2,
})).toEqual({ skinColor: 0, face: 6, hairStyle: 4, hairColor: 8, feature: 2 });
});
it("applies the original 2-12 letter naming boundary", () => {
expect(normalizeCharacterName(" hEAL3R!! ")).toBe("Healr");
expect(validateCharacterName("A")).toMatch(/2 to 12/);
expect(validateCharacterName("Healer-Man")).toMatch(/letters only/);
expect(validateCharacterName("Serenity")).toBeNull();
});
});