import { Given, Then, When } from "@badeball/cypress-cucumber-preprocessor"; // Mocks the API via cy.intercept — this job has no live backend (see // .github/workflows/ci.yml); apps/api's Mocha suite covers the real // leftover endpoints. Drives @dnd-kit's MouseSensor with raw mouse // events (mousedown on the handle → two mousemoves → mouseup on the // target), the reliable way to script one of its drags in Cypress. /** One fresh batch: 12 prepared for 10 requested -> a pool of 2, no leftovers item pointing at it yet. */ const SOURCE_ITEM = { id: 1, weekDay: "lundi", meal: "diner", portions: 12, portionsRequested: 10, discardedPortions: 0, sourceItem: null, recipe: { id: 1, name: "Lasagnes" }, }; Given("the planning has a batch with leftovers to spare", () => { cy.intercept("GET", "**/profile/disliked-ingredients", { statusCode: 200, body: [] }); cy.intercept("GET", /\/planning\?/, { statusCode: 200, body: { id: 1, startDate: "2026-08-17T00:00:00.000Z", finishDate: "2026-08-23T00:00:00.000Z", items: [SOURCE_ITEM], }, }).as("getPlanning"); }); When("I open the leftovers basket", () => { cy.get(".leftovers-basket__toggle").click(); }); Then( "the leftovers basket lists {string} with {int} portions", (recipe: string, portions: number) => { cy.contains(".leftovers-basket__line", recipe).should("contain.text", `${portions} en restes`); }, ); /** dnd-kit's MouseSensor listens on the drag element for `mousedown`, then on the document for `mousemove`/`mouseup`; a first move past the 5px activation distance, then a move onto the target, then release. */ function dragLeftoverOnto(targetSelectorChain: () => Cypress.Chainable>) { cy.get(".leftovers-basket__drag-handle") .first() .then(($handle) => { const h = $handle[0].getBoundingClientRect(); const start = { clientX: Math.round(h.left + h.width / 2), clientY: Math.round(h.top + h.height / 2), }; targetSelectorChain().then(($target) => { const t = $target[0].getBoundingClientRect(); const end = { clientX: Math.round(t.left + t.width / 2), clientY: Math.round(t.top + t.height / 2), }; cy.wrap($handle).trigger("mousedown", { button: 0, ...start }); cy.get("body") .trigger("mousemove", { button: 0, clientX: start.clientX + 12, clientY: start.clientY + 12, }) .trigger("mousemove", { button: 0, ...end }) .trigger("mouseup", { button: 0, ...end }); }); }); } /** The `` for (Wednesday, lunch): MEALS order puts `dejeuner` at row index 2, WEEK_DAYS puts `mercredi` at td index 2. */ function wednesdayLunchCell() { return cy.get(".planning-grid tbody tr").eq(2).find("td").eq(2); } When("I drag the {string} leftover onto Wednesday lunch", (_recipe: string) => { cy.intercept("POST", "**/planning/items/leftover", (req) => { req.reply({ statusCode: 201, body: { id: 99, weekDay: "mercredi", meal: "dejeuner", portions: req.body.portions, portionsRequested: req.body.portions, discardedPortions: 0, sourceItem: { id: req.body.sourceItemId }, recipe: { id: 1, name: "Lasagnes" }, }, }); }).as("assignLeftover"); dragLeftoverOnto(wednesdayLunchCell); }); When("I confirm serving {int} leftover portions", (portions: number) => { cy.get("#leftover-assign-portions").clear().type(String(portions)); cy.contains(".recipe-picker-confirm__confirm", "Ajouter au planning").click(); cy.wait("@assignLeftover").its("request.body").should("deep.include", { sourceItemId: 1, weekDay: "mercredi", meal: "dejeuner", portions, }); }); Then("Wednesday lunch shows a leftovers chip for {string}", (recipe: string) => { wednesdayLunchCell() .find(".recipe-chip--leftover") .should("contain.text", recipe) .and("contain.text", "♻️"); }); Then("the leftovers basket count is gone", () => { cy.get(".leftovers-basket__count").should("not.exist"); }); When("I throw away the {string} leftover", (recipe: string) => { cy.intercept("PATCH", "**/planning/items/1/discard-leftover", { statusCode: 200, body: { ...SOURCE_ITEM, discardedPortions: 2 }, }).as("discard"); cy.contains(".leftovers-basket__line", recipe).find(".leftovers-basket__discard").click(); cy.wait("@discard"); }); Then("the leftovers basket shows nothing left", () => { cy.contains(".leftovers-basket__empty", "Aucun reste cette semaine.").should("be.visible"); });