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

(-)a/t/cypress/support/e2e.js (+1 lines)
Lines 26-31 Link Here
26
26
27
// Import Select2 helpers
27
// Import Select2 helpers
28
import "./select2";
28
import "./select2";
29
import "./flatpickr.js";
29
30
30
function get_fallback_login_value(param) {
31
function get_fallback_login_value(param) {
31
    var env_var = param == "username" ? "KOHA_USER" : "KOHA_PASS";
32
    var env_var = param == "username" ? "KOHA_USER" : "KOHA_PASS";
(-)a/t/cypress/support/flatpickr.js (-1 / +1049 lines)
Line 0 Link Here
0
- 
1
// flatpickrHelpers.js - Reusable Cypress functions for Flatpickr date pickers
2
3
/**
4
 * Helper functions for interacting with Flatpickr date picker components in Cypress tests
5
 * Uses click-driven interactions instead of Flatpickr's JavaScript API
6
 * Supports all standard Flatpickr operations including date selection, range selection,
7
 * navigation, and direct input with full chainability
8
 *
9
 * CHAINABILITY:
10
 * All Flatpickr helper commands are fully chainable. You can:
11
 * - Chain multiple Flatpickr operations (open, navigate, select)
12
 * - Chain Flatpickr commands with standard Cypress commands
13
 * - Split complex interactions into multiple steps for better reliability
14
 *
15
 * Examples:
16
 *   cy.getFlatpickr('#myDatepicker')
17
 *     .flatpickr({ open: true })
18
 *     .flatpickr({ selectDate: '2023-05-15' });
19
 *
20
 *   cy.getFlatpickr('#rangePicker')
21
 *     .flatpickr({ open: true })
22
 *     .flatpickr({ selectRange: ['2023-06-01', '2023-06-15'] })
23
 *     .should('have.value', '2023-06-01 to 2023-06-15');
24
 */
25
26
/**
27
 * Main Flatpickr interaction command to perform operations on Flatpickr date pickers
28
 * @param {string|JQuery} [subject] - Optional jQuery element (when used with .flatpickr())
29
 * @param {Object} options - Configuration options for the Flatpickr operation
30
 * @param {boolean} [options.open=false] - Whether to open the Flatpickr calendar
31
 * @param {boolean} [options.close=false] - Whether to close the Flatpickr calendar
32
 * @param {boolean} [options.clear=false] - Whether to clear the current selection
33
 * @param {string|Date} [options.selectDate] - Date to select (YYYY-MM-DD string or Date object)
34
 * @param {Array} [options.selectRange] - Array with start and end dates for range selection
35
 * @param {string} [options.typeDate] - Date string to type directly into the input
36
 * @param {string|Date} [options.navigateToMonth] - Navigate to specified month/year
37
 * @param {number} [options.selectDay] - Day of month to select from current month view
38
 * @param {boolean} [options.nextMonth=false] - Whether to navigate to next month
39
 * @param {boolean} [options.prevMonth=false] - Whether to navigate to previous month
40
 * @param {number} [options.setYear] - Year to set in the calendar
41
 * @param {boolean} [options.selectToday=false] - Whether to select today's date
42
 * @param {string} [options.selector] - CSS selector to find the element (when not using subject)
43
 * @param {number} [options.timeout=10000] - Timeout for operations in milliseconds
44
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
45
 *
46
 * @example
47
 * // Basic usage
48
 * cy.get('#myDatepicker').flatpickr({ selectDate: '2023-05-15' });
49
 *
50
 * @example
51
 * // Chained operations (more reliable for complex interactions)
52
 * cy.getFlatpickr('#myDatepicker')
53
 *   .flatpickr({ open: true })
54
 *   .flatpickr({ navigateToMonth: '2023-06-01' })
55
 *   .flatpickr({ selectDay: 15 });
56
 *
57
 * @example
58
 * // Date range selection
59
 * cy.getFlatpickr('#rangePicker').flatpickr({
60
 *   selectRange: ['2023-06-01', '2023-06-15']
61
 * });
62
 */
63
Cypress.Commands.add(
64
    "flatpickr",
65
    {
66
        prevSubject: "optional",
67
    },
68
    (subject, options) => {
69
        // Default configuration
70
        const defaults = {
71
            open: false, // Whether to open the Flatpickr calendar
72
            close: false, // Whether to close the Flatpickr calendar
73
            clear: false, // Whether to clear the current selection
74
            selectDate: null, // Date to select (YYYY-MM-DD string or Date object)
75
            selectRange: null, // Array with start and end dates for range selection
76
            typeDate: null, // Date string to type directly into the input
77
            navigateToMonth: null, // Navigate to specified month/year
78
            selectDay: null, // Day of month to select from current month view
79
            nextMonth: false, // Whether to navigate to next month
80
            prevMonth: false, // Whether to navigate to previous month
81
            setYear: null, // Year to set in the calendar
82
            selectToday: false, // Whether to select today's date
83
            selector: null, // CSS selector to find the element (when not using subject)
84
            timeout: 10000, // Timeout for operations in milliseconds
85
        };
86
87
        // Merge passed options with defaults
88
        const config = { ...defaults, ...options };
89
90
        // Handle selecting the target Flatpickr element
91
        let $originalInput;
92
        if (subject) {
93
            $originalInput = subject;
94
            // Store the element ID as a data attribute for chaining
95
            cy.wrap(subject).then($el => {
96
                const inputId = $el.attr("id");
97
                if (inputId) {
98
                    Cypress.$($el).attr("data-flatpickr-helper-id", inputId);
99
                }
100
            });
101
        } else if (config.selector) {
102
            $originalInput = cy.get(config.selector);
103
        } else {
104
            throw new Error(
105
                "Either provide a subject or a selector to identify the Flatpickr element"
106
            );
107
        }
108
109
        return cy.wrap($originalInput).then($el => {
110
            // Try to get the ID either from the element or from the stored data attribute
111
            const inputId = $el.attr("id") || $el.data("flatpickr-helper-id");
112
            const originalInputSelector = inputId ? `#${inputId}` : null;
113
114
            // Get the visible flatpickr input element
115
            const getVisibleInput = () => {
116
                if (originalInputSelector) {
117
                    return cy
118
                        .get(originalInputSelector)
119
                        .parents()
120
                        .find(".flatpickr_wrapper input.flatpickr-input");
121
                } else {
122
                    return cy
123
                        .wrap($el)
124
                        .parents()
125
                        .find(".flatpickr_wrapper input.flatpickr-input");
126
                }
127
            };
128
129
            // Open the Flatpickr calendar if requested
130
            if (config.open) {
131
                cy.get(".flatpickr-calendar").then($calendar => {
132
                    const isVisible =
133
                        $calendar.hasClass("open") && $calendar.is(":visible");
134
135
                    if (!isVisible) {
136
                        getVisibleInput().click();
137
                    }
138
139
                    // Always wait for the calendar to be visible — retry if necessary
140
                    cy.get(".flatpickr-calendar.open", {
141
                        timeout: config.timeout,
142
                    }).should("be.visible");
143
                });
144
            }
145
146
            // Close the Flatpickr calendar if requested
147
            if (config.close) {
148
                cy.get("body").click(0, 0);
149
                cy.get(".flatpickr-calendar.open").should("not.exist", {
150
                    timeout: config.timeout,
151
                });
152
            }
153
154
            // Clear the Flatpickr input if requested
155
            if (config.clear) {
156
                cy.wrap($el).parents().find(".clear_date").click();
157
                cy.wrap($el).should("have.value", "");
158
                getVisibleInput().should("have.value", "");
159
            }
160
161
            // Navigate to a specific month/year if requested
162
            if (config.navigateToMonth !== null) {
163
                // Ensure calendar is open
164
                if (!config.open) {
165
                    cy.get(".flatpickr-calendar").then($calendar => {
166
                        const isVisible =
167
                            $calendar.hasClass("open") &&
168
                            $calendar.is(":visible");
169
170
                        if (!isVisible) {
171
                            getVisibleInput().click();
172
                        }
173
174
                        // Always wait for the calendar to be visible — retry if necessary
175
                        cy.get(".flatpickr-calendar.open", {
176
                            timeout: config.timeout,
177
                        }).should("be.visible");
178
                    });
179
                }
180
181
                // Convert to Date object if string was provided
182
                const dateObj =
183
                    typeof config.navigateToMonth === "string"
184
                        ? new Date(config.navigateToMonth)
185
                        : config.navigateToMonth;
186
187
                const targetYear = dateObj.getFullYear();
188
                const targetMonth = dateObj.getMonth(); // 0-based index
189
190
                // Click-based navigation to the correct month and year
191
                cy.get(".flatpickr-current-month").then($currentMonth => {
192
                    cy.log("Inside currentMonth");
193
                    cy.get(".flatpickr-current-month .numInput.cur-year").then(
194
                        $currentYear => {
195
                            // Parse the current month and year from the calendar display
196
                            const currentMonthName = $currentMonth
197
                                .text()
198
                                .trim();
199
                            const monthNames = [
200
                                "January",
201
                                "February",
202
                                "March",
203
                                "April",
204
                                "May",
205
                                "June",
206
                                "July",
207
                                "August",
208
                                "September",
209
                                "October",
210
                                "November",
211
                                "December",
212
                            ];
213
                            const currentMonth = monthNames.findIndex(name =>
214
                                currentMonthName.includes(name)
215
                            );
216
                            const currentYear = parseInt($currentYear.val());
217
218
                            // Calculate the number of months to move forward or backward
219
                            const monthDiff =
220
                                (targetYear - currentYear) * 12 +
221
                                (targetMonth - currentMonth);
222
223
                            if (monthDiff > 0) {
224
                                // Click next month button the appropriate number of times
225
                                for (let i = 0; i < monthDiff; i++) {
226
                                    cy.get(".flatpickr-next-month").click();
227
                                    cy.wait(100); // Give time for the calendar to update
228
                                }
229
                            } else if (monthDiff < 0) {
230
                                // Click previous month button the appropriate number of times
231
                                for (let i = 0; i < Math.abs(monthDiff); i++) {
232
                                    cy.get(".flatpickr-prev-month").click();
233
                                    cy.wait(100); // Give time for the calendar to update
234
                                }
235
                            }
236
                        }
237
                    );
238
                });
239
            }
240
241
            // Move to next month if requested
242
            if (config.nextMonth) {
243
                // Ensure calendar is open
244
                if (!config.open && !config.navigateToMonth) {
245
                    cy.get(".flatpickr-calendar").then($calendar => {
246
                        const isVisible =
247
                            $calendar.hasClass("open") &&
248
                            $calendar.is(":visible");
249
250
                        if (!isVisible) {
251
                            getVisibleInput().click();
252
                        }
253
254
                        // Always wait for the calendar to be visible — retry if necessary
255
                        cy.get(".flatpickr-calendar.open", {
256
                            timeout: config.timeout,
257
                        }).should("be.visible");
258
                    });
259
                }
260
261
                cy.get(".flatpickr-next-month").click();
262
                cy.wait(100); // Give time for the calendar to update
263
            }
264
265
            // Move to previous month if requested
266
            if (config.prevMonth) {
267
                // Ensure calendar is open
268
                if (
269
                    !config.open &&
270
                    !config.navigateToMonth &&
271
                    !config.nextMonth
272
                ) {
273
                    cy.get(".flatpickr-calendar").then($calendar => {
274
                        const isVisible =
275
                            $calendar.hasClass("open") &&
276
                            $calendar.is(":visible");
277
278
                        if (!isVisible) {
279
                            getVisibleInput().click();
280
                        }
281
282
                        // Always wait for the calendar to be visible — retry if necessary
283
                        cy.get(".flatpickr-calendar.open", {
284
                            timeout: config.timeout,
285
                        }).should("be.visible");
286
                    });
287
                }
288
289
                cy.get(".flatpickr-prev-month").click();
290
                cy.wait(100); // Give time for the calendar to update
291
            }
292
293
            // Set year if requested - using click interactions on year input
294
            if (config.setYear !== null) {
295
                // Ensure calendar is open
296
                if (
297
                    !config.open &&
298
                    !config.navigateToMonth &&
299
                    !config.nextMonth &&
300
                    !config.prevMonth
301
                ) {
302
                    cy.get(".flatpickr-calendar").then($calendar => {
303
                        const isVisible =
304
                            $calendar.hasClass("open") &&
305
                            $calendar.is(":visible");
306
307
                        if (!isVisible) {
308
                            getVisibleInput().click();
309
                        }
310
311
                        // Always wait for the calendar to be visible — retry if necessary
312
                        cy.get(".flatpickr-calendar.open", {
313
                            timeout: config.timeout,
314
                        }).should("be.visible");
315
                    });
316
                }
317
318
                const yearNum = parseInt(config.setYear, 10);
319
320
                // Click on the year input to focus it
321
                cy.get(".flatpickr-current-month .numInput.cur-year").click();
322
323
                // Clear the current year and type the new year
324
                cy.get(".flatpickr-current-month .numInput.cur-year")
325
                    .clear()
326
                    .type(yearNum.toString(), { force: true })
327
                    .type("{enter}");
328
329
                cy.wait(100); // Give time for the calendar to update
330
            }
331
332
            // Select a specific day from the current month view if requested
333
            if (config.selectDay !== null) {
334
                // Ensure calendar is open
335
                if (
336
                    !config.open &&
337
                    !config.navigateToMonth &&
338
                    !config.nextMonth &&
339
                    !config.prevMonth &&
340
                    !config.setYear
341
                ) {
342
                    cy.get(".flatpickr-calendar").then($calendar => {
343
                        const isVisible =
344
                            $calendar.hasClass("open") &&
345
                            $calendar.is(":visible");
346
347
                        if (!isVisible) {
348
                            getVisibleInput().click();
349
                        }
350
351
                        // Always wait for the calendar to be visible — retry if necessary
352
                        cy.get(".flatpickr-calendar.open", {
353
                            timeout: config.timeout,
354
                        }).should("be.visible");
355
                    });
356
                }
357
358
                const dayNum = parseInt(config.selectDay, 10);
359
360
                // Get current month and year from the calendar display
361
                cy.get(".flatpickr-current-month").then($currentMonth => {
362
                    cy.get(".flatpickr-current-month .numInput.cur-year").then(
363
                        $currentYear => {
364
                            const currentMonthName = $currentMonth
365
                                .text()
366
                                .trim();
367
                            const monthNames = [
368
                                "January",
369
                                "February",
370
                                "March",
371
                                "April",
372
                                "May",
373
                                "June",
374
                                "July",
375
                                "August",
376
                                "September",
377
                                "October",
378
                                "November",
379
                                "December",
380
                            ];
381
                            const currentMonth = monthNames.findIndex(name =>
382
                                currentMonthName.includes(name)
383
                            );
384
                            const currentYear = parseInt($currentYear.val());
385
386
                            // Format the aria-label for the target day
387
                            const formattedDayLabel = `${monthNames[currentMonth]} ${dayNum}, ${currentYear}`;
388
389
                            // Select the day using aria-label
390
                            cy.get(
391
                                `.flatpickr-day[aria-label="${formattedDayLabel}"]`
392
                            ).click();
393
                        }
394
                    );
395
                });
396
397
                // Verify the selection was made
398
                cy.wrap($el).should("not.have.value", "");
399
            }
400
401
            // Select today's date if requested
402
            if (config.selectToday) {
403
                // Ensure calendar is open
404
                if (
405
                    !config.open &&
406
                    !config.navigateToMonth &&
407
                    !config.nextMonth &&
408
                    !config.prevMonth &&
409
                    !config.setYear &&
410
                    !config.selectDay
411
                ) {
412
                    cy.get(".flatpickr-calendar").then($calendar => {
413
                        const isVisible =
414
                            $calendar.hasClass("open") &&
415
                            $calendar.is(":visible");
416
417
                        if (!isVisible) {
418
                            getVisibleInput().click();
419
                        }
420
421
                        // Always wait for the calendar to be visible — retry if necessary
422
                        cy.get(".flatpickr-calendar.open", {
423
                            timeout: config.timeout,
424
                        }).should("be.visible");
425
                    });
426
                }
427
428
                cy.get(".flatpickr-day.today").click();
429
430
                // Verify the selection was made
431
                cy.wrap($el).should("not.have.value", "");
432
            }
433
434
            // Select a specific date if requested
435
            if (config.selectDate !== null) {
436
                // Ensure calendar is open
437
                if (
438
                    !config.open &&
439
                    !config.navigateToMonth &&
440
                    !config.nextMonth &&
441
                    !config.prevMonth &&
442
                    !config.setYear &&
443
                    !config.selectDay &&
444
                    !config.selectToday
445
                ) {
446
                    cy.get(".flatpickr-calendar").then($calendar => {
447
                        const isVisible =
448
                            $calendar.hasClass("open") &&
449
                            $calendar.is(":visible");
450
451
                        if (!isVisible) {
452
                            getVisibleInput().click();
453
                        }
454
455
                        // Always wait for the calendar to be visible — retry if necessary
456
                        cy.get(".flatpickr-calendar.open", {
457
                            timeout: config.timeout,
458
                        }).should("be.visible");
459
                    });
460
                }
461
462
                // Convert to Date object if string was provided
463
                const dateObj =
464
                    typeof config.selectDate === "string"
465
                        ? new Date(config.selectDate)
466
                        : config.selectDate;
467
468
                const targetYear = dateObj.getFullYear();
469
                const targetMonth = dateObj.getMonth(); // 0-based index
470
                const targetDay = dateObj.getDate();
471
472
                // Click-based navigation to the correct month and year
473
                cy.get(".flatpickr-current-month").then($currentMonth => {
474
                    cy.get(".flatpickr-current-month .numInput.cur-year").then(
475
                        $currentYear => {
476
                            // Parse the current month and year from the calendar display
477
                            const currentMonthName = $currentMonth
478
                                .text()
479
                                .trim();
480
                            const monthNames = [
481
                                "January",
482
                                "February",
483
                                "March",
484
                                "April",
485
                                "May",
486
                                "June",
487
                                "July",
488
                                "August",
489
                                "September",
490
                                "October",
491
                                "November",
492
                                "December",
493
                            ];
494
                            const currentMonth = monthNames.findIndex(name =>
495
                                currentMonthName.includes(name)
496
                            );
497
                            const currentYear = parseInt($currentYear.val());
498
499
                            // Calculate the number of months to move forward or backward
500
                            const monthDiff =
501
                                (targetYear - currentYear) * 12 +
502
                                (targetMonth - currentMonth);
503
504
                            if (monthDiff > 0) {
505
                                // Click next month button the appropriate number of times
506
                                for (let i = 0; i < monthDiff; i++) {
507
                                    cy.get(".flatpickr-next-month").click();
508
                                    cy.wait(100); // Give time for the calendar to update
509
                                }
510
                            } else if (monthDiff < 0) {
511
                                // Click previous month button the appropriate number of times
512
                                for (let i = 0; i < Math.abs(monthDiff); i++) {
513
                                    cy.get(".flatpickr-prev-month").click();
514
                                    cy.wait(100); // Give time for the calendar to update
515
                                }
516
                            }
517
518
                            // Format the aria-label for the target date
519
                            const formattedDateLabel = `${monthNames[targetMonth]} ${targetDay}, ${targetYear}`;
520
521
                            // Select the day using aria-label
522
                            cy.get(
523
                                `.flatpickr-day[aria-label="${formattedDateLabel}"]`
524
                            ).click();
525
                        }
526
                    );
527
                });
528
529
                // Verify the selection was made
530
                cy.wait(200);
531
                const formattedDate = `${targetYear}-${(targetMonth + 1).toString().padStart(2, "0")}-${targetDay.toString().padStart(2, "0")}`;
532
                cy.wrap($el).should("have.value", formattedDate);
533
            }
534
535
            // Select a date range if requested
536
            if (config.selectRange !== null) {
537
                // Ensure calendar is open
538
                if (
539
                    !config.open &&
540
                    !config.navigateToMonth &&
541
                    !config.nextMonth &&
542
                    !config.prevMonth &&
543
                    !config.setYear &&
544
                    !config.selectDay &&
545
                    !config.selectToday &&
546
                    !config.selectDate
547
                ) {
548
                    cy.get(".flatpickr-calendar").then($calendar => {
549
                        const isVisible =
550
                            $calendar.hasClass("open") &&
551
                            $calendar.is(":visible");
552
553
                        if (!isVisible) {
554
                            getVisibleInput().click();
555
                        }
556
557
                        // Always wait for the calendar to be visible — retry if necessary
558
                        cy.get(".flatpickr-calendar.open", {
559
                            timeout: config.timeout,
560
                        }).should("be.visible");
561
                    });
562
                }
563
564
                if (
565
                    !Array.isArray(config.selectRange) ||
566
                    config.selectRange.length !== 2
567
                ) {
568
                    throw new Error(
569
                        "selectRange must be an array with exactly two dates"
570
                    );
571
                }
572
573
                // Convert to Date objects if strings were provided
574
                const startDateObj =
575
                    typeof config.selectRange[0] === "string"
576
                        ? new Date(config.selectRange[0])
577
                        : config.selectRange[0];
578
579
                const endDateObj =
580
                    typeof config.selectRange[1] === "string"
581
                        ? new Date(config.selectRange[1])
582
                        : config.selectRange[1];
583
584
                // Navigate to start date using click-based interactions
585
                const startYear = startDateObj.getFullYear();
586
                const startMonth = startDateObj.getMonth();
587
                const startDay = startDateObj.getDate();
588
589
                // Check if the flatpickr instance is in range mode
590
                // Get the flatpickr instance from the DOM element
591
                cy.window().then(win => {
592
                    // First ensure we have a valid element before proceeding
593
                    if (!$el || !$el.length) {
594
                        throw new Error("Cannot find flatpickr element");
595
                    }
596
597
                    // Try to get the flatpickr instance from the element
598
                    const fpInstance = $el[0]._flatpickr;
599
600
                    if (!fpInstance) {
601
                        throw new Error(
602
                            "Cannot find flatpickr instance on this element. Make sure it's initialized with flatpickr."
603
                        );
604
                    }
605
606
                    // Check if it's in range mode
607
                    if (
608
                        fpInstance.config &&
609
                        fpInstance.config.mode !== "range"
610
                    ) {
611
                        throw new Error(
612
                            "This flatpickr instance is not in range mode. Current mode: " +
613
                            fpInstance.config.mode
614
                        );
615
                    }
616
617
                    cy.log("Confirmed flatpickr is in range mode");
618
                });
619
620
                cy.get(
621
                    ".flatpickr-current-month .flatpickr-monthDropdown-months"
622
                ).then($dropdown => {
623
                    cy.get(".flatpickr-current-month .numInput.cur-year").then(
624
                        $currentYear => {
625
                            // Get the selected month value from the dropdown
626
                            const selectedOption = $dropdown
627
                                .find("option:selected")
628
                                .first();
629
                            const currentMonthName = selectedOption
630
                                .text()
631
                                .trim();
632
633
                            const monthNames = [
634
                                "January",
635
                                "February",
636
                                "March",
637
                                "April",
638
                                "May",
639
                                "June",
640
                                "July",
641
                                "August",
642
                                "September",
643
                                "October",
644
                                "November",
645
                                "December",
646
                            ];
647
                            const currentMonth = monthNames.findIndex(name =>
648
                                currentMonthName.includes(name)
649
                            );
650
                            const currentYear = parseInt($currentYear.val());
651
652
                            // Calculate the number of months to move forward or backward
653
                            const monthDiff =
654
                                (startYear - currentYear) * 12 +
655
                                (startMonth - currentMonth);
656
657
                            if (monthDiff > 0) {
658
                                // Click next month button the appropriate number of times
659
                                for (let i = 0; i < monthDiff; i++) {
660
                                    cy.get(".flatpickr-next-month").click();
661
                                    cy.wait(100); // Give time for the calendar to update
662
                                }
663
                            } else if (monthDiff < 0) {
664
                                // Click previous month button the appropriate number of times
665
                                for (let i = 0; i < Math.abs(monthDiff); i++) {
666
                                    cy.get(".flatpickr-prev-month").click();
667
                                    cy.wait(100); // Give time for the calendar to update
668
                                }
669
                            }
670
671
                            // Format the aria-label for the start date
672
                            const formattedStartDateLabel = `${monthNames[startMonth]} ${startDay}, ${startYear}`;
673
674
                            // Select start date using aria-label - don't wait here
675
                            cy.get(
676
                                `.flatpickr-day[aria-label="${formattedStartDateLabel}"]`
677
                            ).click();
678
                            cy.wait(300);
679
680
                            // Verify calendar stays open for range selection
681
                            cy.get(".flatpickr-calendar.open").should(
682
                                "be.visible",
683
                                {
684
                                    timeout: config.timeout,
685
                                }
686
                            );
687
                        }
688
                    );
689
                });
690
691
                // Navigate to end date if in different month
692
                const endYear = endDateObj.getFullYear();
693
                const endMonth = endDateObj.getMonth();
694
                const endDay = endDateObj.getDate();
695
696
                cy.get(".flatpickr-calendar.open").should("be.visible");
697
698
                if (startMonth !== endMonth || startYear !== endYear) {
699
                    cy.get(
700
                        ".flatpickr-current-month .flatpickr-monthDropdown-months"
701
                    ).then($dropdown => {
702
                        cy.get(
703
                            ".flatpickr-current-month .numInput.cur-year"
704
                        ).then($currentYear => {
705
                            // Get the selected month value from the dropdown
706
                            const selectedOption = $dropdown
707
                                .find("option:selected")
708
                                .first();
709
                            const currentMonthName = selectedOption
710
                                .text()
711
                                .trim();
712
713
                            const monthNames = [
714
                                "January",
715
                                "February",
716
                                "March",
717
                                "April",
718
                                "May",
719
                                "June",
720
                                "July",
721
                                "August",
722
                                "September",
723
                                "October",
724
                                "November",
725
                                "December",
726
                            ];
727
                            const currentMonth = monthNames.findIndex(name =>
728
                                currentMonthName.includes(name)
729
                            );
730
                            const currentYear = parseInt($currentYear.val());
731
732
                            // Calculate months to navigate
733
                            const monthDiff =
734
                                (endYear - currentYear) * 12 +
735
                                (endMonth - currentMonth);
736
737
                            if (monthDiff > 0) {
738
                                // Click next month button
739
                                for (let i = 0; i < monthDiff; i++) {
740
                                    cy.get(".flatpickr-next-month").click();
741
                                    cy.wait(100); // Give time for the calendar to update
742
                                }
743
                            } else if (monthDiff < 0) {
744
                                // Click previous month button
745
                                for (let i = 0; i < Math.abs(monthDiff); i++) {
746
                                    cy.get(".flatpickr-prev-month").click();
747
                                    cy.wait(100); // Give time for the calendar to update
748
                                }
749
                            }
750
                        });
751
                    });
752
                }
753
754
                // Select end date using aria-label
755
                const monthNames = [
756
                    "January",
757
                    "February",
758
                    "March",
759
                    "April",
760
                    "May",
761
                    "June",
762
                    "July",
763
                    "August",
764
                    "September",
765
                    "October",
766
                    "November",
767
                    "December",
768
                ];
769
                const formattedEndDateLabel = `${monthNames[endMonth]} ${endDay}, ${endYear}`;
770
771
                // Ensure the calendar is still open before selecting the end date
772
                cy.get(".flatpickr-calendar.open").should("be.visible");
773
774
                cy.get(
775
                    `.flatpickr-day[aria-label="${formattedEndDateLabel}"]`
776
                ).click();
777
778
                // Add a small delay to ensure proper state before proceeding
779
                cy.wait(300);
780
781
                // Verify the selection was made
782
                cy.wrap($el).should("not.have.value", "");
783
            }
784
785
            // Type a date directly into the input if requested
786
            if (config.typeDate !== null) {
787
                getVisibleInput().clear().type(config.typeDate);
788
789
                // Click away to apply the date
790
                cy.get("body").click(0, 0);
791
792
                // Verify the input has been updated
793
                cy.wrap($el).should("not.have.value", "");
794
            }
795
        });
796
    }
797
);
798
799
/**
800
 * Helper to get a Flatpickr input by any jQuery-like selector
801
 * This is the recommended starting point for chainable Flatpickr operations
802
 *
803
 * @param {string} selector - jQuery-like selector for the original input element
804
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
805
 *
806
 * @example
807
 * // Chain multiple Flatpickr operations
808
 * cy.getFlatpickr('#dateInput')
809
 *   .flatpickr({ open: true })
810
 *   .flatpickr({ navigateToMonth: '2023-06-01' })
811
 *   .flatpickr({ selectDay: 15 });
812
 *
813
 * @example
814
 * // Chain with standard Cypress assertions
815
 * cy.getFlatpickr('#startDate')
816
 *   .flatpickr({ selectDate: '2023-05-15' })
817
 *   .should('have.value', '2023-05-15');
818
 */
819
Cypress.Commands.add("getFlatpickr", selector => {
820
    return cy.get(selector);
821
});
822
823
/**
824
 * Helper to clear a Flatpickr selection
825
 * Can be used as a standalone command or as part of a chain
826
 *
827
 * @param {string} selector - jQuery-like selector for the original input element
828
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
829
 *
830
 * @example
831
 * // Standalone usage
832
 * cy.clearFlatpickr('#dateInput');
833
 *
834
 * @example
835
 * // As part of a chain
836
 * cy.getFlatpickr('#dateInput')
837
 *   .flatpickr({ selectDate: '2023-05-15' })
838
 *   .clearFlatpickr('#dateInput')
839
 *   .flatpickr({ selectDate: '2023-06-01' });
840
 */
841
Cypress.Commands.add("clearFlatpickr", selector => {
842
    return cy.getFlatpickr(selector).flatpickr({ clear: true });
843
});
844
845
/**
846
 * Helper to open a Flatpickr calendar
847
 * Can be used as a standalone command or as part of a chain
848
 *
849
 * @param {string} selector - jQuery-like selector for the original input element
850
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
851
 *
852
 * @example
853
 * // Standalone usage
854
 * cy.openFlatpickr('#dateInput');
855
 *
856
 * @example
857
 * // As part of a chain
858
 * cy.getFlatpickr('#dateInput')
859
 *   .openFlatpickr('#dateInput')
860
 *   .flatpickr({ selectDay: 15 });
861
 */
862
Cypress.Commands.add("openFlatpickr", selector => {
863
    return cy.getFlatpickr(selector).flatpickr({ open: true });
864
});
865
866
/**
867
 * Helper to close an open Flatpickr calendar
868
 * Can be used as a standalone command or as part of a chain
869
 *
870
 * @param {string} [selector] - Optional jQuery-like selector for the original input element
871
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
872
 *
873
 * @example
874
 * // Standalone usage
875
 * cy.closeFlatpickr();
876
 *
877
 * @example
878
 * // As part of a chain
879
 * cy.getFlatpickr('#dateInput')
880
 *   .flatpickr({ open: true })
881
 *   .flatpickr({ close: true });
882
 */
883
Cypress.Commands.add("closeFlatpickr", selector => {
884
    if (selector) {
885
        return cy.getFlatpickr(selector).flatpickr({ close: true });
886
    } else {
887
        return cy.flatpickr({ close: true });
888
    }
889
});
890
891
/**
892
 * Helper to select a date in a Flatpickr
893
 * Can be used as a standalone command or as part of a chain
894
 *
895
 * @param {string} selector - jQuery-like selector for the original input element
896
 * @param {Date|string} date - The date to select (Date object or YYYY-MM-DD string)
897
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
898
 *
899
 * @example
900
 * // Standalone usage
901
 * cy.selectFlatpickrDate('#dateInput', '2023-05-15');
902
 *
903
 * @example
904
 * // As part of a chain
905
 * cy.getFlatpickr('#dateInput')
906
 *   .selectFlatpickrDate('#dateInput', '2023-05-15')
907
 *   .should('have.value', '2023-05-15');
908
 */
909
Cypress.Commands.add("selectFlatpickrDate", (selector, date) => {
910
    return cy.getFlatpickr(selector).flatpickr({ selectDate: date });
911
});
912
913
/**
914
 * Helper to type a date directly into a Flatpickr input
915
 * Can be used as a standalone command or as part of a chain
916
 *
917
 * @param {string} selector - jQuery-like selector for the original input element
918
 * @param {string} dateString - The date string to type in the format expected by Flatpickr
919
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
920
 *
921
 * @example
922
 * // Standalone usage
923
 * cy.typeFlatpickrDate('#dateInput', '2023-05-15');
924
 *
925
 * @example
926
 * // As part of a chain
927
 * cy.getFlatpickr('#dateInput')
928
 *   .typeFlatpickrDate('#dateInput', '2023-05-15')
929
 *   .should('have.value', '2023-05-15');
930
 */
931
Cypress.Commands.add("typeFlatpickrDate", (selector, dateString) => {
932
    return cy.getFlatpickr(selector).flatpickr({ typeDate: dateString });
933
});
934
935
/**
936
 * Helper to select a date range in a Flatpickr range picker
937
 * Can be used as a standalone command or as part of a chain
938
 *
939
 * @param {string} selector - jQuery-like selector for the original input element
940
 * @param {Date|string} startDate - The start date to select
941
 * @param {Date|string} endDate - The end date to select
942
 * @returns {Cypress.Chainable} - Returns a chainable Cypress object for further commands
943
 *
944
 * @example
945
 * // Standalone usage
946
 * cy.selectFlatpickrDateRange('#rangePicker', '2023-06-01', '2023-06-15');
947
 *
948
 * @example
949
 * // As part of a chain
950
 * cy.getFlatpickr('#rangePicker')
951
 *   .selectFlatpickrDateRange('#rangePicker', '2023-06-01', '2023-06-15')
952
 *   .should('have.value', '2023-06-01 to 2023-06-15');
953
 */
954
Cypress.Commands.add(
955
    "selectFlatpickrDateRange",
956
    (selector, startDate, endDate) => {
957
        return cy.getFlatpickr(selector).flatpickr({
958
            selectRange: [startDate, endDate],
959
        });
960
    }
961
);
962
963
// Helper function to navigate to a specific date in the flatpickr calendar
964
Cypress.Commands.add("navigateToFlatpickrDate", targetDate => {
965
    cy.get(".flatpickr-current-month .numInput.cur-year").then($year => {
966
        cy.get(".flatpickr-current-month").then($month => {
967
            const currentYear = parseInt($year.val());
968
969
            // Extract month name from the displayed text
970
            const currentMonthText = $month.text().trim();
971
            const monthNames = [
972
                "January",
973
                "February",
974
                "March",
975
                "April",
976
                "May",
977
                "June",
978
                "July",
979
                "August",
980
                "September",
981
                "October",
982
                "November",
983
                "December",
984
            ];
985
986
            // Find current month index
987
            const currentMonth = monthNames.findIndex(name =>
988
                currentMonthText.includes(name)
989
            );
990
991
            // Calculate how many months to move
992
            const targetYear = targetDate.getFullYear();
993
            const targetMonth = targetDate.getMonth();
994
995
            const monthDiff =
996
                (targetYear - currentYear) * 12 + (targetMonth - currentMonth);
997
998
            if (monthDiff > 0) {
999
                // Move forward
1000
                for (let i = 0; i < monthDiff; i++) {
1001
                    cy.get(".flatpickr-next-month").click();
1002
                    cy.wait(100);
1003
                }
1004
            } else if (monthDiff < 0) {
1005
                // Move backward
1006
                for (let i = 0; i < Math.abs(monthDiff); i++) {
1007
                    cy.get(".flatpickr-prev-month").click();
1008
                    cy.wait(100);
1009
                }
1010
            }
1011
        });
1012
    });
1013
});
1014
1015
/**
1016
 * Helper to get the current value from a Flatpickr input
1017
 *
1018
 * @param {string} selector - jQuery-like selector for the original input element
1019
 * @returns {string} The date value in the input
1020
 *
1021
 * @example
1022
 * // Standalone usage
1023
 * cy.getFlatpickrValue('#dateInput').then(value => {
1024
 *   expect(value).to.equal('2023-05-15');
1025
 * });
1026
 */
1027
Cypress.Commands.add("getFlatpickrValue", selector => {
1028
    return cy.get(selector).invoke("val");
1029
});
1030
1031
/**
1032
 * Helper to assert that a Flatpickr input has a specific date value
1033
 *
1034
 * @param {string} selector - jQuery-like selector for the original input element
1035
 * @param {string} expectedDate - The expected date value in the input
1036
 *
1037
 * @example
1038
 * // Standalone usage
1039
 * cy.flatpickrShouldHaveValue('#dateInput', '2023-05-15');
1040
 *
1041
 * @example
1042
 * // As part of a chain
1043
 * cy.getFlatpickr('#dateInput')
1044
 *   .flatpickr({ selectDate: '2023-05-15' })
1045
 *   .flatpickrShouldHaveValue('#dateInput', '2023-05-15');
1046
 */
1047
Cypress.Commands.add("flatpickrShouldHaveValue", (selector, expectedDate) => {
1048
    return cy.get(selector).should("have.value", expectedDate);
1049
});

Return to bug 39916