From b302481a4da694c97c8cb0a5fd03fd0abb7c0f6d Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Wed, 17 Jun 2026 07:55:35 -0400 Subject: [PATCH] Bug 42862: Use current-relative dates in bookingsModalDatePicker_spec.ts The booking date picker tests fake "today" with cy.clock and create bookings on hardcoded June 2026 dates. The modal fetches existing bookings from from the api and that query uses the real current time, not the fake one in the browser. Once the real date passes the hardcoded dates, the server stops returning those bookings ( they are now in the past ), so the modal sees no conflicts and assertions such as "the date should have the leadDisable class" fail. This patch anchors each test's "today" to the start of next month based on the current date so the bookings always stay in the future and are returned no matter when the test runs. The 'any item' test also sets cy.clock before its second page reload because the freeze does not survive the reload. Test Plan: 1) On main run: yarn cypress run --spec t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts 2) Note the test fails 3) Apply this patch 4) Run the command again 5) Note all tests pass! Signed-off-by: David Nind Signed-off-by: Lucas Gass Signed-off-by: Pedro Amorim --- .../bookingsModalDatePicker_spec.ts | 127 ++++++++++-------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts b/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts index fc7a0d1a93..1c35a7861a 100644 --- a/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts +++ b/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts @@ -735,11 +735,15 @@ describe("Booking Modal Date Picker Tests", () => { * Phase 5: Select June 13, hover June 20 (max) → Trail June 21-23 (clear) → selectable */ - // Fix the browser Date object to June 10, 2026 at 09:00 Europe/London - // Using ["Date"] to avoid freezing timers which breaks Select2 async operations - const fixedToday = new Date("2026-06-10T08:00:00Z"); // 09:00 BST (UTC+1) + // Anchor "today" to next month so bookings stay in the future ( the API drops past ones ) + // ["Date"] avoids freezing timers, which breaks Select2 + const fixedToday = dayjs() + .add(1, "month") + .startOf("month") + .hour(8) + .toDate(); cy.clock(fixedToday, ["Date"]); - cy.log(`Fixed today: June 10, 2026`); + const today = dayjs(fixedToday); // Circulation rules with short periods for focused testing const circulationRules = { @@ -759,9 +763,9 @@ describe("Booking Modal Date Picker Tests", () => { body: [circulationRules], }).as("getFixedDateRules"); - // Create blocker booking: June 25-27, 2026 - const blockerStart = "2026-06-25 00:00:00"; - const blockerEnd = "2026-06-27 23:59:59"; + // Create blocker booking 15-17 days out + const blockerStart = today.add(15, "day").format("YYYY-MM-DD 00:00:00"); + const blockerEnd = today.add(17, "day").format("YYYY-MM-DD 23:59:59"); cy.task("query", { sql: `INSERT INTO bookings (biblio_id, item_id, patron_id, start_date, end_date, pickup_library_id, status) @@ -775,7 +779,6 @@ describe("Booking Modal Date Picker Tests", () => { testData.libraries[0].library_id, ], }); - cy.log(`Blocker booking created: June 25-27, 2026`); // Setup modal setupModalForDateTesting({ skipItemSelection: true }); @@ -788,11 +791,11 @@ describe("Booking Modal Date Picker Tests", () => { cy.get("#period").as("fp"); cy.get("@fp").openFlatpickr(); - // Helper to get a specific date element by ISO date string - const getDateByISO = (isoDate: string) => { - const date = new Date(isoDate); - return cy.get("@fp").getFlatpickrDate(date); - }; + // Helper to get a flatpickr day cell N days from "today". + const getDate = (daysFromToday: number) => + cy + .get("@fp") + .getFlatpickrDate(today.add(daysFromToday, "day").toDate()); // ======================================================================== // PHASE 1: Lead Period Clear - Visual Classes @@ -805,19 +808,19 @@ describe("Booking Modal Date Picker Tests", () => { * Expected: leadRangeStart on June 11, leadRange on June 12, no leadDisable on June 13 */ - getDateByISO("2026-06-13").trigger("mouseover"); + getDate(3).trigger("mouseover"); // Check lead period classes - getDateByISO("2026-06-11") + getDate(1) .should("have.class", "leadRangeStart") .and("have.class", "leadRange"); - getDateByISO("2026-06-12") + getDate(2) .should("have.class", "leadRange") .and("have.class", "leadRangeEnd"); // Hovered date should NOT have leadDisable (lead period is clear) - getDateByISO("2026-06-13").should("not.have.class", "leadDisable"); + getDate(3).should("not.have.class", "leadDisable"); // ======================================================================== // PHASE 2: Trail Period Clear - Visual Classes @@ -833,24 +836,24 @@ describe("Booking Modal Date Picker Tests", () => { */ // Select June 13 as start date (same date we just hovered - lead is clear) - getDateByISO("2026-06-13").click(); + getDate(3).click(); // Hover June 16 as potential end date - getDateByISO("2026-06-16").trigger("mouseover"); + getDate(6).trigger("mouseover"); // Check trail period classes - getDateByISO("2026-06-17") + getDate(7) .should("have.class", "trailRangeStart") .and("have.class", "trailRange"); - getDateByISO("2026-06-18").should("have.class", "trailRange"); + getDate(8).should("have.class", "trailRange"); - getDateByISO("2026-06-19") + getDate(9) .should("have.class", "trailRangeEnd") .and("have.class", "trailRange"); // Hovered date (June 16) should NOT have trailDisable (trail period is clear) - getDateByISO("2026-06-16").should("not.have.class", "trailDisable"); + getDate(6).should("not.have.class", "trailDisable"); // Clear selection for next phase cy.get("#period").clearFlatpickr(); @@ -868,10 +871,10 @@ describe("Booking Modal Date Picker Tests", () => { * Expected: leadDisable on June 11 because lead period extends into past */ - getDateByISO("2026-06-11").trigger("mouseover"); + getDate(1).trigger("mouseover"); // June 11 should have leadDisable because lead period (June 9-10) includes past date - getDateByISO("2026-06-11").should("have.class", "leadDisable"); + getDate(1).should("have.class", "leadDisable"); /** * Hover June 29 as potential start date @@ -880,10 +883,10 @@ describe("Booking Modal Date Picker Tests", () => { * Expected: leadDisable on June 29 because lead period extends into existing booking */ - getDateByISO("2026-06-29").trigger("mouseover"); + getDate(19).trigger("mouseover"); // June 29 should have leadDisable because lead period (June 27-28) includes existing booking date - getDateByISO("2026-06-29").should("have.class", "leadDisable"); + getDate(19).should("have.class", "leadDisable"); // ======================================================================== // PHASE 3c: BIDIRECTIONAL - Lead Period Conflicts with Existing Booking TRAIL @@ -902,12 +905,12 @@ describe("Booking Modal Date Picker Tests", () => { */ // Hover July 1 - lead period (June 29-30) overlaps blocker's trail (June 28-30) - getDateByISO("2026-07-01").trigger("mouseover"); - getDateByISO("2026-07-01").should("have.class", "leadDisable"); + getDate(21).trigger("mouseover"); + getDate(21).should("have.class", "leadDisable"); // Hover July 2 - lead period (June 30-July 1) still overlaps blocker's trail at June 30 - getDateByISO("2026-07-02").trigger("mouseover"); - getDateByISO("2026-07-02").should("have.class", "leadDisable"); + getDate(22).trigger("mouseover"); + getDate(22).should("have.class", "leadDisable"); // ======================================================================== // PHASE 3d: First Clear Start Date After Blocker's Protected Period @@ -918,8 +921,8 @@ describe("Booking Modal Date Picker Tests", () => { * - July 3: Lead July 1-2 → completely clear of blocker trail (ends June 30) → no leadDisable */ - getDateByISO("2026-07-03").trigger("mouseover"); - getDateByISO("2026-07-03").should("not.have.class", "leadDisable"); + getDate(23).trigger("mouseover"); + getDate(23).should("not.have.class", "leadDisable"); // ======================================================================== // PHASE 4a: Trail Period Conflict - Existing Booking ACTUAL Dates @@ -934,13 +937,13 @@ describe("Booking Modal Date Picker Tests", () => { */ // Select June 20 as start date - getDateByISO("2026-06-20").click(); + getDate(10).click(); // Hover June 23 as potential end date - getDateByISO("2026-06-23").trigger("mouseover"); + getDate(13).trigger("mouseover"); // June 23 should have trailDisable because trail period (June 24-26) overlaps blocker ACTUAL (June 25-27) - getDateByISO("2026-06-23").should("have.class", "trailDisable"); + getDate(13).should("have.class", "trailDisable"); // Clear selection for next phase cy.get("#period").clearFlatpickr(); @@ -964,21 +967,21 @@ describe("Booking Modal Date Picker Tests", () => { */ // Select June 13 as start date (lead June 11-12, both clear) - getDateByISO("2026-06-13").click(); + getDate(3).click(); // Hover June 21 as potential end date // Trail period: June 22-24, Blocker LEAD: June 23-24 // Overlap at June 23-24 → should have trailDisable - getDateByISO("2026-06-21").trigger("mouseover"); - getDateByISO("2026-06-21").should("have.class", "trailDisable"); + getDate(11).trigger("mouseover"); + getDate(11).should("have.class", "trailDisable"); // Also test June 20 - trail June 21-23, June 23 overlaps blocker lead - getDateByISO("2026-06-20").trigger("mouseover"); - getDateByISO("2026-06-20").should("have.class", "trailDisable"); + getDate(10).trigger("mouseover"); + getDate(10).should("have.class", "trailDisable"); // Verify June 19 is clear - trail June 20-22, doesn't reach blocker lead (starts June 23) - getDateByISO("2026-06-19").trigger("mouseover"); - getDateByISO("2026-06-19").should("not.have.class", "trailDisable"); + getDate(9).trigger("mouseover"); + getDate(9).should("not.have.class", "trailDisable"); // Clear selection for next phase cy.get("#period").clearFlatpickr(); @@ -999,24 +1002,21 @@ describe("Booking Modal Date Picker Tests", () => { */ // Select June 13 as start date - getDateByISO("2026-06-13").click(); + getDate(3).click(); // First, verify June 20 HAS trailDisable (trail June 21-23 overlaps blocker lead June 23-24) - getDateByISO("2026-06-20").trigger("mouseover"); - getDateByISO("2026-06-20").should("have.class", "trailDisable"); + getDate(10).trigger("mouseover"); + getDate(10).should("have.class", "trailDisable"); // June 19 should NOT have trailDisable (trail June 20-22 is clear of blocker lead) - getDateByISO("2026-06-19").trigger("mouseover"); - getDateByISO("2026-06-19").should("not.have.class", "trailDisable"); + getDate(9).trigger("mouseover"); + getDate(9).should("not.have.class", "trailDisable"); // June 19 should not be disabled by flatpickr - getDateByISO("2026-06-19").should( - "not.have.class", - "flatpickr-disabled" - ); + getDate(9).should("not.have.class", "flatpickr-disabled"); // Actually select June 19 to confirm booking can be made - getDateByISO("2026-06-19").click(); + getDate(9).click(); // Verify dates were accepted in the form cy.get("#booking_start_date").should("not.have.value", ""); @@ -1274,9 +1274,13 @@ describe("Booking Modal Date Picker Tests", () => { * - ITEM 3: Booked days 1-7, then 23-30 */ - // Fix the browser Date object to June 10, 2026 at 09:00 Europe/London - // Using ["Date"] to avoid freezing timers which breaks Select2 async operations - const fixedToday = new Date("2026-06-10T08:00:00Z"); // 09:00 BST (UTC+1) + // Anchor "today" to next month so bookings stay in the future ( the API drops past ones ) + // ["Date"] avoids freezing timers, which breaks Select2 + const fixedToday = dayjs() + .add(1, "month") + .startOf("month") + .hour(8) + .toDate(); cy.clock(fixedToday, ["Date"]); const today = dayjs(fixedToday); @@ -1581,9 +1585,12 @@ describe("Booking Modal Date Picker Tests", () => { * 4. Visual feedback: Check existingBookingLead on days 8-9 (June 9-10) */ - // Fix the browser Date object to June 1, 2026 at 09:00 Europe/London - // This ensures all test dates (days 5-17) fall within June - const fixedToday = new Date("2026-06-01T08:00:00Z"); // 09:00 BST (UTC+1) + // Anchor "today" to next month so bookings stay in the future ( the API drops past ones ) + const fixedToday = dayjs() + .add(1, "month") + .startOf("month") + .hour(8) + .toDate(); cy.clock(fixedToday, ["Date"]); const today = dayjs(fixedToday); @@ -1776,6 +1783,8 @@ describe("Booking Modal Date Picker Tests", () => { "new", ], }).then(() => { + // Re-arm the frozen clock so the reloaded page loads with the faked today + cy.clock(fixedToday, ["Date"]); // Reload page to get updated booking data cy.visit( `/cgi-bin/koha/catalogue/detail.pl?biblionumber=${testBiblio.biblio_id}`