import { describe, expect, it } from "vitest"; import { aliasToIconPx, buildIngredientCoverSprite, findAliasForIconPx, foldAccents, INGREDIENT_SPRITE_CELL, isPreferredVariantAlias, normalizeIngredientAliasKey, parseIngredientSpritesBentext, resolveIconPxForIngredientName } from "./sprites.mjs"; describe("parseIngredientSpritesBentext", () => { it("parse les blocs --- avec coordonnées et alias (| = synonymes)", () => { const raw = `2 0 eau|vinaigre de riz|Vinaigre blanc --- 0 4 carnet --- 8 1 fromage `; const { byAlias, preferredAliases, csv } = parseIngredientSpritesBentext(raw); // eau → (0, 64) ; carnet → (128, 0) ; fromage → (32, 256) — ligne 8 = preferred expect(byAlias.eau).toEqual({ x: 0, y: 64, X: 0, Y: 64 }); expect(byAlias["vinaigre de riz"]).toEqual({ x: 0, y: 64, X: 0, Y: 64 }); expect(byAlias["vinaigre blanc"]).toEqual({ x: 0, y: 64, X: 0, Y: 64 }); expect(byAlias.carnet).toEqual({ x: 128, y: 0, X: 128, Y: 0 }); expect(byAlias.fromage).toEqual({ x: 32, y: 256, X: 32, Y: 256 }); expect(preferredAliases.has("fromage")).toBe(true); expect(preferredAliases.has("eau")).toBe(false); expect(preferredAliases.has("carnet")).toBe(false); expect(csv).toContain("carnet"); expect(csv).toContain("fromage"); }); it("ignore un bloc sans coordonnées valides", () => { const raw = `not_a_coord eau --- 0 0 couvert `; const { byAlias } = parseIngredientSpritesBentext(raw); expect(byAlias.eau).toBeUndefined(); expect(byAlias.couvert).toEqual({ x: 0, y: 0, X: 0, Y: 0 }); }); it("conserve la première définition en cas de conflit d’alias", () => { const raw = `0 0 eau --- 1 0 eau `; const { byAlias } = parseIngredientSpritesBentext(raw); expect(byAlias.eau).toEqual({ x: 0, y: 0, X: 0, Y: 0 }); }); it("retire le BOM et tolère CRLF entre blocs", () => { const raw = "\uFEFF0 0\r\ncouvert\r\n---\r\n0 1\r\nassiette\r\n"; const { byAlias } = parseIngredientSpritesBentext(raw); expect(byAlias.couvert).toBeDefined(); expect(byAlias.assiette).toBeDefined(); }); }); describe("resolveIconPxForIngredientName", () => { const byAlias = { riz: { x: 0, y: 0 }, "riz gluant": { x: 32, y: 0 }, avocat: { x: 64, y: 0 } }; it("trouve l’alias direct", () => { expect(resolveIconPxForIngredientName("riz", byAlias)).toEqual({ x: 0, y: 0 }); }); it("recherche sans accents", () => { expect(resolveIconPxForIngredientName("AVOCAT", byAlias)).toEqual({ x: 64, y: 0 }); }); it("retombe sur l’alias le plus long en inclusion", () => { expect(resolveIconPxForIngredientName("Riz gluant blanc", byAlias)).toEqual({ x: 32, y: 0 }); }); it("retourne null si pas de correspondance", () => { expect(resolveIconPxForIngredientName("introuvable", byAlias)).toBeNull(); }); it("gère un nom composé avec / ou et (segments)", () => { const local = { "eau": { x: 0, y: 0 }, "sel": { x: 32, y: 0 } }; expect(resolveIconPxForIngredientName("Sel / eau", local)).toEqual({ x: 32, y: 0 }); }); }); describe("aliasToIconPx", () => { it("lit les coordonnées (x, y) ou (X, Y)", () => { expect(aliasToIconPx({ a: { x: 1, y: 2 } }, "a")).toEqual({ x: 1, y: 2 }); expect(aliasToIconPx({ a: { X: 3, Y: 4 } }, "a")).toEqual({ x: 3, y: 4 }); }); it("retourne null si alias ou coords absentes", () => { expect(aliasToIconPx({}, "x")).toBeNull(); expect(aliasToIconPx({ a: { x: "a", y: 2 } }, "a")).toBeNull(); }); }); describe("findAliasForIconPx", () => { it("renvoie l’alias correspondant aux coordonnées exactes", () => { const byAlias = { a: { x: 0, y: 0 }, b: { x: 32, y: 0 } }; expect(findAliasForIconPx(byAlias, { x: 32, y: 0 })).toBe("b"); expect(findAliasForIconPx(byAlias, { x: 100, y: 0 })).toBeNull(); }); }); describe("isPreferredVariantAlias", () => { it("matche case-insensitive et tolère un set vide", () => { const preferred = new Set(["fromage", "tomate"]); expect(isPreferredVariantAlias(preferred, "Fromage")).toBe(true); expect(isPreferredVariantAlias(preferred, "autre")).toBe(false); expect(isPreferredVariantAlias(new Set(), "fromage")).toBe(false); expect(isPreferredVariantAlias(preferred, "")).toBe(false); }); }); describe("buildIngredientCoverSprite", () => { const sheet = { width: 512, height: 1024 }; it("renvoie null si coords ou sheet invalides", () => { expect(buildIngredientCoverSprite(null, sheet)).toBeNull(); expect(buildIngredientCoverSprite({ x: 0, y: 0 }, null)).toBeNull(); }); it("calcule col/row depuis les pixels et fige sheet/cell", () => { const result = buildIngredientCoverSprite({ x: 64, y: 256 }, sheet); expect(result).toEqual({ sheet: "/ingredients.png", col: 2, row: 8, cell: INGREDIENT_SPRITE_CELL, sheetWidth: 512, sheetHeight: 1024 }); }); }); describe("normalizeIngredientAliasKey & foldAccents", () => { it("normalise espaces et casse", () => { expect(normalizeIngredientAliasKey(" Riz gluant ")).toBe("riz gluant"); }); it("plie les accents", () => { expect(foldAccents("Crème pâtissière")).toBe("creme patissiere"); }); });