diff --git a/apps/web/cypress/e2e/auth.cy.ts b/apps/web/cypress/e2e/auth.cy.ts index 9be4fde..e0baebd 100644 --- a/apps/web/cypress/e2e/auth.cy.ts +++ b/apps/web/cypress/e2e/auth.cy.ts @@ -76,7 +76,7 @@ describe("Signup", () => { describe("Login", () => { it("logs in and lands on the home page", () => { cy.intercept("GET", "**/auth/me", { statusCode: 401 }); - cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null }); + cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: null }); cy.intercept("POST", "**/auth/login", { statusCode: 200, body: { @@ -130,7 +130,7 @@ describe("Already authenticated", () => { dietId: null, }, }); - cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null }); + cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: null }); cy.visit("/login"); cy.url().should("not.include", "/login"); @@ -150,7 +150,7 @@ describe("Already authenticated", () => { dietId: null, }, }); - cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null }); + cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: null }); cy.intercept("POST", "**/auth/logout", { statusCode: 204 }).as("logout"); cy.visit("/"); diff --git a/apps/web/cypress/e2e/onboarding.cy.ts b/apps/web/cypress/e2e/onboarding.cy.ts index 8906290..c8cf368 100644 --- a/apps/web/cypress/e2e/onboarding.cy.ts +++ b/apps/web/cypress/e2e/onboarding.cy.ts @@ -16,7 +16,7 @@ const signupResponse = { function signupAndReachOnboarding() { cy.intercept("GET", "**/auth/me", { statusCode: 401 }); cy.intercept("POST", "**/auth/signup", { statusCode: 201, body: signupResponse }).as("signup"); - cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null }); + cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: null }); cy.visit("/signup"); cy.get("#firstName").type("Alice"); diff --git a/apps/web/cypress/e2e/sidebar.cy.ts b/apps/web/cypress/e2e/sidebar.cy.ts index 07295d2..a7d20ab 100644 --- a/apps/web/cypress/e2e/sidebar.cy.ts +++ b/apps/web/cypress/e2e/sidebar.cy.ts @@ -13,7 +13,9 @@ const authenticatedProfile = { describe("Sidebar — settings menu and account menu", () => { beforeEach(() => { cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile }); - cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null }); + // Not "**/planning*" — that glob also matches the Vite dev request for + // planning-page.scss (see planning-page.cy.ts for the same gotcha). + cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: null }); }); it("no longer lists Foyer in the main nav", () => { @@ -21,15 +23,24 @@ describe("Sidebar — settings menu and account menu", () => { cy.get(".app-sidebar__nav a").should("not.contain", "Foyer"); }); - it("reveals the three settings pages behind the Paramètres toggle", () => { + it("reveals the four settings pages behind the Paramètres toggle", () => { cy.visit("/"); cy.contains("a", "Compte").should("not.exist"); cy.contains("button", "Paramètres").click(); cy.contains("a", "Compte").should("have.attr", "href", "/parametres/compte"); - cy.contains("a", "Préférences").should("have.attr", "href", "/parametres/preferences"); + cy.contains("a", "Préférences alimentaires").should( + "have.attr", + "href", + "/parametres/preferences", + ); cy.contains("a", "Foyer").should("have.attr", "href", "/parametres/foyer"); + cy.contains("a", "Préférences utilisateur").should( + "have.attr", + "href", + "/parametres/preferences-utilisateur", + ); }); it("opens the account menu from the greeting and links to Mon compte", () => { diff --git a/apps/web/cypress/e2e/user-preferences.cy.ts b/apps/web/cypress/e2e/user-preferences.cy.ts new file mode 100644 index 0000000..0c09b1c --- /dev/null +++ b/apps/web/cypress/e2e/user-preferences.cy.ts @@ -0,0 +1,54 @@ +// Mocks the API via cy.intercept — see auth.cy.ts for the rationale. + +const authenticatedProfile = { + id: 1, + firstName: "Alice", + lastName: "Martin", + email: "alice@example.com", + tokenVersion: 0, + houseId: null, + dietId: null, +}; + +describe("User preferences (/parametres/preferences-utilisateur)", () => { + beforeEach(() => { + cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile }); + }); + + it("shows SYSTEM selected by default", () => { + cy.intercept("GET", "**/preferences", { statusCode: 200, body: { theme: "SYSTEM" } }); + + cy.visit("/parametres/preferences-utilisateur"); + + cy.contains("label", "Système").find("input[type=radio]").should("be.checked"); + cy.contains("label", "Clair").find("input[type=radio]").should("not.be.checked"); + cy.contains("label", "Sombre").find("input[type=radio]").should("not.be.checked"); + // SYSTEM never sets an override — the OS/browser preference decides. + cy.get("html").should("not.have.attr", "data-theme"); + }); + + it("shows the previously saved theme selected, and applies it to the document", () => { + cy.intercept("GET", "**/preferences", { statusCode: 200, body: { theme: "DARK" } }); + + cy.visit("/parametres/preferences-utilisateur"); + + cy.contains("label", "Sombre").find("input[type=radio]").should("be.checked"); + cy.get("html").should("have.attr", "data-theme", "dark"); + }); + + it("switching theme autosaves and applies immediately, no explicit save button", () => { + cy.intercept("GET", "**/preferences", { statusCode: 200, body: { theme: "SYSTEM" } }); + cy.intercept("PATCH", "**/preferences", { statusCode: 200, body: { theme: "LIGHT" } }).as( + "updatePreferences", + ); + + cy.visit("/parametres/preferences-utilisateur"); + cy.contains("button", "Enregistrer").should("not.exist"); + + cy.contains("label", "Clair").click(); + + cy.wait("@updatePreferences").its("request.body").should("deep.equal", { theme: "LIGHT" }); + cy.contains("Enregistré ✓").should("be.visible"); + cy.get("html").should("have.attr", "data-theme", "light"); + }); +});