batchCooking/apps/web/cypress/e2e/recipe-form.feature
Nicolas ba030ec18c
All checks were successful
CI / lint (push) Successful in 3m5s
CI / build (push) Successful in 3m15s
CI / intent-service-test (push) Successful in 5m12s
CI / e2e (push) Successful in 11m1s
CI / test (push) Successful in 19m13s
CI / build-android (push) Has been skipped
feat(recipes): ingrédients d'assaisonnement « selon le goût » sans quantité (#128)
`RecipeIngredient.quantity`/`unitId` deviennent nullable (migration purement
additive) : une ligne de recette peut n'avoir ni quantité ni unité — sel,
poivre… ajoutés selon le goût. Le schéma partagé impose « quantité et unité,
ou aucune des deux » ; `toRecipeView` renvoie `null`/`null`.

`aggregateShoppingList` fait apparaître une telle ligne une seule fois, sans
quantité ni unité, jamais sommée ni mise à l'échelle. `ShoppingListPage`
affiche « selon le goût » à la place de la quantité.

Formulaire : `IngredientRow` gagne une case « Selon le goût » qui masque
quantité/unité ; `RecipeFormPage` et `RecipeImportForm` n'exigent plus
quantité/unité pour ces lignes et les envoient sans. Un import dont la source
laisse une ligne résolue sans quantité (« salt to taste ») arrive
directement en « selon le goût ».

Le chemin clause-de-technique (session de cuisine) tolérait déjà `quantity`
null — inchangé.

Tests : Mocha (création/édition d'une ligne sans quantité, rejet
quantité-sans-unité, liste de courses non sommée) ; Cypress (case à cocher +
parcours), régression recette/liste de courses OK.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 14:15:28 +02:00

121 lines
6.1 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 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 |