// Mocks the API via cy.intercept — see auth.cy.ts for the rationale. const adminProfile = { id: 1, firstName: "Alice", lastName: "Martin", email: "alice@example.com", tokenVersion: 0, houseId: null as number | null, dietId: null, }; const houseWithTwoMembers = { id: 1, name: "Chez Alice", adminId: 1, inviteCode: "ABCD2345", members: [ { id: 1, firstName: "Alice", lastName: "Martin" }, { id: 2, firstName: "Bob", lastName: "Dupont" }, ], }; describe("Household settings (/parametres/foyer) — no household yet", () => { beforeEach(() => { cy.intercept("GET", "**/auth/me", { statusCode: 200, body: adminProfile }); cy.intercept("GET", "**/house/current", { statusCode: 200, body: null }); }); it("offers to create or join a household", () => { cy.visit("/parametres/foyer"); cy.contains("Créer un foyer").should("be.visible"); cy.contains("Rejoindre un foyer").should("be.visible"); }); it("creates a household", () => { const createdHouse = { id: 1, name: "Chez Alice", adminId: 1, inviteCode: "ABCD2345", members: [{ id: 1, firstName: "Alice", lastName: "Martin" }], }; // The page reloads `GET /house/current` right after creating succeeds — // see the "deletes the household" test above for the same pattern. let created = false; cy.intercept("GET", "**/house/current", (req) => { req.reply({ statusCode: 200, body: created ? createdHouse : null }); }); cy.intercept("POST", "**/house", (req) => { created = true; req.reply({ statusCode: 201, body: createdHouse }); }).as("createHouse"); cy.visit("/parametres/foyer"); cy.get("#houseName").type("Chez Alice"); cy.contains("button", "Créer").click(); cy.wait("@createHouse").its("request.body").should("deep.equal", { name: "Chez Alice" }); cy.contains("ABCD2345").should("be.visible"); }); it("joins a household by invite code", () => { let joined = false; cy.intercept("GET", "**/house/current", (req) => { req.reply({ statusCode: 200, body: joined ? houseWithTwoMembers : null }); }); cy.intercept("POST", "**/house/join", (req) => { joined = true; req.reply({ statusCode: 200, body: houseWithTwoMembers }); }).as("joinHouse"); cy.visit("/parametres/foyer"); cy.get("#inviteCode").type("abcd2345"); cy.contains("button", "Rejoindre").click(); cy.wait("@joinHouse").its("request.body").should("deep.equal", { inviteCode: "ABCD2345" }); cy.contains("Bob Dupont").should("be.visible"); }); }); describe("Household settings (/parametres/foyer) — as the admin", () => { beforeEach(() => { cy.intercept("GET", "**/auth/me", { statusCode: 200, body: { ...adminProfile, houseId: 1 } }); cy.intercept("GET", "**/house/current", { statusCode: 200, body: houseWithTwoMembers }); }); it("shows the household's name, invite code, and members with an admin badge", () => { cy.visit("/parametres/foyer"); cy.get("#houseName").should("have.value", "Chez Alice"); cy.contains("ABCD2345").should("be.visible"); cy.contains("Bob Dupont").should("be.visible"); cy.contains("Alice Martin").parent().contains("Admin"); }); it("autosaves the household name", () => { cy.intercept("PATCH", "**/house/current", { statusCode: 200, body: { ...houseWithTwoMembers, name: "Chez les Martin" }, }).as("renameHouse"); cy.visit("/parametres/foyer"); cy.get("#houseName").clear(); cy.get("#houseName").type("Chez les Martin"); cy.wait("@renameHouse").its("request.body").should("deep.equal", { name: "Chez les Martin" }); cy.contains("Enregistré ✓").should("be.visible"); }); it("removes a member", () => { // Same reasoning as the "deletes the household" test below — the page // reloads `GET /house/current` right after the removal succeeds. let memberRemoved = false; cy.intercept("GET", "**/house/current", (req) => { const body = memberRemoved ? { ...houseWithTwoMembers, members: [houseWithTwoMembers.members[0]] } : houseWithTwoMembers; req.reply({ statusCode: 200, body }); }); cy.intercept("DELETE", "**/house/members/2", (req) => { memberRemoved = true; req.reply({ statusCode: 200, body: { ...houseWithTwoMembers, members: [houseWithTwoMembers.members[0]] }, }); }).as("removeMember"); cy.visit("/parametres/foyer"); cy.contains("li", "Bob Dupont").contains("button", "Retirer").click(); cy.wait("@removeMember"); cy.contains("Bob Dupont").should("not.exist"); }); it("deletes the household after confirming", () => { // The page reloads `GET /house/current` right after the delete // succeeds — this intercept needs to answer differently before/after // that DELETE, hence the shared mutable flag rather than two static // `cy.intercept` calls (the later one would just win for every request, // including the initial page load). let houseDeleted = false; cy.intercept("GET", "**/house/current", (req) => { req.reply({ statusCode: 200, body: houseDeleted ? null : houseWithTwoMembers }); }); cy.intercept("DELETE", "**/house/current", (req) => { houseDeleted = true; req.reply({ statusCode: 204 }); }).as("deleteHouse"); cy.visit("/parametres/foyer"); cy.contains("button", "Supprimer le foyer").click(); cy.contains("button", "Confirmer la suppression").click(); cy.wait("@deleteHouse"); cy.contains("Créer un foyer").should("be.visible"); }); }); describe("Household settings (/parametres/foyer) — as a non-admin member", () => { beforeEach(() => { cy.intercept("GET", "**/auth/me", { statusCode: 200, body: { ...adminProfile, id: 2, firstName: "Bob", lastName: "Dupont", houseId: 1 }, }); cy.intercept("GET", "**/house/current", { statusCode: 200, body: houseWithTwoMembers }); }); it("offers to leave the household instead of deleting it", () => { cy.visit("/parametres/foyer"); cy.contains("button", "Quitter le foyer").should("be.visible"); cy.contains("button", "Supprimer le foyer").should("not.exist"); }); it("leaves the household", () => { cy.intercept("POST", "**/house/leave", { statusCode: 204 }).as("leaveHouse"); cy.visit("/parametres/foyer"); cy.contains("button", "Quitter le foyer").click(); cy.wait("@leaveHouse"); }); });