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 |
}); |