batchCooking/apps/web/cypress/e2e/home-planning.cy.ts
Nicolas 72472581d6 Tests + docs: sidebar/planning Cypress coverage, frontend-architecture.md (step 5/5)
- New apps/web/cypress/e2e/home-planning.cy.ts (mocked API, same
  cy.intercept convention as auth.cy.ts):
  - sidebar nav between sections + active-link highlighting
  - user name + logout from the sidebar footer
  - home planning: empty / loaded (table rows) / error states
- specs/frontend-architecture.md: documents AppLayout (single
  RequireAuth+AppLayout parent route, nested routes via <Outlet />),
  the ComingSoonPage stub pattern, updated folder tree and i18n
  namespace list, updated routing diagram.
- README.md: new "Accueil, sidebar & sections" section; notes the
  Cypress-can't-run-headless-here environment limitation (confirmed
  pre-existing on main) and the docker-vs-local-dev CORS_ORIGIN gotcha
  hit while manually verifying this feature.

Closes out the home-page-after-login feature (5 commits, this PR):
GET /planning/current -> AppLayout -> routing/stub pages -> HomePage
planning view -> this commit.
2026-08-16 21:09:31 +02:00

106 lines
3.7 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 authenticatedProfile = {
id: 1,
firstName: "Alice",
lastName: "Martin",
email: "alice@example.com",
tokenVersion: 0,
houseId: 1,
dietId: null,
};
describe("Sidebar navigation", () => {
beforeEach(() => {
cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile });
cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null });
cy.visit("/");
});
it("highlights the current section and navigates between stub pages", () => {
cy.contains("nav a", "Planning").should("have.class", "active");
cy.contains("nav a", "Recettes").click();
cy.url().should("include", "/recettes");
cy.contains("h1", "Recettes").should("be.visible");
cy.contains("nav a", "Recettes").should("have.class", "active");
cy.contains("nav a", "Planning").should("not.have.class", "active");
cy.contains("nav a", "Liste de courses").click();
cy.url().should("include", "/liste-de-courses");
cy.contains("h1", "Liste de courses").should("be.visible");
cy.contains("nav a", "Foyer & profil").click();
cy.url().should("include", "/foyer");
cy.contains("h1", "Foyer & profil").should("be.visible");
cy.contains("nav a", "Planning").click();
cy.url().should("eq", `${Cypress.config().baseUrl}/`);
cy.contains("h1", "Planning de la semaine").should("be.visible");
});
it("shows the signed-in user's name and lets them log out from the sidebar", () => {
cy.intercept("POST", "**/auth/logout", { statusCode: 204 }).as("logout");
cy.contains("Bonjour Alice").should("be.visible");
cy.contains("button", "Se déconnecter").click();
cy.wait("@logout");
cy.url().should("include", "/login");
});
});
describe("Home planning view", () => {
it("shows an empty state when the household has no current planning", () => {
cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile });
cy.intercept("GET", "**/planning/current", { statusCode: 200, body: null });
cy.visit("/");
cy.contains("h1", "Planning de la semaine").should("be.visible");
cy.contains("Aucun planning pour cette semaine.").should("be.visible");
cy.get("table").should("not.exist");
});
it("renders the current planning's meals when there is one", () => {
cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile });
cy.intercept("GET", "**/planning/current", {
statusCode: 200,
body: {
id: 1,
startDate: "2026-08-10T00:00:00.000Z",
finishDate: "2026-08-16T00:00:00.000Z",
items: [
{ id: 1, weekDay: "lundi", meal: "Dîner", recipe: { id: 1, name: "Ratatouille" } },
{
id: 2,
weekDay: "mardi",
meal: "Déjeuner",
recipe: { id: 2, name: "Curry de lentilles" },
},
],
},
});
cy.visit("/");
cy.contains("Aucun planning pour cette semaine.").should("not.exist");
cy.get("table.planning-table tbody tr").should("have.length", 2);
cy.contains("td", "Ratatouille").should("be.visible");
cy.contains("td", "Curry de lentilles").should("be.visible");
});
it("shows an error state when the planning request fails", () => {
cy.intercept("GET", "**/auth/me", { statusCode: 200, body: authenticatedProfile });
cy.intercept("GET", "**/planning/current", {
statusCode: 500,
body: { code: 5000, message: "boom" },
});
cy.visit("/");
cy.contains("Impossible de charger le planning, réessayez plus tard").should("be.visible");
});
});