import "../../src/i18n/i18n"; import { TechStepCorrectionPopover } from "../../src/features/recipes/steps/TechStepCorrectionPopover"; // Mounts the popover in isolation (no StepDescription/selection plumbing // around it) — same "generic component test" posture as CheckboxOption.cy.tsx, // but this one needs `../../src/i18n/i18n` imported for its side effect // (initializes the default i18next instance `useTranslation` falls back to // with no `` in the tree — see that module's own doc // comment) since, unlike Checkbox/Radio, this component calls // `useTranslation()`. const cook = { id: 1, key: "cook" }; const simmer = { id: 3, key: "simmer" }; function mountPopover( overrides: Partial<{ previousTechStepId: number | null; onClose: () => void; onSubmitted: (correction: unknown) => void; }> = {}, ) { cy.mount(
{/* A genuinely separate sibling to click for the "outside click closes it" test — clicking blindly at a viewport coordinate would risk still landing inside the popover, which fills most of the mounted area on its own. */}
{})} onSubmitted={overrides.onSubmitted ?? (() => {})} />
, ); } describe("TechStepCorrectionPopover", () => { beforeEach(() => { cy.intercept("GET", "**/reference/tech-steps", { statusCode: 200, body: [cook, simmer] }).as( "getTechSteps", ); }); it("shows the selected text and every technique option once loaded", () => { mountPopover(); cy.wait("@getTechSteps"); cy.contains(".tech-step-correction-popover__selection", "Cuire").should("be.visible"); cy.get(".tech-step-correction-popover__list button").should("have.length", 2); }); it("offers a 'no technique here' option only when correcting an existing match", () => { mountPopover({ previousTechStepId: null }); cy.wait("@getTechSteps"); cy.get(".tech-step-correction-popover__remove").should("not.exist"); mountPopover({ previousTechStepId: cook.id }); cy.wait("@getTechSteps"); cy.get(".tech-step-correction-popover__remove").should("exist"); }); it("submits the selected technique and calls onSubmitted", () => { // Asserting on the resolved `@submitCorrection` interception below, // rather than inside this handler — a Chai assertion failing *inside* // a `cy.intercept` callback surfaces as an opaque "onResponse cannot be // called twice" Cypress internal error instead of a normal assertion // failure, found while writing this exact test. cy.intercept("POST", "**/recipes/2/steps/2/corrections", { statusCode: 201, body: { id: 1, start: 0, end: 5, previousTechStep: null, correctedTechStep: simmer, createdAt: new Date().toISOString(), }, }).as("submitCorrection"); const onSubmitted = cy.stub().as("onSubmitted"); mountPopover({ onSubmitted }); cy.wait("@getTechSteps"); cy.contains(".tech-step-correction-popover__list button", "Mijoter").click(); cy.wait("@submitCorrection").its("request.body").should("deep.equal", { start: 0, end: 5, previousTechStepId: null, correctedTechStepId: simmer.id, }); cy.get("@onSubmitted").should("have.been.calledOnce"); }); it("shows an error message and stays open when the submission fails", () => { cy.intercept("POST", "**/recipes/2/steps/2/corrections", { statusCode: 404, body: { code: 4051, message: "TechStep not found" }, }).as("submitCorrection"); const onClose = cy.stub().as("onClose"); mountPopover({ onClose }); cy.wait("@getTechSteps"); cy.contains(".tech-step-correction-popover__list button", "Cuire").click(); cy.wait("@submitCorrection"); cy.get(".field-error").should("be.visible"); cy.get("@onClose").should("not.have.been.called"); }); it("calls onClose on an outside click", () => { const onClose = cy.stub().as("onClose"); mountPopover({ onClose }); cy.wait("@getTechSteps"); cy.get('[data-testid="outside-popover"]').click(); cy.get("@onClose").should("have.been.calledOnce"); }); });