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); } }); });