View | Details | Raw Unified | Return to bug 39584
Collapse All | Expand All

(-)a/t/cypress/integration/Circulation/bookingsModalDatePicker_spec.ts (-1 / +304 lines)
Lines 674-679 describe("Booking Modal Date Picker Tests", () => { Link Here
674
        );
674
        );
675
    });
675
    });
676
676
677
    it("should handle lead and trail periods", () => {
678
        /**
679
         * Lead and Trail Period Behaviour Tests
680
         * =====================================
681
         *
682
         * Test Coverage:
683
         * 1. Lead period visual hints (CSS classes) in clear zone
684
         * 2. Trail period visual hints (CSS classes) in clear zone
685
         * 3. Lead period conflicts with past dates or existing booking (leadDisable)
686
         * 4. Trail period conflicts with existing booking (trailDisable)
687
         * 5. Max date selectable when trail period is clear of existing booking
688
         *
689
         * Fixed Date Setup:
690
         * ================
691
         * - Today: June 10, 2026 (Wednesday)
692
         * - Timezone: Europe/London
693
         * - Lead Period: 2 days
694
         * - Trail Period: 3 days
695
         * - Issue Length: 3 days
696
         * - Renewal Period: 2 days
697
         * - Max Renewals: 2
698
         * - Max Booking Period: 3 + (2 × 2) = 7 days
699
         *
700
         * Blocker Booking: June 25-27, 2026
701
         *
702
         * Timeline:
703
         * =========
704
         * June 2026
705
         * Sun Mon Tue Wed Thu Fri Sat
706
         *      8   9  10  11  12  13   ← 10 = TODAY
707
         *  14  15  16  17  18  19  20
708
         *  21  22  23  24  25  26  27   ← 25-27 = BLOCKER
709
         *  28  29  30
710
         *
711
         * Test Scenarios:
712
         * ==============
713
         * Phase 1: Hover June 13 → Lead June 11-12 (clear) → no leadDisable
714
         * Phase 2: Select June 13, hover June 16 → Trail June 17-19 (clear) → no trailDisable
715
         * Phase 3a: Hover June 11 → Lead June 9-10, June 9 is past → leadDisable
716
         * Phase 3b: Hover June 29 → Lead June 27-28, June 27 is in blocker → leadDisable
717
         * Phase 4: Select June 20, hover June 23 → Trail June 24-26 overlaps blocker → trailDisable
718
         * Phase 5: Select June 13, hover June 20 (max) → Trail June 21-23 (clear) → selectable
719
         */
720
721
        // Fix the browser Date object to June 10, 2026 at 09:00 Europe/London
722
        // Using ["Date"] to avoid freezing timers which breaks Select2 async operations
723
        const fixedToday = new Date("2026-06-10T08:00:00Z"); // 09:00 BST (UTC+1)
724
        cy.clock(fixedToday, ["Date"]);
725
        cy.log(`Fixed today: June 10, 2026`);
726
727
        // Circulation rules with short periods for focused testing
728
        const circulationRules = {
729
            bookings_lead_period: 2,
730
            bookings_trail_period: 3,
731
            issuelength: 3,
732
            renewalsallowed: 2,
733
            renewalperiod: 2,
734
        };
735
736
        const maxBookingPeriod =
737
            circulationRules.issuelength +
738
            circulationRules.renewalsallowed * circulationRules.renewalperiod; // 7 days
739
        cy.log(`Max booking period: ${maxBookingPeriod} days`);
740
741
        cy.intercept("GET", "/api/v1/circulation_rules*", {
742
            body: [circulationRules],
743
        }).as("getFixedDateRules");
744
745
        // Create blocker booking: June 25-27, 2026
746
        const blockerStart = "2026-06-25 00:00:00";
747
        const blockerEnd = "2026-06-27 23:59:59";
748
749
        cy.task("query", {
750
            sql: `INSERT INTO bookings (biblio_id, item_id, patron_id, start_date, end_date, pickup_library_id, status)
751
                  VALUES (?, ?, ?, ?, ?, ?, '1')`,
752
            values: [
753
                testData.biblio.biblio_id,
754
                testData.items[0].item_id,
755
                testData.patron.patron_id,
756
                blockerStart,
757
                blockerEnd,
758
                testData.libraries[0].library_id,
759
            ],
760
        });
761
        cy.log(`Blocker booking created: June 25-27, 2026`);
762
763
        // Setup modal
764
        setupModalForDateTesting({ skipItemSelection: true });
765
766
        cy.get("#booking_item_id").should("not.be.disabled");
767
        cy.selectFromSelect2ByIndex("#booking_item_id", 1);
768
        cy.wait("@getFixedDateRules");
769
770
        cy.get("#period").should("not.be.disabled");
771
        cy.get("#period").as("fp");
772
        cy.get("@fp").openFlatpickr();
773
774
        // Helper to get a specific date element by ISO date string
775
        const getDateByISO = (isoDate: string) => {
776
            const date = new Date(isoDate);
777
            return cy.get("@fp").getFlatpickrDate(date);
778
        };
779
780
        // ========================================================================
781
        // PHASE 1: Lead Period Clear - Visual Classes
782
        // ========================================================================
783
        cy.log("=== PHASE 1: Lead period visual hints in clear zone ===");
784
785
        /**
786
         * Hover June 13 as potential start date
787
         * Lead period: June 11-12 (both after today June 10, no booking conflict)
788
         * Expected: leadRangeStart on June 11, leadRange on June 12, no leadDisable on June 13
789
         */
790
791
        getDateByISO("2026-06-13").trigger("mouseover");
792
793
        // Check lead period classes
794
        getDateByISO("2026-06-11")
795
            .should("have.class", "leadRangeStart")
796
            .and("have.class", "leadRange");
797
        cy.log("✓ June 11: Has leadRange and leadRangeStart classes");
798
799
        getDateByISO("2026-06-12").should("have.class", "leadRange");
800
        cy.log("✓ June 12: Has leadRange class");
801
802
        // Hovered date should NOT have leadDisable (lead period is clear)
803
        getDateByISO("2026-06-13")
804
            .should("have.class", "leadRangeEnd")
805
            .and("not.have.class", "leadDisable");
806
        cy.log(
807
            "✓ June 13: Has leadRangeEnd and not leadDisable (lead period is clear)"
808
        );
809
810
        // ========================================================================
811
        // PHASE 2: Trail Period Clear - Visual Classes
812
        // ========================================================================
813
        cy.log("=== PHASE 2: Trail period visual hints in clear zone ===");
814
815
        /**
816
         * Select June 13 as start date (lead June 11-12 is clear)
817
         * Then hover June 16 as potential end date
818
         * Trail period calculation: trailStart = hoverDate, trailEnd = hoverDate + 3
819
         * So: trailStart = June 16, trailEnd = June 19
820
         * Classes: June 16 = trailRangeStart, June 17-18 = trailRange, June 19 = trailRange + trailRangeEnd
821
         */
822
823
        // Select June 13 as start date (same date we just hovered - lead is clear)
824
        getDateByISO("2026-06-13").click();
825
        cy.log("Selected June 13 as start date");
826
827
        // Hover June 16 as potential end date
828
        getDateByISO("2026-06-16").trigger("mouseover");
829
830
        // Check trail period classes
831
        // trailRangeStart is on the hovered date itself (June 16)
832
        getDateByISO("2026-06-16").should("have.class", "trailRangeStart");
833
        cy.log("✓ June 16: Has trailRangeStart class (hovered date)");
834
835
        // trailRange is on days after trailStart up to and including trailEnd
836
        getDateByISO("2026-06-17").should("have.class", "trailRange");
837
        cy.log("✓ June 17: Has trailRange class");
838
839
        getDateByISO("2026-06-18").should("have.class", "trailRange");
840
        cy.log("✓ June 18: Has trailRange class");
841
842
        // trailRangeEnd is on the last day of trail period
843
        getDateByISO("2026-06-19")
844
            .should("have.class", "trailRangeEnd")
845
            .and("have.class", "trailRange");
846
        cy.log("✓ June 19: Has trailRangeEnd and trailRange classes");
847
848
        // Hovered date should NOT have trailDisable (trail period is clear)
849
        getDateByISO("2026-06-16").should("not.have.class", "trailDisable");
850
        cy.log("✓ June 16: No trailDisable (trail period is clear)");
851
852
        // Clear selection for next phase
853
        cy.get("#period").clearFlatpickr();
854
        cy.get("@fp").openFlatpickr();
855
        cy.log("Cleared selection for next phase");
856
857
        // ========================================================================
858
        // PHASE 3: Lead Period Conflict - Past Dates and Existing bookings
859
        // ========================================================================
860
        cy.log("=== PHASE 3: Lead period conflicts ===");
861
862
        /**
863
         * Hover June 11 as potential start date
864
         * Lead period: June 9-10
865
         * June 9 is in the past (before today June 10)
866
         * Expected: leadDisable on June 11 because lead period extends into past
867
         */
868
869
        getDateByISO("2026-06-11").trigger("mouseover");
870
871
        // June 11 should have leadDisable because lead period (June 9-10) includes past date
872
        getDateByISO("2026-06-11").should("have.class", "leadDisable");
873
        cy.log(
874
            "✓ June 11: Has leadDisable (lead period June 9-10 includes past date)"
875
        );
876
877
        /**
878
         * Hover June 29 as potential start date
879
         * Lead period: June 27-28
880
         * June 27 is in the existing booking (25-27 June)
881
         * Expected: leadDisable on June 29 because lead period extends into existing booking
882
         */
883
884
        getDateByISO("2026-06-29").trigger("mouseover");
885
886
        // June 29 should have leadDisable because lead period (June 27-28) includes existing booking date
887
        getDateByISO("2026-06-29").should("have.class", "leadDisable");
888
        cy.log(
889
            "✓ June 29: Has leadDisable (lead period June 27-28 includes existing booking date)"
890
        );
891
892
        // ========================================================================
893
        // PHASE 4: Trail Period Conflict - Existing Booking
894
        // ========================================================================
895
        cy.log("=== PHASE 4: Trail period conflict with existing booking ===");
896
897
        /**
898
         * Select June 20 as start date (lead June 18-19, both clear)
899
         * Then hover June 23 as potential end date
900
         * Trail period: June 24-26
901
         * Blocker booking: June 25-27 (partial overlap)
902
         * Expected: trailDisable on June 23
903
         */
904
905
        // Select June 20 as start date
906
        getDateByISO("2026-06-20").click();
907
        cy.log("Selected June 20 as start date");
908
909
        // Hover June 23 as potential end date
910
        getDateByISO("2026-06-23").trigger("mouseover");
911
912
        // June 23 should have trailDisable because trail period (June 24-26) overlaps blocker (June 25-27)
913
        getDateByISO("2026-06-23").should("have.class", "trailDisable");
914
        cy.log(
915
            "✓ June 23: Has trailDisable (trail June 24-26 overlaps blocker June 25-27)"
916
        );
917
918
        // Clear selection for next phase
919
        cy.get("#period").clearFlatpickr();
920
        cy.get("@fp").openFlatpickr();
921
        cy.log("Cleared selection for next phase");
922
923
        // ========================================================================
924
        // PHASE 5: Max Date Selectable When Trail is Clear
925
        // ========================================================================
926
        cy.log("=== PHASE 5: Max date selectable when trail is clear ===");
927
928
        /**
929
         * Select June 13 as start date (lead June 11-12, both clear)
930
         * Max end date: June 20 (13 + 7 days)
931
         * Hover June 20: Trail period June 21-23
932
         * Trail period is clear (blocker is June 25-27)
933
         * Expected: June 20 is selectable (no trailDisable), can book full 7-day period
934
         */
935
936
        // Select June 13 as start date
937
        getDateByISO("2026-06-13").click();
938
        cy.log("Selected June 13 as start date");
939
940
        // Hover June 20 (max date = start + 7 days)
941
        getDateByISO("2026-06-20").trigger("mouseover");
942
943
        // Max date should NOT have trailDisable (trail June 21-23 is clear)
944
        getDateByISO("2026-06-20").should("not.have.class", "trailDisable");
945
        cy.log("✓ June 20: No trailDisable (trail June 21-23 is clear)");
946
947
        // Max date should not be disabled by flatpickr
948
        getDateByISO("2026-06-20").should(
949
            "not.have.class",
950
            "flatpickr-disabled"
951
        );
952
        cy.log("✓ June 20: Not flatpickr-disabled (max date is selectable)");
953
954
        // Actually select the max date to confirm booking can be made
955
        getDateByISO("2026-06-20").click();
956
957
        // Verify dates were accepted in the form
958
        cy.get("#booking_start_date").should("not.have.value", "");
959
        cy.get("#booking_end_date").should("not.have.value", "");
960
        cy.log("✓ Full 7-day period selected: June 13 to June 20");
961
962
        // ========================================================================
963
        // SUMMARY
964
        // ========================================================================
965
        cy.log(
966
            "✓ CONFIRMED: Lead and trail period behaviour working correctly"
967
        );
968
        cy.log("✓ Phase 1: Lead period visual hints appear in clear zones");
969
        cy.log("✓ Phase 2: Trail period visual hints appear in clear zones");
970
        cy.log(
971
            "✓ Phase 3: Lead period into past dates or existing bookings triggers leadDisable"
972
        );
973
        cy.log(
974
            "✓ Phase 4: Trail period overlapping booking triggers trailDisable"
975
        );
976
        cy.log(
977
            "✓ Phase 5: Max date is selectable when trail period has no conflicts"
978
        );
979
    });
980
677
    it("should show event dots for dates with existing bookings", () => {
981
    it("should show event dots for dates with existing bookings", () => {
678
        /**
982
        /**
679
         * Comprehensive Event Dots Visual Indicator Test
983
         * Comprehensive Event Dots Visual Indicator Test
680
- 

Return to bug 39584