From 50c9124ddd3e79c60e5b0cdb2422e6fe2aa02604 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sat, 22 Aug 2026 10:58:47 +0200 Subject: [PATCH] fix(tech-steps): corrige un span de correction incorrect sur un highlight existant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug reel trouve en lancant l'application pour de vrai et en cliquant sur un highlight existant : la correction soumise couvrait presque toute la description au lieu du seul mot-cle cliqué (ex: [6, 56) au lieu de [6, 13) pour "mijoter"). Cause : StepDescription.tsx capturait `start` dans un `const` par iteration de `.map()` (correct), mais utilisait `offset` directement (la variable mutable partagee, pas une valeur capturee) pour `end` dans le gestionnaire onClick - une fermeture classique sur variable de boucle encore mutee. Par le temps ou l'utilisateur clique reellement (bien apres la fin du rendu), `offset` contient sa valeur finale (fin de la description entiere), pas celle du segment concerne. Corrige en capturant `end` dans un `const` au meme endroit que `start`. Renforce aussi l'assertion e2e correspondante (recipes.ts) qui ne verifiait auparavant que la requete avait ete faite, jamais son contenu - elle serait passee malgre ce bug. Verifie en conditions reelles : recette creee via l'UI, correction soumise, span persiste verifie directement en base (start=6, end=13, previous=simmer, corrected=grill). Co-Authored-By: Claude Sonnet 5 --- apps/web/cypress/e2e/recipes.ts | 10 +++++++++- .../web/src/features/recipes/steps/StepDescription.tsx | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/web/cypress/e2e/recipes.ts b/apps/web/cypress/e2e/recipes.ts index 9af2311..5acf3ee 100644 --- a/apps/web/cypress/e2e/recipes.ts +++ b/apps/web/cypress/e2e/recipes.ts @@ -100,7 +100,15 @@ When("I choose {string} as the correct technique", (label: string) => { }); Then("the correction request should have been made", () => { - cy.wait("@correction"); + // Asserts the actual span, not just that *a* request fired — a real bug + // (StepDescription.tsx's click handler reading a shared, still-mutating + // `offset` variable by reference instead of a value captured at render + // time) once sent `end` all the way to the end of the description + // instead of "Cuire"'s own tight [0, 5) span, and a request-fired-only + // assertion here didn't catch it — found only via manual testing. + cy.wait("@correction") + .its("request.body") + .should("deep.include", { start: 0, end: 5, previousTechStepId: 1 }); }); Then("the recipe {string} should not be visible in the table", (name: string) => { diff --git a/apps/web/src/features/recipes/steps/StepDescription.tsx b/apps/web/src/features/recipes/steps/StepDescription.tsx index fd1ccdc..b37100a 100644 --- a/apps/web/src/features/recipes/steps/StepDescription.tsx +++ b/apps/web/src/features/recipes/steps/StepDescription.tsx @@ -92,6 +92,14 @@ export function StepDescription({ {segments.map((segment, index) => { const start = offset; offset += segment.text.length; + // Captured now, not read as `offset` later inside a click + // handler below — `offset` keeps mutating for every subsequent + // segment this same `.map()` pass renders, so a closure + // referencing it directly would see its *final* value (the end + // of the whole description) whenever it actually fires, long + // after render — found via a real correction submitted with + // `end` far past this segment's own text. + const end = offset; // A segment's own text/techStep don't uniquely identify it (the // same word can appear twice in one description) — index is the // only thing that does, but this list is fully regenerated from @@ -130,7 +138,7 @@ export function StepDescription({ editable ? () => setActiveCorrection({ - range: { start, end: offset }, + range: { start, end }, selectedText: segment.text, previousTechStepId: techStep.id, })