94 lines
3.1 KiB
TypeScript
94 lines
3.1 KiB
TypeScript
import { existsSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import type { InventoryItem } from "../game/lootTypes";
|
|
import {
|
|
equipmentAssetUrls,
|
|
equipmentModelUrls,
|
|
resolveEquipmentAppearance,
|
|
} from "./equipmentAppearance";
|
|
|
|
function item(itemId: number): InventoryItem {
|
|
return { itemId } as InventoryItem;
|
|
}
|
|
|
|
describe("resolveEquipmentAppearance", () => {
|
|
it("maps equipped loot to its exact client-native model attachment", () => {
|
|
const resolved = resolveEquipmentAppearance([
|
|
item(6472),
|
|
item(6447),
|
|
item(5404),
|
|
], "male");
|
|
|
|
expect(resolved.models).toEqual(expect.arrayContaining([
|
|
expect.objectContaining({ itemId: 6472, attachment: "handRight" }),
|
|
expect.objectContaining({ itemId: 6447, attachment: "shield" }),
|
|
expect.objectContaining({ itemId: 5404, attachment: "shoulderLeft" }),
|
|
expect.objectContaining({ itemId: 5404, attachment: "shoulderRight" }),
|
|
]));
|
|
});
|
|
|
|
it("does not stack two equipped models into the same hand", () => {
|
|
const resolved = resolveEquipmentAppearance([
|
|
item(6472),
|
|
item(5243),
|
|
], "male");
|
|
|
|
expect(resolved.models).toEqual([
|
|
expect.objectContaining({ itemId: 6472, attachment: "handRight" }),
|
|
]);
|
|
});
|
|
|
|
it("routes every shield model through the dedicated shield attachment", () => {
|
|
for (const itemId of [6447, 6630, 13245]) {
|
|
expect(resolveEquipmentAppearance([item(itemId)], "male").models).toEqual([
|
|
expect.objectContaining({ itemId, attachment: "shield" }),
|
|
]);
|
|
}
|
|
});
|
|
|
|
it("declares a stance-matched procedural fallback for every held model", () => {
|
|
for (const itemId of [5243, 6447, 6448, 6469, 6472, 6630, 6631, 13245]) {
|
|
const heldModels = resolveEquipmentAppearance([item(itemId)], "male").models
|
|
.filter((model) => model.attachment !== "shoulderLeft" && model.attachment !== "shoulderRight");
|
|
expect(heldModels).not.toHaveLength(0);
|
|
for (const model of heldModels) {
|
|
expect(model.fallbackKind).toBeTruthy();
|
|
expect(model.weaponStance).toBeTruthy();
|
|
}
|
|
}
|
|
});
|
|
|
|
it("uses gendered robe textures, equipment geosets, and the actual cape skin", () => {
|
|
const resolved = resolveEquipmentAppearance([
|
|
item(6465),
|
|
item(5970),
|
|
item(6632),
|
|
], "female");
|
|
|
|
expect(resolved.bodyLayers.map((layer) => layer.url)).toContain(
|
|
"/assets/equipment/wow335a-v1/textures/6465-robe-of-the-moccasin/torso-upper-female.png",
|
|
);
|
|
expect(resolved.geosets).toMatchObject({
|
|
Sleeves: 3,
|
|
Chest: 1,
|
|
Trousers: 2,
|
|
Gloves: 2,
|
|
Cloak: 6,
|
|
});
|
|
expect(resolved.cloakUrl).toContain("6632-feyscale-cloak/cape.png");
|
|
});
|
|
|
|
it("ships every referenced GLB in the public asset tree", () => {
|
|
for (const url of equipmentModelUrls()) {
|
|
expect(existsSync(resolve("public", url.replace(/^\//, "")))).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("ships every referenced client-native model and texture asset", () => {
|
|
for (const url of equipmentAssetUrls()) {
|
|
expect(existsSync(resolve("public", url.replace(/^\//, "")))).toBe(true);
|
|
}
|
|
});
|
|
});
|