All checks were successful
`RecipeDiet` (tag manuel) est supprimé — table droppée, `dietIds` retiré de `createRecipeSchema`, `DietTagSelect` retiré des formulaires de recette (création + import). `RecipeSummaryView.diets` / `RecipeView.diets` sont désormais **dérivés** via `computeRecipeDietViews` (`@batch-cooking/shared`, pur, testé isolément) : - un régime (vegetarian / vegan / pescatarian) est retenu ssi *tous* les ingrédients y sont compatibles (`IngredientDiet`, exposé par `IngredientView.diets`) ; - `glutenFree` est dérivé des allergènes (aucun ingrédient ne porte `gluten`) — cohérent avec le fait que le seed ne le stocke pas dans `IngredientDiet` ; - `[]` dès qu'une recette contient un ingrédient placeholder (hors-catalogue) : un ingrédient inconnu ne certifie rien. Filtres du sélecteur de planning (`suitableForHousehold`, `dietIds`) : ne peuvent plus être du SQL sur une colonne stockée — devenus des post-filtres sur `diets` calculé dans `listRecipes` ; `omnivore` traité comme « aucune contrainte ». `assertDietsExist` / `ErrorCode.DIET_NOT_FOUND` sur la création recette supprimés (le code d'erreur reste, utilisé par la préférence profil). `DietBadges` inchangé (affiche `diets` tel quel). `RecipePickerDialog` garde son filtre régime (fonctionne via le post-filtre). Tests : `recipe-diets.test.ts` (fonction pure) + `recipe.test.ts` réécrit (régimes calculés attendus, tests DIET_NOT_FOUND retirés) ; Cypress OK (formulaire sans sélecteur de régime, régression recettes/sources/planning). Migration `DROP TABLE recipe_diet` — aucune donnée à conserver. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
125 lines
6.3 KiB
Gherkin
125 lines
6.3 KiB
Gherkin
Feature: Recipe form — associating ingredients
|
|
As a signed-in user
|
|
I want to build a recipe by picking ingredients, quantities, and steps
|
|
So that I can save a complete recipe in one form
|
|
|
|
Background:
|
|
Given I am signed in as "Alice" "Martin"
|
|
And my household id is 1
|
|
And the ingredient and diet catalog is available
|
|
|
|
Scenario: Adds an ingredient from the picker, fills its quantity/unit, and creates the recipe
|
|
Given creating the recipe will succeed and return id 42
|
|
When I visit "/recettes/nouvelle"
|
|
And I fill in the "recipe-name" field with "Salade de tomates"
|
|
And I search the ingredient picker for "tomat"
|
|
And I select the ingredient "Tomate" from the picker
|
|
Then the ingredient "Tomate" should no longer be in the picker
|
|
And the recipe should include the ingredient "Tomate"
|
|
When I fill in the ingredient's quantity with "3" and unit "unité"
|
|
And I add a step
|
|
And I fill in the step description with "Couper les tomates."
|
|
Then the "Enregistrer" button should not be disabled
|
|
When I click the button "Enregistrer"
|
|
Then the recipe creation request should have included name "Salade de tomates", portions 4, and ingredient 1 with quantity 3 and unitId 1
|
|
And the URL should include "/recettes/42"
|
|
|
|
# Regression test for the exact bug reported: `crypto.randomUUID()` (used
|
|
# to mint each ingredient/step draft's client-only React key) throws
|
|
# outside a secure context — https, or literally the hostname `localhost`
|
|
# — so a LAN IP during on-device testing or a Capacitor WebView's
|
|
# `capacitor://` origin hit a black screen with "TypeError:
|
|
# crypto.randomUUID is not a function" the instant an ingredient was
|
|
# added. Cypress's own origin is secure, so this forces the same failure
|
|
# by deleting `crypto.randomUUID` before the app boots — see
|
|
# `apps/web/src/lib/client-key.ts`, which replaced it.
|
|
Scenario: Still works when crypto.randomUUID is unavailable (insecure-context regression)
|
|
When I visit the new recipe form without a secure random UUID
|
|
And I fill in the "recipe-name" field with "Recette hors contexte sécurisé"
|
|
And I select the ingredient "Tomate" from the picker
|
|
And I select the ingredient "Oeuf" from the picker
|
|
Then there should be 2 ingredient rows
|
|
And the recipe should include the ingredient "Tomate"
|
|
And the recipe should include the ingredient "Oeuf"
|
|
When I add a step
|
|
And I add a step
|
|
Then there should be 2 step editor items
|
|
|
|
Scenario: Adds an off-catalog ingredient as a free-text placeholder when nothing in the picker matches
|
|
Given creating the recipe will succeed and return id 43
|
|
When I visit "/recettes/nouvelle"
|
|
And I fill in the "recipe-name" field with "Poulet basquaise"
|
|
And I search the ingredient picker for "piment d'espelette"
|
|
Then the picker should offer to add "piment d'espelette" as a placeholder
|
|
When I add "piment d'espelette" as a placeholder ingredient
|
|
Then the recipe should include the placeholder ingredient "piment d'espelette"
|
|
When I fill in the ingredient's quantity with "1" and unit "unité"
|
|
And I add a step
|
|
And I fill in the step description with "Tout mélanger."
|
|
And I click the button "Enregistrer"
|
|
Then the recipe creation request should have included a placeholder ingredient "piment d'espelette" with quantity 1 and unitId 1
|
|
And the URL should include "/recettes/43"
|
|
|
|
Scenario: Excludes an already-selected ingredient from the picker, and removing it brings it back
|
|
When I visit "/recettes/nouvelle"
|
|
And I select the ingredient "Carotte" from the picker
|
|
Then the ingredient "Carotte" should no longer be in the picker
|
|
When I remove the ingredient "Carotte" from the recipe
|
|
Then the ingredient "Carotte" should be visible in the picker
|
|
And there should be 0 ingredient rows
|
|
|
|
Scenario: Marks an ingredient as « selon le goût » and saves it with no quantity
|
|
Given creating the recipe will succeed and return id 44
|
|
When I visit "/recettes/nouvelle"
|
|
And I fill in the "recipe-name" field with "Tomates au sel"
|
|
And I select the ingredient "Tomate" from the picker
|
|
And I mark the ingredient "Tomate" as seasoning
|
|
Then the ingredient "Tomate" should have no quantity field
|
|
When I add a step
|
|
And I fill in the step description with "Saler."
|
|
Then the "Enregistrer" button should not be disabled
|
|
When I click the button "Enregistrer"
|
|
Then the recipe creation request should have included the seasoning-only ingredient 1
|
|
And the URL should include "/recettes/44"
|
|
|
|
Scenario: The recipe form has no regime picker — regimes are computed from ingredients (issue #131)
|
|
When I visit "/recettes/nouvelle"
|
|
Then I should not see "Régime(s) associé(s)"
|
|
|
|
Scenario: The save/cancel bar stays pinned to the foot of a long form
|
|
When I visit "/recettes/nouvelle"
|
|
And I add a step
|
|
And I add a step
|
|
And I scroll the recipe form to the top
|
|
Then the recipe form action bar is pinned to the bottom of the viewport
|
|
And I should see "Enregistrer"
|
|
And I should see "Annuler"
|
|
|
|
Scenario: Cancelling a new recipe goes back to the catalog
|
|
When I visit "/recettes/nouvelle"
|
|
And I fill in the "recipe-name" field with "Brouillon abandonné"
|
|
And I click the button "Annuler"
|
|
Then the URL should include "/recettes"
|
|
And the URL should not include "/nouvelle"
|
|
|
|
Scenario: Cancelling an edit goes back to that recipe
|
|
Given recipe 7 exists with an egg omelette
|
|
When I visit "/recettes/7/modifier"
|
|
And I click the button "Annuler"
|
|
Then the URL should include "/recettes/7"
|
|
And the URL should not include "/modifier"
|
|
|
|
Scenario: Preloads an existing recipe's ingredients when editing, and lets you add another
|
|
Given recipe 7 exists with an egg omelette
|
|
And updating recipe 7 will succeed
|
|
When I visit "/recettes/7/modifier"
|
|
Then the recipe should include the ingredient "Oeuf"
|
|
And the ingredient's quantity should be "3"
|
|
When I select the ingredient "Tomate" from the picker
|
|
Then there should be 2 ingredient rows
|
|
When I fill in the last ingredient's quantity with "1" and unit "unité"
|
|
And I click the button "Enregistrer"
|
|
Then the recipe update request should have included these ingredients:
|
|
| ingredientId | quantity | unitId |
|
|
| 2 | 3 | 1 |
|
|
| 1 | 1 | 1 |
|