first commit

This commit is contained in:
2026-07-21 16:50:58 +02:00
commit 256599626e
407 changed files with 30489 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
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 dalias", () => {
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 lalias 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 lalias 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 lalias 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");
});
});