Le catalogue de référence (diets/allergènes/ingrédients) était écrit en
français dans reference-seed-data.ts, avec une table de correspondance
séparée (catalog-en-keys.ts, 666 lignes, ~563 entrées) traduisant chaque
libellé français vers une clé anglaise snake_case, elle-même utilisée pour
peupler la colonne `key` en base et régénérer translation.json. Décision :
remplacer par un authoring 100% anglais camelCase directement dans le seed
— plus de détour, plus de table de correspondance.
- `Ingredient.name`/`allergenNames`/`dietNames` → `uid`/`allergenUids`/
`dietUids`, valeurs en camelCase directement (ex: "Tomate" → "tomato",
"Fruits à coque" → "treeNuts").
- `IngredientCategory`/`IngredientSubcategory` (enums Prisma) renommés du
français SCREAMING_SNAKE_CASE (`PRODUITS_FRAIS`, `LEGUMES`...) vers
l'anglais camelCase (`freshProduce`, `vegetables`...) — même mécanique
de migration que le renommage d'enum précédent
(20260818113250_ingredient_taxonomy_rework) : nouvelle colonne avec
valeur par défaut sûre, jamais de cast direct (aucune valeur commune
entre ancien et nouvel enum), seedReferenceData() corrige chaque ligne
au démarrage suivant.
- Migration `20260819180000_catalog_camel_case_uids` : renomme les clés
existantes (diet/category/ingredients, même mécanique que
20260818193000_catalog_keys_to_english) + swap des deux enums. Un cas
particulier corrigé à la main : "sesame_seeds" était à la fois la clé
d'un allergène (Category) et d'un ingrédient qui se référence lui-même
("Graines de sésame") — les deux tables ont besoin de leur propre
UPDATE.
- `catalog-en-keys.ts`, `slugify.ts`, `generate-catalog-i18n.ts`,
`validate-catalog-en-keys.ts` — supprimés (plus de raison d'être).
Conséquence assumée : `translation.json` n'est plus régénéré
automatiquement, c'est désormais la seule source du texte FR, tenue à
jour à la main en parallèle du uid (même clé qui les relie).
- `packages/shared/src/types/reference.ts`, `apps/web`'s
`ingredient-icons.tsx` (CATEGORY_ICON/SUBCATEGORY_ICON), fixtures
Cypress codées en dur — mis à jour avec les nouveaux noms.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
159 lines
4.9 KiB
TypeScript
159 lines
4.9 KiB
TypeScript
import { type DataTable, Given, Then, When } from "@badeball/cypress-cucumber-preprocessor";
|
|
|
|
const tomato = {
|
|
id: 1,
|
|
key: "tomato",
|
|
icon: "VEGETABLE",
|
|
category: "freshProduce",
|
|
subcategory: "vegetables",
|
|
allergens: [],
|
|
diets: [{ id: 2, key: "vegetarian" }],
|
|
};
|
|
const egg = {
|
|
id: 2,
|
|
key: "egg",
|
|
icon: "EGG",
|
|
category: "dairyAndCheese",
|
|
subcategory: "eggs",
|
|
allergens: [{ id: 1, key: "eggs", kind: "ALLERGY" }],
|
|
diets: [],
|
|
};
|
|
const carrot = {
|
|
id: 3,
|
|
key: "carrot",
|
|
icon: "VEGETABLE",
|
|
category: "freshProduce",
|
|
subcategory: "vegetables",
|
|
allergens: [],
|
|
diets: [{ id: 2, key: "vegetarian" }],
|
|
};
|
|
|
|
const diets = [
|
|
{ id: 1, key: "omnivore" },
|
|
{ id: 2, key: "vegetarian" },
|
|
];
|
|
|
|
Given("the ingredient and diet catalog is available", () => {
|
|
cy.intercept("GET", "**/reference/ingredients", { statusCode: 200, body: [tomato, egg, carrot] });
|
|
cy.intercept("GET", "**/reference/diets", { statusCode: 200, body: diets });
|
|
});
|
|
|
|
Given("creating the recipe will succeed and return id {int}", (id: number) => {
|
|
cy.intercept("POST", "**/recipes", { statusCode: 201, body: { id } }).as("createRecipe");
|
|
});
|
|
|
|
When("I visit the new recipe form without a secure random UUID", () => {
|
|
cy.visit("/recettes/nouvelle", {
|
|
onBeforeLoad(win) {
|
|
Object.defineProperty(win.crypto, "randomUUID", {
|
|
value: undefined,
|
|
configurable: true,
|
|
});
|
|
},
|
|
});
|
|
});
|
|
|
|
When("I search the ingredient picker for {string}", (text: string) => {
|
|
cy.get("input[placeholder='Rechercher un ingrédient…']").type(text);
|
|
});
|
|
|
|
When("I select the ingredient {string} from the picker", (name: string) => {
|
|
cy.contains(".ingredient-picker__card", name).click();
|
|
});
|
|
|
|
Then("the ingredient {string} should no longer be in the picker", (name: string) => {
|
|
cy.contains(".ingredient-picker__card", name).should("not.exist");
|
|
});
|
|
|
|
Then("the ingredient {string} should be visible in the picker", (name: string) => {
|
|
cy.contains(".ingredient-picker__card", name).should("be.visible");
|
|
});
|
|
|
|
Then("the recipe should include the ingredient {string}", (name: string) => {
|
|
cy.contains(".ingredient-row__name", name).should("be.visible");
|
|
});
|
|
|
|
Then("there should be {int} ingredient rows", (count: number) => {
|
|
cy.get(".ingredient-row").should("have.length", count);
|
|
});
|
|
|
|
When("I remove the ingredient {string} from the recipe", (name: string) => {
|
|
cy.contains(".ingredient-row", name).find("button[title='Retirer cet ingrédient']").click();
|
|
});
|
|
|
|
When(
|
|
"I fill in the ingredient's quantity with {string} and unit {string}",
|
|
(quantity: string, unit: string) => {
|
|
cy.get(".ingredient-row .ingredient-row__quantity").type(quantity);
|
|
cy.get(".ingredient-row .ingredient-row__unit").type(unit);
|
|
},
|
|
);
|
|
|
|
Then("the ingredient's quantity should be {string}", (quantity: string) => {
|
|
cy.get(".ingredient-row .ingredient-row__quantity").should("have.value", quantity);
|
|
});
|
|
|
|
When(
|
|
"I fill in the last ingredient's quantity with {string} and unit {string}",
|
|
(quantity: string, unit: string) => {
|
|
cy.get(".ingredient-row .ingredient-row__quantity").last().type(quantity);
|
|
cy.get(".ingredient-row .ingredient-row__unit").last().type(unit);
|
|
},
|
|
);
|
|
|
|
When("I add a step", () => {
|
|
cy.contains("button", "Ajouter une étape").click();
|
|
});
|
|
|
|
When("I fill in the step description with {string}", (text: string) => {
|
|
cy.get(".step-list-editor__item textarea").type(text);
|
|
});
|
|
|
|
Then("there should be {int} step editor items", (count: number) => {
|
|
cy.get(".step-list-editor__item").should("have.length", count);
|
|
});
|
|
|
|
Then(
|
|
"the recipe creation request should have included name {string} and ingredient {int} with quantity {int} and unit {string}",
|
|
(name: string, ingredientId: number, quantity: number, unit: string) => {
|
|
cy.wait("@createRecipe")
|
|
.its("request.body")
|
|
.should("deep.include", {
|
|
name,
|
|
ingredients: [{ ingredientId, quantity, unit }],
|
|
});
|
|
},
|
|
);
|
|
|
|
Given("recipe 7 exists with an egg omelette", () => {
|
|
const existingRecipe = {
|
|
id: 7,
|
|
name: "Omelette",
|
|
description: null,
|
|
picture: null,
|
|
authorId: 1,
|
|
visibility: "PERSONAL",
|
|
allergens: [{ id: 1, key: "eggs", kind: "ALLERGY" }],
|
|
diets: [],
|
|
isFavorite: false,
|
|
ingredients: [{ ingredient: egg, quantity: 3, unit: "unité" }],
|
|
steps: [{ id: 1, description: "Battre les œufs.", picture: null, order: 0 }],
|
|
};
|
|
cy.intercept("GET", "**/recipes/7", { statusCode: 200, body: existingRecipe });
|
|
});
|
|
|
|
Given("updating recipe 7 will succeed", () => {
|
|
cy.intercept("PATCH", "**/recipes/7", { statusCode: 200, body: { id: 7 } }).as("updateRecipe");
|
|
});
|
|
|
|
Then(
|
|
"the recipe update request should have included these ingredients:",
|
|
(dataTable: DataTable) => {
|
|
const expected = dataTable.hashes().map((row) => ({
|
|
ingredientId: Number(row.ingredientId),
|
|
quantity: Number(row.quantity),
|
|
unit: row.unit,
|
|
}));
|
|
cy.wait("@updateRecipe").its("request.body.ingredients").should("deep.equal", expected);
|
|
},
|
|
);
|