Quand le catalogue seede ne couvre pas un ingredient, l'utilisateur pouvait etre bloque (creation manuelle) ou perdre silencieusement la ligne (import). Une ligne de recette accepte desormais `placeholderName` (texte libre) au lieu de `ingredientId` : l'API cree une ligne `Ingredient` `isPlaceholder` (cle `placeholder:<uuid>`, `displayName`, `createdById`) dans la transaction de la recette, et emet `ingredient.placeholder_created`. Ces lignes sont exclues de `GET /reference/ingredients` et de `ingredient-matcher`. Front : bouton "Ajouter << ... >>" dans l'etat vide de `IngredientPicker` (formulaire + import), badge "a completer" sur la ligne, helper `ingredientLabel` applique partout ou un libelle d'ingredient est rendu. Admin : `/admin/catalog/*` (+ page `apps/admin-web`) liste les placeholders regroupes par nom normalise, "marquer traite" (`reviewedAt`) et purge des orphelins. La promotion en vraie entree catalogue reste manuelle. Migration `ingredient_placeholder` ecrite a la main (Postgres indisponible). Suites Mocha DB-backed ecrites, non executees en session ; test pur `normalizePlaceholderName` + Cypress admin-web/web verts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
32 lines
1.5 KiB
TypeScript
32 lines
1.5 KiB
TypeScript
import { expect } from "chai";
|
||
import { normalizePlaceholderName } from "../src/modules/admin/admin-catalog.service.js";
|
||
|
||
/**
|
||
* Pure unit tests for {@link normalizePlaceholderName} — the grouping key
|
||
* that collapses near-duplicate placeholder spellings into one catalog gap.
|
||
* No database, so this file can run standalone (`mocha --no-config`) as
|
||
* well as inside the full suite.
|
||
*/
|
||
describe("normalizePlaceholderName", () => {
|
||
it("lower-cases, strips accents and collapses whitespace", () => {
|
||
expect(normalizePlaceholderName(" Piment d'Espelette ")).to.equal("piment d espelette");
|
||
expect(normalizePlaceholderName("PIMENT D’ESPELETTE")).to.equal("piment d espelette");
|
||
expect(normalizePlaceholderName("piment d espelette")).to.equal("piment d espelette");
|
||
});
|
||
|
||
it("neutralises punctuation to a single space", () => {
|
||
expect(normalizePlaceholderName("sel & poivre")).to.equal("sel poivre");
|
||
expect(normalizePlaceholderName("sel, poivre")).to.equal("sel poivre");
|
||
expect(normalizePlaceholderName("fleur-de-sel")).to.equal("fleur de sel");
|
||
});
|
||
|
||
it("keeps digits (a quantity baked into the name still distinguishes it)", () => {
|
||
expect(normalizePlaceholderName("Chocolat 70%")).to.equal("chocolat 70");
|
||
});
|
||
|
||
it("maps an all-punctuation / empty string to an empty key", () => {
|
||
expect(normalizePlaceholderName("")).to.equal("");
|
||
expect(normalizePlaceholderName(" ")).to.equal("");
|
||
expect(normalizePlaceholderName("--- ///")).to.equal("");
|
||
});
|
||
});
|