Remplace l'unité texte libre de RecipeIngredient (max 20 caractères, "g"/"grammes"/"G"... jamais fiable à additionner) par une référence vers un nouveau catalogue Unit (id/key/type/toBaseFactor), même traitement que Diet/Allergy/Ingredient : GET /reference/units, seedé par reference-seed-data.ts (14 unités : gram/kilogram/milliliter/ centiliter/liter/tablespoon/teaspoon/piece/pinch/slice/clove/bunch/ sachet/sprig), sélectionnable uniquement via un <select> dans le formulaire recette (plus de saisie libre). `toBaseFactor` (combien d'unités de base — gramme pour MASS, millilitre pour VOLUME — vaut une unité) pose les bases d'une future fonctionnalité de conversion (ex. liste de courses additionnant "500g" + "0.5kg") sans construire cette fonctionnalité elle-même — les unités COUNT restent à toBaseFactor=1, non convertibles entre elles (une "pincée" n'est pas une fraction fixe d'une "gousse"). Migration : recipe_ingredient.unit → unit_id (FK), breaking change sans backfill assumé (pas de recette réelle en prod actuellement, voir commentaire de migration) — mêmes garde-fous service-side que ingredientId (404 UNIT_NOT_FOUND) et mêmes tests de couverture.
104 lines
3 KiB
TypeScript
104 lines
3 KiB
TypeScript
import { Given, Then, When } from "@badeball/cypress-cucumber-preprocessor";
|
|
|
|
const oeufs = { id: 2, key: "eggs", kind: "ALLERGY" };
|
|
|
|
const omelette = {
|
|
id: 2,
|
|
name: "Omelette",
|
|
description: null,
|
|
picture: null,
|
|
portions: 2,
|
|
authorId: 1,
|
|
visibility: "PERSONAL",
|
|
allergens: [oeufs],
|
|
diets: [],
|
|
isFavorite: false,
|
|
};
|
|
|
|
const omeletteDetail = {
|
|
...omelette,
|
|
description: "Une omelette toute simple.",
|
|
ingredients: [
|
|
{
|
|
ingredient: {
|
|
id: 10,
|
|
key: "egg",
|
|
icon: "EGG",
|
|
category: "dairyAndCheese",
|
|
subcategory: "eggs",
|
|
allergens: [oeufs],
|
|
diets: [],
|
|
},
|
|
quantity: 3,
|
|
unit: { id: 1, key: "piece", type: "COUNT", toBaseFactor: 1 },
|
|
},
|
|
],
|
|
steps: [
|
|
{ id: 1, description: "Battre les œufs.", picture: null, order: 1 },
|
|
{ id: 2, description: "Cuire à la poêle.", picture: null, order: 2 },
|
|
],
|
|
};
|
|
|
|
Given("the disliked ingredients list is empty", () => {
|
|
cy.intercept("GET", "**/profile/disliked-ingredients", { statusCode: 200, body: [] });
|
|
});
|
|
|
|
Given("the recipe catalog contains {string}", () => {
|
|
cy.intercept("GET", /\/recipes\?/, { statusCode: 200, body: [omelette] });
|
|
});
|
|
|
|
Given("recipe 2's detail is available", () => {
|
|
cy.intercept("GET", "**/recipes/2", { statusCode: 200, body: omeletteDetail }).as("getRecipe");
|
|
});
|
|
|
|
Given("toggling recipe 2's favorite will succeed", () => {
|
|
cy.intercept("POST", "**/recipes/2/favorite", { statusCode: 204 }).as("favorite");
|
|
});
|
|
|
|
Given("deleting recipe 2 will succeed", () => {
|
|
cy.intercept("DELETE", "**/recipes/2", { statusCode: 204 }).as("deleteRecipe");
|
|
});
|
|
|
|
Then("the recipe {string} should not be visible in the table", (name: string) => {
|
|
cy.contains(".recipe-table__name", name).should("not.exist");
|
|
});
|
|
|
|
Then("the recipe {string} should be marked as favorite", (name: string) => {
|
|
cy.contains(".recipe-table__name", name).find(".recipe-table__fav-mark").should("exist");
|
|
});
|
|
|
|
Then("the recipe {string} should not be marked as favorite", (name: string) => {
|
|
cy.contains(".recipe-table__name", name).find(".recipe-table__fav-mark").should("not.exist");
|
|
});
|
|
|
|
Then("the recipe detail panel heading should be {string}", (text: string) => {
|
|
cy.get(".recipe-detail-panel").contains("h2", text).should("be.visible");
|
|
});
|
|
|
|
When("I click the favorite star", () => {
|
|
cy.get(".favorite-star-button").click();
|
|
});
|
|
|
|
Then("the favorite request should have been made", () => {
|
|
cy.wait("@favorite");
|
|
});
|
|
|
|
Then("the favorite star should be marked as favorite", () => {
|
|
cy.get(".favorite-star-button").should("have.class", "is-favorite");
|
|
});
|
|
|
|
When("I click {string} in the recipe detail panel", (text: string) => {
|
|
cy.contains(".recipe-detail-panel__danger-button", text).click();
|
|
});
|
|
|
|
When("I confirm the deletion in the recipe detail panel", () => {
|
|
cy.contains(".recipe-detail-panel__danger-button", "Confirmer la suppression").click();
|
|
});
|
|
|
|
Then("the delete request should have been made", () => {
|
|
cy.wait("@deleteRecipe");
|
|
});
|
|
|
|
Then("the URL should match the recipes list", () => {
|
|
cy.url().should("match", /\/recettes\/?$/);
|
|
});
|