batchCooking/apps/web/cypress/e2e/onboarding.cy.ts
Nicolas 8f25e53f11 Web: allergies/intolérances séparées + hot saving sur /foyer (step 8/8)
Retour fonctionnel : allergies et intolérances doivent être distinguées
dans l'UI, et /foyer doit sauvegarder à la volée plutôt que via des
boutons "Enregistrer".

- AllergySelect prend un `legend` en prop au lieu d'un libellé interne
  fixe — le même composant est rendu deux fois par chaque page
  consommatrice (HouseholdPage, OnboardingAllergensPage), une fois par
  `kind` (ALLERGY / INTOLERANCE), la sélection restant une seule liste
  d'IDs partagée.
- HouseholdPage : suppression des boutons "Enregistrer", autosave
  déclenché depuis le handler onChange de chaque champ (jamais un
  useEffect générique sur la valeur — se déclencherait aussi au
  chargement initial, sans distinction propre "chargé" vs "modifié").
  Nom du foyer et allergènes/intolérances debouncés (600ms/500ms),
  régime sauvegardé immédiatement (sélection discrète). Validation
  client (nom vide) empêche l'autosave plutôt que de déclencher un
  aller-retour API voué à l'échec.
- i18n : household.form.allergiesLabel devient "Allergies" (au lieu de
  "Allergies & intolérances"), nouvelle clé intolerancesLabel, save/
  saved remplacés par saving/saved (plus de bouton à libeller).
- Cypress (household.cy.ts réécrit, onboarding.cy.ts mis à jour) +
  specs/frontend-architecture.md + README.md.

Vérifié dans le navigateur : wizard d'inscription affiche bien les
deux groupes (12 allergies / 2 intolérances) ; /foyer sans aucun
bouton, chaque section sauvegarde automatiquement (vérifié en base
après édition du nom du foyer et du régime) ; compte de test nettoyé.

Clôt le retour fonctionnel sur la feature profil/foyer/régime/
allergènes (8 commits au total sur cette PR).
2026-08-17 00:09:09 +02:00

131 lines
5.2 KiB
TypeScript

// Mocks the API via cy.intercept — see auth.cy.ts for the rationale (no
// live backend in this CI job; apps/api's own Mocha/Cucumber suites cover
// real API behavior against a real database).
const signupResponse = {
id: 1,
firstName: "Alice",
lastName: "Martin",
email: "alice@example.com",
tokenVersion: 0,
houseId: 1,
dietId: null,
};
describe("Onboarding wizard (household → regime → allergens)", () => {
it("walks through all three steps after signup and lands on the home", () => {
cy.intercept("GET", "**/auth/me", { statusCode: 401 });
cy.intercept("POST", "**/auth/signup", { statusCode: 201, body: signupResponse }).as("signup");
cy.intercept("GET", "**/house/current", {
statusCode: 200,
body: { id: 1, name: "Foyer de Alice" },
});
cy.intercept("PATCH", "**/house/current", {
statusCode: 200,
body: { id: 1, name: "Chez Alice" },
}).as("renameHouse");
cy.intercept("GET", "**/reference/diets", {
statusCode: 200,
body: [
{ id: 1, name: "Omnivore" },
{ id: 2, name: "Végétarien" },
],
});
cy.intercept("PATCH", "**/profile/diet", {
statusCode: 200,
body: { ...signupResponse, dietId: 2 },
}).as("updateDiet");
cy.intercept("GET", "**/reference/allergies", {
statusCode: 200,
body: [
{ id: 1, name: "Arachides", kind: "ALLERGY" },
{ id: 2, name: "Gluten", kind: "INTOLERANCE" },
],
});
cy.intercept("PATCH", "**/profile/allergies", { statusCode: 200, body: [1] }).as(
"updateAllergies",
);
cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null });
cy.visit("/signup");
cy.get("#firstName").type("Alice");
cy.get("#lastName").type("Martin");
cy.get("#email").type("alice@example.com");
cy.get("#password").type("correct-horse-battery-staple");
cy.contains("button", "Créer mon profil").click();
cy.wait("@signup");
// Step 1/3 — household name, prefilled with the auto-generated default.
cy.url().should("include", "/onboarding/foyer");
cy.contains("Étape 1 sur 3").should("be.visible");
cy.get("#houseName").should("have.value", "Foyer de Alice");
cy.get("#houseName").clear();
cy.get("#houseName").type("Chez Alice");
cy.contains("button", "Continuer").click();
cy.wait("@renameHouse").its("request.body").should("deep.equal", { name: "Chez Alice" });
// Step 2/3 — dietary regime.
cy.url().should("include", "/onboarding/regime");
cy.contains("Étape 2 sur 3").should("be.visible");
cy.get("#diet").select("Végétarien");
cy.contains("button", "Continuer").click();
cy.wait("@updateDiet").its("request.body").should("deep.equal", { dietId: 2 });
// Step 3/3 — allergens (grouped into two lists) and intolerances, then finish.
cy.url().should("include", "/onboarding/allergenes");
cy.contains("Étape 3 sur 3").should("be.visible");
cy.contains("legend", "Allergies").should("be.visible");
cy.contains("legend", "Intolérances").should("be.visible");
cy.contains("label", "Arachides").find("input[type=checkbox]").check();
cy.contains("button", "Terminer").click();
cy.wait("@updateAllergies")
.its("request.body")
.should("deep.equal", { allergyIds: [1] });
cy.url().should("eq", `${Cypress.config().baseUrl}/`);
cy.contains("h1", "Planning de la semaine").should("be.visible");
});
it("lets every step be skipped without changing anything", () => {
cy.intercept("GET", "**/auth/me", { statusCode: 401 });
cy.intercept("POST", "**/auth/signup", { statusCode: 201, body: signupResponse });
cy.intercept("GET", "**/house/current", {
statusCode: 200,
body: { id: 1, name: "Foyer de Alice" },
});
cy.intercept("PATCH", "**/house/current", {
statusCode: 200,
body: { id: 1, name: "Foyer de Alice" },
}).as("renameHouse");
cy.intercept("GET", "**/reference/diets", { statusCode: 200, body: [] });
cy.intercept("PATCH", "**/profile/diet", { statusCode: 200, body: signupResponse }).as(
"updateDiet",
);
cy.intercept("GET", "**/reference/allergies", { statusCode: 200, body: [] });
cy.intercept("PATCH", "**/profile/allergies", { statusCode: 200, body: [] }).as(
"updateAllergies",
);
cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null });
cy.visit("/signup");
cy.get("#firstName").type("Alice");
cy.get("#lastName").type("Martin");
cy.get("#email").type("alice@example.com");
cy.get("#password").type("correct-horse-battery-staple");
cy.contains("button", "Créer mon profil").click();
cy.url().should("include", "/onboarding/foyer");
cy.contains("button", "Continuer").click();
cy.wait("@renameHouse").its("request.body").should("deep.equal", { name: "Foyer de Alice" });
cy.url().should("include", "/onboarding/regime");
cy.contains("button", "Continuer").click();
cy.wait("@updateDiet").its("request.body").should("deep.equal", { dietId: null });
cy.url().should("include", "/onboarding/allergenes");
cy.contains("button", "Terminer").click();
cy.wait("@updateAllergies").its("request.body").should("deep.equal", { allergyIds: [] });
cy.url().should("eq", `${Cypress.config().baseUrl}/`);
});
});