fix(web-tests): résout les steps Cucumber manquants dans recipe-sources.feature

La CI a révélé que la résolution des steps Cucumber n'est pas globale sur
tout cypress/e2e/ comme je le pensais — seuls le fichier/dossier de même
nom que la .feature et cypress/support/step_definitions/ sont cherchés
(voir le message d'erreur de la CI). recipes.ts vit directement dans
cypress/e2e/ (pas dans step_definitions/), donc ses steps ne résolvaient
que pour recipes.feature — recipe-sources.feature qui les réutilisait
plantait avec "Step implementation missing".

- Les steps génériques réellement partagés entre les deux features
  (heading du panneau détail, technique surlignée, tooltip) migrent vers
  common.steps.ts (step_definitions/, cherché globalement) plutôt que
  d'être dupliqués une deuxième fois.
- Les steps propres à un fixture précis (recipe 2's detail, disliked
  ingredients, catalogue vide) sont redéclarés localement dans
  recipe-sources.ts avec leurs propres données minimales — même
  précédent que recipes.cy.ts, qui duplique déjà indépendamment le
  fixture omeletteDetail plutôt que de dépendre de recipes.ts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Nicolas 2026-08-20 16:22:12 +02:00
parent b5a12cf489
commit a860363438
4 changed files with 76 additions and 27 deletions

View file

@ -10,7 +10,7 @@ Feature: Browsing external recipe sources
And the planning request returns nothing And the planning request returns nothing
Scenario: Prompts to enable a source when the household hasn't enabled any Scenario: Prompts to enable a source when the household hasn't enabled any
Given the recipe catalog contains "Omelette" Given the recipe catalog contains nothing
And the sources reference list has options And the sources reference list has options
And the household's enabled sources are empty And the household's enabled sources are empty
When I visit "/recettes" When I visit "/recettes"
@ -18,7 +18,7 @@ Feature: Browsing external recipe sources
Then I should see "Aucune source n'est activée" Then I should see "Aucune source n'est activée"
Scenario: Browses an enabled source, distinguishing already-imported items from new ones Scenario: Browses an enabled source, distinguishing already-imported items from new ones
Given the recipe catalog contains "Omelette" Given the recipe catalog contains nothing
And the sources reference list has options And the sources reference list has options
And the household has enabled TheMealDB And the household has enabled TheMealDB
And browsing TheMealDB returns some items And browsing TheMealDB returns some items
@ -34,7 +34,7 @@ Feature: Browsing external recipe sources
And the recipe detail panel heading should be "Omelette" And the recipe detail panel heading should be "Omelette"
Scenario: Previews a not-yet-imported item, highlighting its detected techniques Scenario: Previews a not-yet-imported item, highlighting its detected techniques
Given the recipe catalog contains "Omelette" Given the recipe catalog contains nothing
And the sources reference list has options And the sources reference list has options
And the household has enabled TheMealDB And the household has enabled TheMealDB
And browsing TheMealDB returns some items And browsing TheMealDB returns some items

View file

@ -4,10 +4,53 @@ import { Given, Then, When } from "@badeball/cypress-cucumber-preprocessor";
// .github/workflows/ci.yml); apps/api's own Mocha suite covers real API // .github/workflows/ci.yml); apps/api's own Mocha suite covers real API
// behavior against a real database (see test/sources.test.ts). // behavior against a real database (see test/sources.test.ts).
// //
// "the sources reference list has options" (theMealDb id 1, marmiton id 2) // "the sources reference list has options"/"the household's enabled
// and "recipe 2's detail is available" are shared with reference-data.steps.ts // sources are empty" resolve from cypress/support/step_definitions/ (the
// / recipes.ts respectively — Cucumber step matching is global across every // preprocessor's step-lookup is *not* global across cypress/e2e/ — only a
// step-definition file, not scoped per feature. // feature's own same-named file/directory plus that shared folder are
// searched, see its error message when a step isn't found). recipes.ts
// sits directly in cypress/e2e/ (not that shared folder), so its own
// "the disliked ingredients list is empty"/"the recipe catalog
// contains"/"recipe 2's detail is available" are scoped to recipes.feature
// only — this file redeclares its own minimal equivalents rather than
// relocating shared infra, the same "each spec's own self-contained
// fixtures" precedent recipes.cy.ts already sets alongside recipes.ts.
Given("the disliked ingredients list is empty", () => {
cy.intercept("GET", "**/profile/disliked-ingredients", { statusCode: 200, body: [] });
});
Given("the recipe catalog contains nothing", () => {
cy.intercept("GET", /\/recipes\?/, { statusCode: 200, body: [] });
});
Given("recipe 2's detail is available", () => {
cy.intercept("GET", "**/recipes/2", {
statusCode: 200,
body: {
id: 2,
name: "Omelette",
description: null,
picture: null,
portions: 2,
authorId: 1,
visibility: "PERSONAL",
allergens: [],
diets: [],
isFavorite: false,
ingredients: [],
steps: [
{
id: 1,
description: "Cuire à la poêle.",
picture: null,
order: 1,
techSteps: [{ techStep: { id: 1, key: "cook" }, start: 0, end: 5 }],
},
],
},
});
});
Given("the household has enabled TheMealDB", () => { Given("the household has enabled TheMealDB", () => {
cy.intercept("GET", "**/house/current/sources", { statusCode: 200, body: [1] }); cy.intercept("GET", "**/house/current/sources", { statusCode: 200, body: [1] });

View file

@ -80,10 +80,6 @@ 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"); 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", () => { When("I click the favorite star", () => {
cy.get(".favorite-star-button").click(); cy.get(".favorite-star-button").click();
}); });
@ -111,19 +107,3 @@ Then("the delete request should have been made", () => {
Then("the URL should match the recipes list", () => { Then("the URL should match the recipes list", () => {
cy.url().should("match", /\/recettes\/?$/); cy.url().should("match", /\/recettes\/?$/);
}); });
Then("I should see the highlighted technique {string}", (text: string) => {
// The steps section sits below the panel's header/photo/description, off
// the fold of `.app-content`'s own scroll (see layout.cy.ts) — a bare
// `.should("be.visible")` doesn't auto-scroll, same fix as
// household-settings.feature's sources-section scenario.
cy.contains(".step-tech-step", text).scrollIntoView().should("be.visible");
});
When("I focus the highlighted technique {string}", (text: string) => {
cy.contains(".step-tech-step", text).focus();
});
Then("the tooltip should show {string}", (label: string) => {
cy.get(".tooltip__bubble").contains(label).should("be.visible");
});

View file

@ -133,6 +133,32 @@ When("I scroll to the section {string}", (legend: string) => {
cy.contains("legend", legend).scrollIntoView(); cy.contains("legend", legend).scrollIntoView();
}); });
// `.recipe-detail-panel` is used by both a saved recipe's real detail
// (RecipeDetailPanel) and an unsaved source item's read-only preview
// (SourceItemPreviewPanel) — recipes.feature and recipe-sources.feature
// both need this.
Then("the recipe detail panel heading should be {string}", (text: string) => {
cy.get(".recipe-detail-panel").contains("h2", text).should("be.visible");
});
// `.step-tech-step`/`.tooltip__bubble` come from StepDescription/Tooltip
// (components/ui/), rendered by both of those same two panels — same
// reasoning as the detail-panel-heading step above.
Then("I should see the highlighted technique {string}", (text: string) => {
// The steps section can sit below the panel's header/photo/description,
// off the fold of `.app-content`'s own scroll (see layout.cy.ts) — a
// bare `.should("be.visible")` doesn't auto-scroll.
cy.contains(".step-tech-step", text).scrollIntoView().should("be.visible");
});
When("I focus the highlighted technique {string}", (text: string) => {
cy.contains(".step-tech-step", text).focus();
});
Then("the tooltip should show {string}", (label: string) => {
cy.get(".tooltip__bubble").contains(label).should("be.visible");
});
Then("the checkbox {string} should be checked", (label: string) => { Then("the checkbox {string} should be checked", (label: string) => {
cy.contains("label", label).find("input[type=checkbox]").should("be.checked"); cy.contains("label", label).find("input[type=checkbox]").should("be.checked");
}); });