Files
healer-man/src/avatar/runewakerAvatarManifest.test.ts
T
2026-08-30 14:16:56 -04:00

62 lines
2.4 KiB
TypeScript

import { readFile, stat } from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import type { AvatarManifest } from "./characterManifest";
interface RunewakerGlbDocument {
readonly animations?: readonly { readonly name?: string }[];
readonly nodes?: readonly { readonly name?: string }[];
}
function readGlbDocument(glb: Buffer): RunewakerGlbDocument {
expect(glb.readUInt32LE(0)).toBe(0x46546c67);
const jsonLength = glb.readUInt32LE(12);
return JSON.parse(
glb.subarray(20, 20 + jsonLength).toString("utf8").trimEnd(),
) as RunewakerGlbDocument;
}
const manifestFile = path.resolve("public/assets/characters/runewaker-v1/manifest.json");
describe("runewaker-v1 playable avatar pack", () => {
it("ships all five authentic race/gender rigs and no female Dwarf", async () => {
const manifest = JSON.parse(await readFile(manifestFile, "utf8")) as AvatarManifest;
expect(Object.keys(manifest.entries).sort()).toEqual([
"rom-dwarf:male",
"rom-elf:female",
"rom-elf:male",
"rom-human:female",
"rom-human:male",
]);
expect(manifest.entries["rom-dwarf:female"]).toBeUndefined();
for (const entry of Object.values(manifest.entries)) {
expect(entry.rendererStrategy).toMatchObject({
kind: "runewaker-modular",
nativeBodyRig: true,
sharedSkeleton: true,
starterPaperdoll: true,
});
expect(entry.animations).toMatchObject({
idle: "Stand - stand_idle01",
walk: "Walk - walk_forward",
run: "Run - run_forward",
attack: "Attack - unarmed_attack01",
cast: "Cast - cast01",
});
const model = path.resolve("public", entry.model.replace(/^\//, ""));
expect((await stat(model)).size).toBe(entry.bytes);
const document = readGlbDocument(await readFile(model));
const animationNames = new Set(
document.animations?.map((animation) => animation.name ?? "") ?? [],
);
for (const animationName of ["Idle - 1h_idle", "Idle - 2h_idle", "Idle - bow_idle"]) {
expect(animationNames.has(animationName), `${entry.model} ${animationName}`).toBe(true);
}
const nodeNames = new Set(document.nodes?.map((node) => node.name ?? "") ?? []);
for (const nodeName of ["p_left_hand", "p_right_hand", "p_hand_shield"]) {
expect(nodeNames.has(nodeName), `${entry.model} ${nodeName}`).toBe(true);
}
}
});
});