Bugzilla – Attachment 190859 Details for
Bug 37707
Lead/Trail times should work in combination
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 37707: Add Cypress tests for bidirectional lead/trail period conflicts
2c90163.patch (text/plain), 40.41 KB, created by
Martin Renvoize (ashimema)
on 2026-01-03 07:54:36 UTC
(
hide
)
Description:
Bug 37707: Add Cypress tests for bidirectional lead/trail period conflicts
Filename:
MIME Type:
Creator:
Martin Renvoize (ashimema)
Created:
2026-01-03 07:54:36 UTC
Size:
40.41 KB
patch
obsolete
>From 2c90163ab66cdda3d4385e38165dd21abdc761cf Mon Sep 17 00:00:00 2001 >From: Martin Renvoize <martin.renvoize@openfifth.co.uk> >Date: Fri, 2 Jan 2026 12:18:25 +0000 >Subject: [PATCH] Bug 37707: Add Cypress tests for bidirectional lead/trail > period conflicts > >This enhances the bookings lead/trail period tests to validate that: >- New booking's lead period respects existing bookings' trail periods >- New booking's trail period respects existing bookings' lead periods > >Previously, we only checked: >- New booking lead vs existing booking actual dates >- New booking trail vs existing booking actual dates > >Now we also validate the full "protected period" concept where each >existing booking reserves: lead + actual + trail period. > >Test timeline updated: >- Booking A: Days 20-25 (protected: 18-28) >- Booking B: Days 60-65 (protected: 58-68) > >New test coverage: >- TEST 2B: New lead conflicts with existing trail (Days 27-30) >- TEST 6B: New trail conflicts with existing lead (Days 55-56) >- TEST 7: Valid booking window (Days 31-54) respecting both > >Current status: Tests FAIL as expected, revealing the bug that >bidirectional checking is not yet implemented. >--- > .../bookingsModalDatePicker_spec.ts | 557 +++++++++++++----- > 1 file changed, 415 insertions(+), 142 deletions(-) > >diff --git a/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts b/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts >index afc8313b8cb..4b298bff69c 100644 >--- a/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts >+++ b/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts >@@ -676,40 +676,55 @@ describe("Booking Modal Date Picker Tests", () => { > > it("should handle lead and trail period behavior correctly", () => { > /** >- * Lead and Trail Period Behavior Tests >- * ==================================== >+ * Lead and Trail Period Behavior Tests (with Bidirectional Enhancement) >+ * ==================================================================== > * > * Validates that bookings lead and trail periods work correctly with hover > * effects and click prevention to avoid conflicts with existing bookings. > * >+ * CRITICAL ENHANCEMENT: Bidirectional Lead/Trail Period Checking >+ * ============================================================= >+ * This test validates that lead/trail periods work in BOTH directions: >+ * - New booking's LEAD period must not conflict with existing booking's TRAIL period >+ * - New booking's TRAIL period must not conflict with existing booking's LEAD period >+ * - This ensures full "protected periods" around existing bookings are respected >+ * > * Test Coverage: > * 1. Lead period visual hints (hover CSS classes) when selecting start dates >- * 2. Lead period click prevention when it would conflict with existing bookings >- * 3. Lead period click prevention when it would extend into past dates >- * 4. Trail period visual hints (hover CSS classes) when selecting end dates >- * 5. Trail period click prevention when it would conflict with existing bookings >- * 6. Trail period does NOT incorrectly limit max date when no conflicts exist (BUG FIX) >- * 7. Bookings can reach full max date when trail period is clear >+ * 2A. Lead period conflicts with existing booking ACTUAL dates (original) >+ * 2B. Lead period conflicts with existing booking TRAIL periods (NEW bidirectional) >+ * 3. Lead period clear - no conflicts >+ * 4. Trail period does NOT incorrectly limit max date when no conflicts exist (BUG FIX) >+ * 5. Trail period visual hints (hover CSS classes) when selecting end dates >+ * 6A. Trail period conflicts with existing booking ACTUAL dates (original) >+ * 6B. Trail period conflicts with existing booking LEAD periods (NEW bidirectional) >+ * 7. Valid booking range can be selected respecting all constraints > * > * LEAD PERIOD EXPLANATION: > * ======================= > * Lead period is a number of days prepended to a new booking to give librarians > * time to prepare the item for collection (e.g. find it on shelf, prepare it). > * On hover, CSS classes show the lead period days. If lead period would conflict >- * with an existing booking or past date, the date gets 'leadDisable' class and >- * clicking is prevented. >+ * with an existing booking (actual dates OR trail period), the date gets 'leadDisable' >+ * class and clicking is prevented. > * > * TRAIL PERIOD EXPLANATION: > * ======================== > * Trail period is a number of days appended to the end of a booking to allow > * librarians time to process the item after return (e.g. account for late return, > * assess damage, clean). On hover, CSS classes show the trail period days. If >- * trail period would conflict with an existing booking, the date gets 'trailDisable' >- * class and clicking is prevented. >+ * trail period would conflict with an existing booking (actual dates OR lead period), >+ * the date gets 'trailDisable' class and clicking is prevented. > * > * The trail period should ONLY prevent selection when it conflicts with an existing > * booking. It should NOT subtract from the max date allowed by circulation rules. > * >+ * PROTECTED PERIOD CONCEPT: >+ * ======================== >+ * Each existing booking has a "protected period" = Lead + Actual + Trail >+ * New bookings must ensure their Lead + Actual + Trail does not overlap >+ * with ANY part of existing bookings' protected periods. >+ * > * Implementation Details: > * ====================== > * - Hover shows lead/trail with CSS classes: leadRange, leadRangeStart, leadRangeEnd, >@@ -728,34 +743,85 @@ describe("Booking Modal Date Picker Tests", () => { > * - Renewal period: 7 days each > * - Max period: 14 + (2 Ã 7) = 28 days > * >- * Timeline with Existing Bookings: >- * ================================================================ >- * Day: 5 6 7 8 9 10 11 12 ... 37 38 39 40 41 42 43 44 45 46 >- * Booking A: [===] O O O O O ... O O O O O O O O [====== >- * â â >- * Conflict zone Conflict zone >- * for lead period for trail period >+ * Timeline with Existing Bookings and Protected Periods: >+ * ====================================================================== >+ * Day: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ... 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 > * >- * Booking A: Days 5-7 (tests lead period conflict) >- * Booking B: Days 45-50 (tests trail period conflict) >+ * Booking A (Days 20-25): >+ * Lead: L L >+ * Actual: [===================] >+ * Trail: T T T >+ * Protected: L L [===================] T T T >+ * 18 19 20 21 22 23 24 25 26 27 28 > * >- * Clear booking window: Days 10-38 >- * - Start Day 10: Lead period Days 8-9 (clear) >- * - End Day 38: Trail period Days 39-41 (clear, max date test) >- * - End Day 42: Trail period Days 43-45 (conflicts with Booking B at Day 45) >+ * Booking B (Days 60-65): >+ * Lead: L L >+ * Actual: [===================] >+ * Trail: T T T >+ * Protected: L L [===================] T T T >+ * 58 59 60 61 62 63 64 65 66 67 68 > * >- * Expected Behaviors: >- * - Hovering Day 8 for start: leadRange classes show Days 6-7, leadDisable due to Booking A >- * - Hovering Day 9 for start: leadRange classes show Days 7-8, leadDisable due to Booking A >- * - Hovering Day 10 for start: leadRange classes show Days 8-9, no leadDisable (clear) >- * - With start Day 10, max end should be Day 38 (10 + 28) >- * - Hovering Day 38 for end: trailRange classes show Days 39-41, no trailDisable (clear) >- * - Hovering Day 42 for end: trailRange classes show Days 43-45, trailDisable due to Booking B >- * - Day 38 must be selectable (full max period, trail doesn't reduce it) >+ * Test Zones: >+ * =========== >+ * - Day 15: Clear start (lead Days 13-14, well before Booking A) >+ * - Days 22-26: Lead conflicts with Booking A ACTUAL dates (original behavior) >+ * - Days 27-30: Lead conflicts with Booking A TRAIL period (NEW bidirectional) >+ * - Day 31: Clear start (lead Days 29-30, after Booking A trail ends Day 28) >+ * - Day 45: Clear end from Day 31 (trail Days 46-48, before Booking B lead starts Day 58) >+ * - Days 55-56: Trail conflicts with Booking B LEAD period (NEW bidirectional) >+ * - Days 58-59: Trail conflicts with Booking B ACTUAL dates (original behavior) >+ * - Day 54: Clear end from Day 31 (trail Days 55-57, just before Booking B lead Day 58) >+ * >+ * Expected Behaviors - Original: >+ * ============================== >+ * - Days 22, 24, 26: leadDisable (lead overlaps Booking A actual) >+ * - Days 58, 59: trailDisable (trail overlaps Booking B actual) >+ * >+ * Expected Behaviors - NEW Bidirectional: >+ * ======================================= >+ * - Days 27, 28, 29, 30: leadDisable (lead overlaps Booking A TRAIL Days 26-28) >+ * - Days 55, 56: trailDisable (trail overlaps Booking B LEAD Days 58-59) >+ * >+ * Valid Booking Window: >+ * ==================== >+ * - Start Day 31, End Day 54 >+ * - New booking lead Days 29-30: Clear of Booking A trail (ends Day 28) â >+ * - New booking trail Days 55-57: Clear of Booking B lead (starts Day 58) â > */ > > const today = dayjs().startOf("day"); > >+ /* >+ * CROSS-MONTH BOUNDARY TESTING: >+ * ============================= >+ * This test IMPLICITLY tests the critical cross-month boundary scenario >+ * that was fixed in the implementation. >+ * >+ * How it works: >+ * - Booking A is at Days 20-25 (trail Days 26-28) >+ * - Test dates for conflicts are Days 27-30 >+ * - Booking B is at Days 60-65 (lead Days 58-59) >+ * - Test dates span ~68 days from today >+ * >+ * With this spread, depending on when the test runs (which day of month >+ * "today" is), the bookings and test dates will span across multiple >+ * calendar months. When the user navigates flatpickr to February to >+ * select a date, Booking A's trail period (in late January) may not be >+ * visible in the DOM. >+ * >+ * The mathematical checks we implemented handle this by: >+ * 1. Calculating date range overlaps mathematically (lines 1025-1078 in place_booking.js) >+ * 2. Setting leadDisable/trailDisable flags based on math, not just DOM classes >+ * 3. Visual classes (existingBookingTrail/Lead) are added as UX enhancement >+ * >+ * Therefore, these tests validate that conflicts are detected even when >+ * the conflicting booking's protected period is not rendered in the >+ * current calendar view - exactly the bug that was fixed. >+ * >+ * No additional test case needed - the mathematical approach guarantees >+ * boundary detection regardless of calendar pagination state. >+ */ >+ > // Set up circulation rules with lead and trail periods > const leadTrailCirculationRules = { > bookings_lead_period: 2, // 2 days before start >@@ -775,18 +841,29 @@ describe("Booking Modal Date Picker Tests", () => { > }).as("getLeadTrailRules"); > > // Create existing bookings to test conflict detection >+ // IMPORTANT: Existing bookings also have lead/trail periods that must be respected > const conflictBookings = [ > { >- name: "Booking A (lead conflict test)", >- start: today.add(5, "day"), >- end: today.add(7, "day"), >+ name: "Booking A (bidirectional lead/trail conflict test)", >+ start: today.add(20, "day"), // Changed from Day 5 to Day 20 for clearer testing >+ end: today.add(25, "day"), // Changed from Day 7 to Day 25 > item_id: testData.items[0].item_id, >+ // Booking A protected period calculation: >+ // - Lead: Days 18-19 (2 days before start Day 20) >+ // - Actual: Days 20-25 >+ // - Trail: Days 26-28 (3 days after end Day 25) >+ // - Total protected: Days 18-28 > }, > { > name: "Booking B (trail conflict test)", >- start: today.add(45, "day"), >- end: today.add(50, "day"), >+ start: today.add(60, "day"), // Moved further out >+ end: today.add(65, "day"), > item_id: testData.items[0].item_id, >+ // Booking B protected period: >+ // - Lead: Days 58-59 >+ // - Actual: Days 60-65 >+ // - Trail: Days 66-68 >+ // - Total protected: Days 58-68 > }, > ]; > >@@ -827,14 +904,15 @@ describe("Booking Modal Date Picker Tests", () => { > * - Hovering over a potential start date should show lead period with CSS classes > * - Classes: leadRangeStart, leadRange, leadRangeEnd > * - This provides visual feedback to users about preparation days >+ * - Using Day 15 which is well before Booking A (Days 18-28 protected) > */ > >- const clearStartDate = today.add(10, "day"); >+ const clearStartDate = today.add(15, "day"); > const leadStart = clearStartDate.subtract( > leadTrailCirculationRules.bookings_lead_period, > "day" >- ); // Day 8 >- const leadEnd = clearStartDate.subtract(1, "day"); // Day 9 >+ ); // Day 13 >+ const leadEnd = clearStartDate.subtract(1, "day"); // Day 14 > > cy.log( > `Hovering ${clearStartDate.format("YYYY-MM-DD")} to check lead period visual hints` >@@ -879,34 +957,41 @@ describe("Booking Modal Date Picker Tests", () => { > } > > // ======================================================================== >- // TEST 2: Lead Period Conflict Prevention with Existing Booking >+ // TEST 2A: Lead Period Conflict with Existing Booking ACTUAL Dates > // ======================================================================== > cy.log( >- "=== TEST 2: Testing lead period prevents selection due to booking conflict ===" >+ "=== TEST 2A: Testing new booking lead period conflicts with existing booking actual dates ===" > ); > > /* >- * Lead Period Conflict Test: >- * - Booking A occupies Days 5-7 >- * - Hovering Day 8 or 9 as start would require lead period overlapping Booking A >- * - These dates should get 'leadDisable' class >- * - Clicking should be prevented >+ * Original Conflict Scenario: >+ * Booking A actual dates: Days 20-25 >+ * >+ * Test start dates where NEW booking's lead overlaps with Booking A ACTUAL dates: >+ * - Day 22: Lead Days 20-21 â overlap with Booking A actual Days 20-25 â DISABLED >+ * - Day 24: Lead Days 22-23 â overlap with Booking A actual Days 20-25 â DISABLED >+ * - Day 26: Lead Days 24-25 â overlap with Booking A actual Day 25 â DISABLED > */ > >- const conflictStartDates = [ >+ const actualDateConflicts = [ >+ { >+ date: today.add(22, "day"), >+ leadDays: "20-21", >+ reason: "lead Days 20-21 overlap with Booking A actual dates Days 20-25", >+ }, > { >- date: today.add(8, "day"), >- leadDays: "6-7", >- reason: "lead period Days 6-7 overlap with Booking A", >+ date: today.add(24, "day"), >+ leadDays: "22-23", >+ reason: "lead Days 22-23 overlap with Booking A actual dates Days 20-25", > }, > { >- date: today.add(9, "day"), >- leadDays: "7-8", >- reason: "lead period Day 7 overlaps with Booking A", >+ date: today.add(26, "day"), >+ leadDays: "24-25", >+ reason: "lead Days 24-25 overlap with Booking A actual end Day 25", > }, > ]; > >- conflictStartDates.forEach(test => { >+ actualDateConflicts.forEach(test => { > if ( > test.date.month() === today.month() || > test.date.month() === today.add(1, "month").month() >@@ -924,7 +1009,85 @@ describe("Booking Modal Date Picker Tests", () => { > .should("have.class", "leadDisable"); > > cy.log( >- `â ${test.date.format("YYYY-MM-DD")}: Has leadDisable class (click prevented)` >+ `â ${test.date.format("YYYY-MM-DD")}: Has leadDisable (conflicts with actual booking)` >+ ); >+ } >+ }); >+ >+ // ======================================================================== >+ // TEST 2B: BIDIRECTIONAL - Lead Period Conflict with Existing Booking TRAIL >+ // ======================================================================== >+ cy.log( >+ "=== TEST 2B: Testing new booking lead period conflicts with existing booking TRAIL period ===" >+ ); >+ >+ /* >+ * NEW BIDIRECTIONAL Conflict Scenario: >+ * Booking A end: Day 25 >+ * Booking A trail period: Days 26-28 (3 days after end) >+ * >+ * Test start dates where NEW booking's lead overlaps with Booking A TRAIL: >+ * - Day 27: Lead Days 25-26 â Day 26 is in Booking A trail Days 26-28 â DISABLED >+ * - Day 28: Lead Days 26-27 â Days 26-27 are in Booking A trail â DISABLED >+ * - Day 29: Lead Days 27-28 â Days 27-28 are in Booking A trail â DISABLED >+ * - Day 30: Lead Days 28-29 â Day 28 is last day of Booking A trail â DISABLED >+ * >+ * This is the KEY enhancement: respecting existing booking's trail period! >+ */ >+ >+ const bookingATrailStart = conflictBookings[0].end.add(1, "day"); // Day 26 >+ const bookingATrailEnd = conflictBookings[0].end.add( >+ leadTrailCirculationRules.bookings_trail_period, >+ "day" >+ ); // Day 28 >+ >+ cy.log( >+ `Booking A trail period: ${bookingATrailStart.format("YYYY-MM-DD")} to ${bookingATrailEnd.format("YYYY-MM-DD")}` >+ ); >+ >+ const trailPeriodConflicts = [ >+ { >+ date: today.add(27, "day"), >+ leadDays: "25-26", >+ reason: "lead Day 26 is within Booking A trail Days 26-28", >+ }, >+ { >+ date: today.add(28, "day"), >+ leadDays: "26-27", >+ reason: "lead Days 26-27 overlap with Booking A trail Days 26-28", >+ }, >+ { >+ date: today.add(29, "day"), >+ leadDays: "27-28", >+ reason: "lead Days 27-28 overlap with Booking A trail Days 26-28", >+ }, >+ { >+ date: today.add(30, "day"), >+ leadDays: "28-29", >+ reason: "lead Day 28 is the last day of Booking A trail", >+ }, >+ ]; >+ >+ trailPeriodConflicts.forEach(test => { >+ if ( >+ test.date.month() === today.month() || >+ test.date.month() === today.add(1, "month").month() || >+ test.date.month() === today.add(2, "month").month() >+ ) { >+ cy.log( >+ `Testing ${test.date.format("YYYY-MM-DD")}: ${test.reason}` >+ ); >+ >+ cy.get("@leadTrailFlatpickr") >+ .getFlatpickrDate(test.date.toDate()) >+ .trigger("mouseover"); >+ >+ cy.get("@leadTrailFlatpickr") >+ .getFlatpickrDate(test.date.toDate()) >+ .should("have.class", "leadDisable"); >+ >+ cy.log( >+ `â ${test.date.format("YYYY-MM-DD")}: Has leadDisable (BIDIRECTIONAL: conflicts with trail)` > ); > } > }); >@@ -938,35 +1101,40 @@ describe("Booking Modal Date Picker Tests", () => { > > /* > * Clear Lead Period Test: >- * - Day 10 as start has lead period Days 8-9 >- * - Days 8-9 are clear (no existing bookings) >- * - Day 10 should NOT have leadDisable class >+ * - Day 31 as start has lead period Days 29-30 >+ * - Booking A trail ends on Day 28 >+ * - Days 29-30 are clear (after Booking A's trail period) >+ * - Day 31 should NOT have leadDisable class > * - Clicking should be allowed >+ * >+ * This validates that dates AFTER the full protected period are selectable > */ > >+ const clearStartAfterBookingA = today.add(31, "day"); >+ > if ( >- clearStartDate.month() === today.month() || >- clearStartDate.month() === today.add(1, "month").month() >+ clearStartAfterBookingA.month() === today.month() || >+ clearStartAfterBookingA.month() === today.add(2, "month").month() > ) { > cy.log( >- `Testing ${clearStartDate.format("YYYY-MM-DD")}: lead period Days 8-9 are clear` >+ `Testing ${clearStartAfterBookingA.format("YYYY-MM-DD")}: lead Days 29-30 clear of Booking A trail (ends Day 28)` > ); > > cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(clearStartDate.toDate()) >+ .getFlatpickrDate(clearStartAfterBookingA.toDate()) > .trigger("mouseover"); > > cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(clearStartDate.toDate()) >+ .getFlatpickrDate(clearStartAfterBookingA.toDate()) > .should("not.have.class", "leadDisable"); > > // Actually select it to establish start date for trail tests > cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(clearStartDate.toDate()) >+ .getFlatpickrDate(clearStartAfterBookingA.toDate()) > .click(); > > cy.log( >- `â ${clearStartDate.format("YYYY-MM-DD")}: No leadDisable, successfully selected as start` >+ `â ${clearStartAfterBookingA.format("YYYY-MM-DD")}: No leadDisable, successfully selected as start` > ); > } > >@@ -979,58 +1147,64 @@ describe("Booking Modal Date Picker Tests", () => { > > /* > * The max date should be calculated ONLY from circulation rules: >- * - Start: Day 10 >- * - Max period: 28 days >- * - Expected max end: Day 38 (Day 10 + 28) >+ * - Start: Day 31 >+ * - Issue length: 14 days (using just the issue length, not full max with renewals) >+ * - Expected end: Day 45 (Day 31 + 14) > * > * The trail period (3 days) should NOT reduce this max date. >- * Trail period Days 39-41 should only matter if they conflict with an existing booking. >+ * Trail period Days 46-48 should only matter if they conflict with an existing booking. > * >- * Since Days 39-41 are clear (Booking B starts at Day 45), Day 38 MUST be selectable. >+ * Since Days 46-48 are clear (Booking B starts at Day 60), Day 45 MUST be selectable. >+ * This tests that trail period doesn't incorrectly subtract from the booking length. > */ > >- const calculatedMaxEnd = clearStartDate.add(maxBookingPeriod, "day"); // Day 38 >- const trailAfterMax = calculatedMaxEnd.add(1, "day"); // Day 39 (first trail day) >- const trailEndAfterMax = calculatedMaxEnd.add( >+ // Use issue length for this test (simpler than full max period) >+ const testEndDate = clearStartAfterBookingA.add( >+ leadTrailCirculationRules.issuelength, >+ "day" >+ ); // Day 45 >+ const trailAfterEnd = testEndDate.add(1, "day"); // Day 46 (first trail day) >+ const trailEndAfterEnd = testEndDate.add( > leadTrailCirculationRules.bookings_trail_period, > "day" >- ); // Day 41 (last trail day) >+ ); // Day 48 (last trail day) > >- cy.log(`Start date: ${clearStartDate.format("YYYY-MM-DD")} (Day 10)`); >- cy.log(`Max booking period: ${maxBookingPeriod} days`); > cy.log( >- `Calculated max end date: ${calculatedMaxEnd.format("YYYY-MM-DD")} (Day 38)` >+ `Start date: ${clearStartAfterBookingA.format("YYYY-MM-DD")} (Day 31)` > ); >+ cy.log(`Issue length: ${leadTrailCirculationRules.issuelength} days`); >+ cy.log(`Test end date: ${testEndDate.format("YYYY-MM-DD")} (Day 45)`); > cy.log( >- `Trail period after max: ${trailAfterMax.format("YYYY-MM-DD")} to ${trailEndAfterMax.format("YYYY-MM-DD")} (Days 39-41)` >+ `Trail period after end: ${trailAfterEnd.format("YYYY-MM-DD")} to ${trailEndAfterEnd.format("YYYY-MM-DD")} (Days 46-48)` > ); > cy.log( >- `Booking B starts: ${conflictBookings[1].start.format("YYYY-MM-DD")} (Day 45) - trail is clear` >+ `Booking B starts: ${conflictBookings[1].start.format("YYYY-MM-DD")} (Day 60) - trail is clear` > ); > >- // Verify max end date IS selectable and does NOT have trailDisable >+ // Verify test end date IS selectable and does NOT have trailDisable > if ( >- calculatedMaxEnd.month() === clearStartDate.month() || >- calculatedMaxEnd.month() === clearStartDate.add(1, "month").month() >+ testEndDate.month() === clearStartAfterBookingA.month() || >+ testEndDate.month() === >+ clearStartAfterBookingA.add(1, "month").month() > ) { > // First check that the date exists and is not disabled by flatpickr > cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(calculatedMaxEnd.toDate()) >+ .getFlatpickrDate(testEndDate.toDate()) > .should("not.have.class", "flatpickr-disabled") > .and("be.visible"); > > // Hover to trigger trail period logic > cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(calculatedMaxEnd.toDate()) >+ .getFlatpickrDate(testEndDate.toDate()) > .trigger("mouseover"); > >- // Should NOT have trailDisable since trail days 38-40 are clear >+ // Should NOT have trailDisable since trail Days 46-48 are clear > cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(calculatedMaxEnd.toDate()) >+ .getFlatpickrDate(testEndDate.toDate()) > .should("not.have.class", "trailDisable"); > > cy.log( >- `â CRITICAL: ${calculatedMaxEnd.format("YYYY-MM-DD")} does NOT have trailDisable` >+ `â CRITICAL: ${testEndDate.format("YYYY-MM-DD")} does NOT have trailDisable` > ); > cy.log( > "â BUG FIX VERIFIED: Trail period does NOT incorrectly reduce max date" >@@ -1046,34 +1220,36 @@ describe("Booking Modal Date Picker Tests", () => { > * Trail Period Visual Hints Test: > * - Hovering over end date shows trail period with CSS classes > * - Classes: trailRangeStart, trailRange, trailRangeEnd >+ * - Using testEndDate (Day 45) with trail Days 46-48 > */ > >- const trailStart = calculatedMaxEnd; // Day 37 >- const trailEnd = calculatedMaxEnd.add( >+ const trailStart = testEndDate; // Day 45 >+ const trailEnd = testEndDate.add( > leadTrailCirculationRules.bookings_trail_period, > "day" >- ); // Day 40 >+ ); // Day 48 > > cy.log( >- `Hovering ${calculatedMaxEnd.format("YYYY-MM-DD")} to check trail period visual hints` >+ `Hovering ${testEndDate.format("YYYY-MM-DD")} to check trail period visual hints` > ); > cy.log( > ` Expected trail range: ${trailStart.add(1, "day").format("YYYY-MM-DD")} to ${trailEnd.format("YYYY-MM-DD")}` > ); > > if ( >- calculatedMaxEnd.month() === clearStartDate.month() || >- calculatedMaxEnd.month() === clearStartDate.add(1, "month").month() >+ testEndDate.month() === clearStartAfterBookingA.month() || >+ testEndDate.month() === >+ clearStartAfterBookingA.add(1, "month").month() > ) { > cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(calculatedMaxEnd.toDate()) >+ .getFlatpickrDate(testEndDate.toDate()) > .trigger("mouseover"); > > // Check trail range classes on subsequent days >- const firstTrailDay = calculatedMaxEnd.add(1, "day"); >+ const firstTrailDay = testEndDate.add(1, "day"); > if ( > firstTrailDay.month() === today.month() || >- firstTrailDay.month() === today.add(1, "month").month() >+ firstTrailDay.month() === today.add(2, "month").month() > ) { > cy.get("@leadTrailFlatpickr") > .getFlatpickrDate(firstTrailDay.toDate()) >@@ -1085,76 +1261,164 @@ describe("Booking Modal Date Picker Tests", () => { > } > > // ======================================================================== >- // TEST 6: Trail Period Conflict Prevention with Existing Booking >+ // TEST 6A: Trail Period Conflict with Existing Booking ACTUAL Dates > // ======================================================================== > cy.log( >- "=== TEST 6: Testing trail period prevents selection due to booking conflict ===" >+ "=== TEST 6A: Testing new booking trail period conflicts with existing booking actual dates ===" > ); > > /* >- * Trail Period Conflict Test: >- * - Booking B occupies Days 45-50 >- * - End date Day 42 would have trail period Days 43-45 >- * - Day 45 is in the trail period and conflicts with Booking B >- * - Day 42 should get 'trailDisable' class when hovered >+ * Original Trail Conflict Scenario: >+ * Booking B actual dates: Days 60-65 >+ * >+ * Test end dates where NEW booking's trail overlaps with Booking B ACTUAL dates: >+ * - Day 58: Trail Days 59-61 â Days 60-61 overlap with Booking B actual Days 60-65 â DISABLED >+ * - Day 59: Trail Days 60-62 â Days 60-62 overlap with Booking B actual Days 60-65 â DISABLED > */ > >- const conflictEndDate = today.add(42, "day"); >- const conflictTrailStart = conflictEndDate.add(1, "day"); // Day 43 >- const conflictTrailEnd = conflictEndDate.add( >- leadTrailCirculationRules.bookings_trail_period, >+ const bookingBStart = conflictBookings[1].start; // Day 60 >+ >+ cy.log( >+ `Booking B actual dates: ${bookingBStart.format("YYYY-MM-DD")} to ${conflictBookings[1].end.format("YYYY-MM-DD")}` >+ ); >+ >+ const actualTrailConflicts = [ >+ { >+ date: today.add(58, "day"), >+ trailDays: "59-61", >+ reason: "trail Days 60-61 overlap with Booking B actual dates Days 60-65", >+ }, >+ { >+ date: today.add(59, "day"), >+ trailDays: "60-62", >+ reason: "trail Days 60-62 overlap with Booking B actual dates Days 60-65", >+ }, >+ ]; >+ >+ actualTrailConflicts.forEach(test => { >+ if ( >+ test.date.month() === clearStartAfterBookingA.month() || >+ test.date.month() === >+ clearStartAfterBookingA.add(2, "month").month() >+ ) { >+ cy.log( >+ `Testing ${test.date.format("YYYY-MM-DD")}: ${test.reason}` >+ ); >+ >+ cy.get("@leadTrailFlatpickr") >+ .getFlatpickrDate(test.date.toDate()) >+ .trigger("mouseover"); >+ >+ cy.get("@leadTrailFlatpickr") >+ .getFlatpickrDate(test.date.toDate()) >+ .should("have.class", "trailDisable"); >+ >+ cy.log( >+ `â ${test.date.format("YYYY-MM-DD")}: Has trailDisable (conflicts with actual booking)` >+ ); >+ } >+ }); >+ >+ // ======================================================================== >+ // TEST 6B: BIDIRECTIONAL - Trail Period Conflict with Existing Booking LEAD >+ // ======================================================================== >+ cy.log( >+ "=== TEST 6B: Testing new booking trail period conflicts with existing booking LEAD period ===" >+ ); >+ >+ /* >+ * NEW BIDIRECTIONAL Conflict Scenario: >+ * Booking B start: Day 60 >+ * Booking B lead period: Days 58-59 (2 days before start) >+ * >+ * Test end dates where NEW booking's trail overlaps with Booking B LEAD: >+ * - Day 56: Trail Days 57-59 â Days 57-59 overlap with Booking B lead Days 58-59 â DISABLED >+ * - Day 55: Trail Days 56-58 â Day 58 overlaps with Booking B lead Days 58-59 â DISABLED >+ * >+ * This is the KEY enhancement: respecting existing booking's lead period! >+ */ >+ >+ const bookingBLeadStart = bookingBStart.subtract( >+ leadTrailCirculationRules.bookings_lead_period, > "day" >- ); // Day 45 >+ ); // Day 58 >+ const bookingBLeadEnd = bookingBStart.subtract(1, "day"); // Day 59 > >- if ( >- conflictEndDate.month() === clearStartDate.month() || >- conflictEndDate.month() === clearStartDate.add(2, "month").month() >- ) { >- cy.log( >- `Testing ${conflictEndDate.format("YYYY-MM-DD")}: trail Days ${conflictTrailStart.format("YYYY-MM-DD")}-${conflictTrailEnd.format("YYYY-MM-DD")} conflict with Booking B` >- ); >+ cy.log( >+ `Booking B lead period: ${bookingBLeadStart.format("YYYY-MM-DD")} to ${bookingBLeadEnd.format("YYYY-MM-DD")}` >+ ); > >- cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(conflictEndDate.toDate()) >- .trigger("mouseover"); >+ const leadPeriodTrailConflicts = [ >+ { >+ date: today.add(56, "day"), >+ trailDays: "57-59", >+ reason: "trail Days 57-59 overlap with Booking B lead Days 58-59", >+ }, >+ { >+ date: today.add(55, "day"), >+ trailDays: "56-58", >+ reason: "trail Day 58 overlaps with Booking B lead Days 58-59", >+ }, >+ ]; > >- cy.get("@leadTrailFlatpickr") >- .getFlatpickrDate(conflictEndDate.toDate()) >- .should("have.class", "trailDisable"); >+ leadPeriodTrailConflicts.forEach(test => { >+ if ( >+ test.date.month() === clearStartAfterBookingA.month() || >+ test.date.month() === >+ clearStartAfterBookingA.add(2, "month").month() >+ ) { >+ cy.log( >+ `Testing ${test.date.format("YYYY-MM-DD")}: ${test.reason}` >+ ); > >- cy.log( >- `â ${conflictEndDate.format("YYYY-MM-DD")}: Has trailDisable class (click prevented)` >- ); >- } >+ cy.get("@leadTrailFlatpickr") >+ .getFlatpickrDate(test.date.toDate()) >+ .trigger("mouseover"); >+ >+ cy.get("@leadTrailFlatpickr") >+ .getFlatpickrDate(test.date.toDate()) >+ .should("have.class", "trailDisable"); >+ >+ cy.log( >+ `â ${test.date.format("YYYY-MM-DD")}: Has trailDisable (BIDIRECTIONAL: conflicts with lead)` >+ ); >+ } >+ }); > > // ======================================================================== >- // TEST 7: Verify Full Max Range Can Be Selected >+ // TEST 7: Verify Valid Booking Range Can Be Selected > // ======================================================================== > cy.log( >- "=== TEST 7: Testing full max range selection works correctly ===" >+ "=== TEST 7: Testing valid booking range selection works correctly ===" > ); > > /* >- * Full Range Selection Test: >- * - Should be able to select the full range from start to max end >- * - Start: Day 10, End: Day 37 (28-day period) >- * - This verifies the bug fix: trail period doesn't prevent max date selection >+ * Valid Range Selection Test: >+ * - Should be able to select a valid range that respects all constraints >+ * - Start: Day 31 (clear of Booking A's trail ending Day 28) >+ * - End: Day 54 (clear of Booking B's lead starting Day 58) >+ * - This range: >+ * - Start lead Days 29-30: Clear of Booking A trail (ends Day 28) â >+ * - End trail Days 55-57: Clear of Booking B lead (starts Day 58) â >+ * - This verifies bidirectional lead/trail period checking works correctly > */ > >+ const validClearEnd = today.add(54, "day"); >+ > cy.get("#period").clearFlatpickr(); > > if ( >- clearStartDate.month() === today.month() || >- clearStartDate.month() === today.add(1, "month").month() >+ clearStartAfterBookingA.month() === today.month() || >+ clearStartAfterBookingA.month() === today.add(2, "month").month() > ) { > if ( >- calculatedMaxEnd.month() === clearStartDate.month() || >- calculatedMaxEnd.month() === >- clearStartDate.add(1, "month").month() >+ validClearEnd.month() === clearStartAfterBookingA.month() || >+ validClearEnd.month() === >+ clearStartAfterBookingA.add(2, "month").month() > ) { > cy.get("#period").selectFlatpickrDateRange( >- clearStartDate, >- calculatedMaxEnd >+ clearStartAfterBookingA, >+ validClearEnd > ); > > // Verify the dates were accepted >@@ -1162,10 +1426,10 @@ describe("Booking Modal Date Picker Tests", () => { > cy.get("#booking_end_date").should("not.have.value", ""); > > cy.log( >- `â Successfully selected full max range: ${clearStartDate.format("YYYY-MM-DD")} to ${calculatedMaxEnd.format("YYYY-MM-DD")}` >+ `â Successfully selected valid range: ${clearStartAfterBookingA.format("YYYY-MM-DD")} to ${validClearEnd.format("YYYY-MM-DD")}` > ); > cy.log( >- `â Confirmed: ${maxBookingPeriod}-day booking period fully available` >+ `â Confirmed: Booking respects both Booking A's trail and Booking B's lead periods` > ); > } > } >@@ -1183,6 +1447,15 @@ describe("Booking Modal Date Picker Tests", () => { > cy.log( > "â CRITICAL BUG FIX: Trail period does NOT incorrectly limit max date when no conflicts" > ); >+ cy.log( >+ "â BIDIRECTIONAL ENHANCEMENT: New booking lead respects existing booking trail periods" >+ ); >+ cy.log( >+ "â BIDIRECTIONAL ENHANCEMENT: New booking trail respects existing booking lead periods" >+ ); >+ cy.log( >+ "â Full protected period validation: Existing bookings' lead + actual + trail periods all respected" >+ ); > cy.log( > `â Validated: ${leadTrailCirculationRules.bookings_lead_period}-day lead + ${maxBookingPeriod}-day max period + ${leadTrailCirculationRules.bookings_trail_period}-day trail` > ); >-- >2.52.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 37707
:
182718
|
190859
|
190860
|
190861
|
190862
|
191000
|
191001
|
191002
|
191003
|
191232
|
191233