Line 0
Link Here
|
0 |
- |
1 |
const dayjs = require("dayjs"); /* Cannot use our calendar JS code, it's in an include file (!) |
|
|
2 |
Also note that moment.js is deprecated */ |
3 |
const isSameOrBefore = require("dayjs/plugin/isSameOrBefore"); |
4 |
dayjs.extend(isSameOrBefore); |
5 |
|
6 |
describe("Booking Modal Tests", () => { |
7 |
// Test data setup |
8 |
const testData = { |
9 |
biblionumber: "134", |
10 |
patronId: "19", |
11 |
pickupLibraryId: "CPL", |
12 |
itemNumber: "287", |
13 |
itemTypeId: "BK", |
14 |
startDate: new Date(new Date().setDate(new Date().getDate() + 1)), // 1 day from now |
15 |
endDate: new Date(new Date().setDate(new Date().getDate() + 5)), // 5 days from now |
16 |
}; |
17 |
|
18 |
beforeEach(() => { |
19 |
cy.login(); |
20 |
cy.title().should("eq", "Koha staff interface"); |
21 |
|
22 |
// Visit the page with the booking modal |
23 |
cy.visit( |
24 |
"/cgi-bin/koha/catalogue/detail.pl?biblionumber=" + |
25 |
testData.biblionumber |
26 |
); |
27 |
|
28 |
// Intercept API calls and provide mock responses |
29 |
cy.intercept("GET", "/api/v1/biblios/*/items?bookable=1&_per_page=-1", { |
30 |
fixture: "bookings/bookable_items.json", |
31 |
}).as("getBookableItems"); |
32 |
|
33 |
cy.fixture("bookings/bookings.json").then(bookings => { |
34 |
const today = dayjs(); |
35 |
|
36 |
// Update the dates in the fixture data relative to today |
37 |
bookings[0].start_date = today |
38 |
.add(8, "day") |
39 |
.startOf("day") |
40 |
.toISOString(); // Today + 8 days at 00:00 |
41 |
bookings[0].end_date = today |
42 |
.add(13, "day") |
43 |
.endOf("day") |
44 |
.toISOString(); // Today + 13 days at 23:59 |
45 |
|
46 |
bookings[1].start_date = today |
47 |
.add(14, "day") |
48 |
.startOf("day") |
49 |
.toISOString(); // Today + 14 days at 00:00 |
50 |
bookings[1].end_date = today |
51 |
.add(18, "day") |
52 |
.endOf("day") |
53 |
.toISOString(); // Today + 18 days at 23:59 |
54 |
|
55 |
bookings[2].start_date = today |
56 |
.add(28, "day") |
57 |
.startOf("day") |
58 |
.toISOString(); // Today + 28 days at 00:00 |
59 |
bookings[2].end_date = today |
60 |
.add(33, "day") |
61 |
.endOf("day") |
62 |
.toISOString(); // Today + 33 days at 23:59 |
63 |
|
64 |
// Use the modified fixture data in your intercept |
65 |
cy.intercept("GET", "/api/v1/bookings?biblio_id=*&_per_page=-1*", { |
66 |
body: bookings, |
67 |
}).as("getBookings"); |
68 |
}); |
69 |
|
70 |
cy.intercept("GET", "/api/v1/biblios/*/checkouts?_per_page=-1", { |
71 |
fixture: "bookings/checkouts.json", |
72 |
}).as("getCheckouts"); |
73 |
|
74 |
cy.intercept("GET", "/api/v1/patrons/*", { |
75 |
fixture: "bookings/patron.json", |
76 |
}).as("getPatron"); |
77 |
|
78 |
cy.intercept("GET", "/api/v1/biblios/*/pickup_locations*", { |
79 |
fixture: "bookings/pickup_locations.json", |
80 |
}).as("getPickupLocations"); |
81 |
|
82 |
cy.intercept("GET", "/api/v1/circulation_rules*", { |
83 |
fixture: "bookings/circulation_rules.json", |
84 |
}).as("getCirculationRules"); |
85 |
|
86 |
cy.intercept("POST", "/api/v1/bookings", { |
87 |
statusCode: 201, |
88 |
body: { |
89 |
booking_id: "1001", |
90 |
start_date: testData.startDate.toISOString(), |
91 |
end_date: testData.endDate.toISOString(), |
92 |
pickup_library_id: testData.pickupLibraryId, |
93 |
biblio_id: testData.biblionumber, |
94 |
item_id: testData.itemNumber, |
95 |
patron_id: testData.patronId, |
96 |
}, |
97 |
}).as("createBooking"); |
98 |
|
99 |
cy.intercept("PUT", "/api/v1/bookings/*", { |
100 |
statusCode: 200, |
101 |
body: { |
102 |
booking_id: "1001", |
103 |
start_date: testData.startDate.toISOString(), |
104 |
end_date: testData.endDate.toISOString(), |
105 |
pickup_library_id: testData.pickupLibraryId, |
106 |
biblio_id: testData.biblionumber, |
107 |
item_id: testData.itemNumber, |
108 |
patron_id: testData.patronId, |
109 |
}, |
110 |
}).as("updateBooking"); |
111 |
|
112 |
// Populate the select2 search results for patron search |
113 |
cy.intercept("GET", "/api/v1/patrons*", { |
114 |
body: [ |
115 |
{ |
116 |
patron_id: testData.patronId, |
117 |
surname: "Doe", |
118 |
firstname: "John", |
119 |
cardnumber: "12345", |
120 |
library_id: "1", |
121 |
library: { |
122 |
name: "Main Library", |
123 |
}, |
124 |
category_id: "1", |
125 |
date_of_birth: "1990-01-01", |
126 |
}, |
127 |
], |
128 |
pagination: { more: false }, |
129 |
}).as("searchPatrons"); |
130 |
|
131 |
// Add clickable button |
132 |
cy.document().then(doc => { |
133 |
const button = doc.createElement("button"); |
134 |
button.setAttribute("data-bs-toggle", "modal"); |
135 |
button.setAttribute("data-bs-target", "#placeBookingModal"); |
136 |
button.setAttribute("data-biblionumber", testData.biblionumber); |
137 |
button.setAttribute("id", "placebooking"); |
138 |
doc.body.appendChild(button); |
139 |
}); |
140 |
}); |
141 |
|
142 |
it("should load the booking modal correctly", () => { |
143 |
// Open the booking modal |
144 |
cy.get("#placebooking").click(); |
145 |
|
146 |
// Check modal title |
147 |
cy.get("#placeBookingLabel").should("contain", "Place booking"); |
148 |
|
149 |
// Check form elements are present |
150 |
cy.get("#booking_patron_id").should("exist"); |
151 |
cy.get("#pickup_library_id").should("exist"); |
152 |
cy.get("#booking_itemtype").should("exist"); |
153 |
cy.get("#booking_item_id").should("exist"); |
154 |
cy.get("#period").should("exist"); |
155 |
|
156 |
// Check hidden fields |
157 |
cy.get("#booking_biblio_id").should( |
158 |
"have.value", |
159 |
testData.biblionumber |
160 |
); |
161 |
cy.get("#booking_start_date").should("have.value", ""); |
162 |
cy.get("#booking_end_date").should("have.value", ""); |
163 |
}); |
164 |
|
165 |
it("should enable fields in proper sequence", () => { |
166 |
// Open the booking modal |
167 |
cy.get("#placebooking").click(); |
168 |
|
169 |
// Initially only patron field should be enabled |
170 |
cy.get("#booking_patron_id").should("not.be.disabled"); |
171 |
cy.get("#pickup_library_id").should("be.disabled"); |
172 |
cy.get("#booking_itemtype").should("be.disabled"); |
173 |
cy.get("#booking_item_id").should("be.disabled"); |
174 |
cy.get("#period").should("be.disabled"); |
175 |
|
176 |
// Select patron |
177 |
cy.selectFromSelect2ByIndex("#booking_patron_id", 0, "John"); |
178 |
//cy.getSelect2("#booking_patron_id") |
179 |
// .select2({ search: "John" }) |
180 |
// .select2({ selectIndex: 0 }); |
181 |
cy.wait("@getPickupLocations"); |
182 |
|
183 |
// After patron selection, pickup location, item type and item should be enabled |
184 |
cy.get("#pickup_library_id").should("not.be.disabled"); |
185 |
cy.get("#booking_itemtype").should("not.be.disabled"); |
186 |
cy.get("#booking_item_id").should("not.be.disabled"); |
187 |
cy.get("#period").should("be.disabled"); |
188 |
|
189 |
// Select pickup location |
190 |
cy.selectFromSelect2ByIndex("#pickup_library_id", 0); |
191 |
|
192 |
// Select item type, trigger circulation rules |
193 |
cy.selectFromSelect2ByIndex("#booking_itemtype", 0); |
194 |
cy.wait("@getCirculationRules"); |
195 |
|
196 |
// After patron, pickup location and itemtype/item selection, date picker should be enabled |
197 |
cy.get("#period").should("not.be.disabled"); |
198 |
|
199 |
// Clear item type and confirm period is disabled |
200 |
cy.clearSelect2("#booking_itemtype"); |
201 |
cy.get("#period").should("be.disabled"); |
202 |
|
203 |
// Select item, re-enable period |
204 |
cy.selectFromSelect2ByIndex("#booking_item_id", 1); |
205 |
cy.get("#period").should("not.be.disabled"); |
206 |
}); |
207 |
|
208 |
it("should handle item type and item dependencies correctly", () => { |
209 |
// Open the booking modal |
210 |
cy.get("#placebooking").click(); |
211 |
|
212 |
// Select patron and pickup location first |
213 |
cy.selectFromSelect2ByIndex("#booking_patron_id", 0, "John"); |
214 |
cy.wait("@getPickupLocations"); |
215 |
cy.selectFromSelect2ByIndex("#pickup_library_id", 0); |
216 |
|
217 |
// Select an item first |
218 |
cy.selectFromSelect2ByIndex("#booking_item_id", 1); |
219 |
cy.wait("@getCirculationRules"); |
220 |
|
221 |
// Verify that item type gets selected automatically |
222 |
cy.get("#booking_itemtype").should("have.value", testData.itemTypeId); |
223 |
|
224 |
// Verify that item type gets disabled |
225 |
cy.get("#booking_itemtype").should("be.disabled"); |
226 |
|
227 |
// Reset the modal |
228 |
cy.get('#placeBookingModal button[data-bs-dismiss="modal"]') |
229 |
.first() |
230 |
.click(); |
231 |
cy.get("#placebooking").click(); |
232 |
|
233 |
// Now select patron, pickup and item type first |
234 |
cy.selectFromSelect2ByIndex("#booking_patron_id", 0, "John"); |
235 |
cy.wait("@getPickupLocations"); |
236 |
cy.selectFromSelect2ByIndex("#pickup_library_id", 0); |
237 |
cy.selectFromSelect2ByIndex("#booking_itemtype", 0); |
238 |
cy.wait("@getCirculationRules"); |
239 |
cy.wait(300); |
240 |
|
241 |
// Verify that only 'Any item' option and items of selected type are enabled |
242 |
cy.get("#booking_item_id > option").then($options => { |
243 |
const enabledOptions = $options.filter(":not(:disabled)"); |
244 |
enabledOptions.each(function () { |
245 |
const $option = cy.wrap(this); |
246 |
|
247 |
// Get both the value and the data-itemtype attribute to make decisions |
248 |
$option.invoke("val").then(value => { |
249 |
if (value === "0") { |
250 |
// We need to re-wrap the element since invoke('val') changed the subject |
251 |
cy.wrap(this).should("contain.text", "Any item"); |
252 |
} else { |
253 |
// Re-wrap the element again for this assertion |
254 |
cy.wrap(this).should( |
255 |
"have.attr", |
256 |
"data-itemtype", |
257 |
testData.itemTypeId |
258 |
); |
259 |
} |
260 |
}); |
261 |
}); |
262 |
}); |
263 |
}); |
264 |
|
265 |
it("should disable dates with existing bookings for same item", () => { |
266 |
// Open the booking modal and setup initial selections |
267 |
cy.get("#placebooking").click(); |
268 |
setupModalForDateTesting(); |
269 |
|
270 |
// Select an item that has existing bookings |
271 |
cy.selectFromSelect2ByIndex("#booking_item_id", 1); |
272 |
|
273 |
// Open the flatpickr |
274 |
cy.openFlatpickr("#period"); |
275 |
|
276 |
// Check that dates with existing bookings are disabled |
277 |
cy.get(".flatpickr-calendar").within(() => { |
278 |
// Days 8-13 should be disabled (from fixture booking data) |
279 |
const today = new Date(); |
280 |
const bookedStart = new Date( |
281 |
today.getTime() + 8 * 24 * 60 * 60 * 1000 |
282 |
); |
283 |
const bookedEnd = new Date( |
284 |
today.getTime() + 13 * 24 * 60 * 60 * 1000 |
285 |
); |
286 |
|
287 |
// Check date before booked range is not disabled |
288 |
const beforeStart = new Date( |
289 |
bookedStart.getTime() - 24 * 60 * 60 * 1000 |
290 |
); |
291 |
getDayElement(beforeStart).should( |
292 |
"not.have.class", |
293 |
"flatpickr-disabled" |
294 |
); |
295 |
|
296 |
// Check dates within the booked range are disabled |
297 |
for (let i = 1; i < 5; i++) { |
298 |
const checkDate = new Date( |
299 |
bookedStart.getTime() + i * 24 * 60 * 60 * 1000 |
300 |
); |
301 |
getDayElement(checkDate).should( |
302 |
"have.class", |
303 |
"flatpickr-disabled" |
304 |
); |
305 |
} |
306 |
|
307 |
// Check date after booked range is not disabled |
308 |
const afterEnd = new Date( |
309 |
bookedEnd.getTime() + 24 * 60 * 60 * 1000 |
310 |
); |
311 |
getDayElement(afterEnd).should( |
312 |
"not.have.class", |
313 |
"flatpickr-disabled" |
314 |
); |
315 |
}); |
316 |
}); |
317 |
|
318 |
it("should disable dates before today and between today and selected start date", () => { |
319 |
// Open the booking modal and setup initial selections |
320 |
cy.get("#placebooking").click(); |
321 |
setupModalForDateTesting(); |
322 |
|
323 |
const today = dayjs(); |
324 |
|
325 |
// Open the flatpickr |
326 |
cy.openFlatpickr("#period"); |
327 |
|
328 |
cy.get(".flatpickr-calendar").within(() => { |
329 |
// Find the first visible date in the calendar to determine range |
330 |
cy.get(".flatpickr-day") |
331 |
.first() |
332 |
.then($firstDay => { |
333 |
const firstDate = dayjs($firstDay.attr("aria-label")); |
334 |
|
335 |
// Check all dates from first visible date up to today are disabled |
336 |
for ( |
337 |
let checkDate = firstDate; |
338 |
checkDate.isSameOrBefore(today); |
339 |
checkDate = checkDate.add(1, "day") |
340 |
) { |
341 |
getDayElement(checkDate.toDate()).should( |
342 |
"have.class", |
343 |
"flatpickr-disabled" |
344 |
); |
345 |
} |
346 |
|
347 |
// Check dates after today are enabled (until we make a selection) |
348 |
for ( |
349 |
let checkDate = today.add(1, "day"); |
350 |
checkDate.isSameOrBefore(today.add(5, "day")); |
351 |
checkDate = checkDate.add(1, "day") |
352 |
) { |
353 |
getDayElement(checkDate.toDate()).should( |
354 |
"not.have.class", |
355 |
"flatpickr-disabled" |
356 |
); |
357 |
} |
358 |
}); |
359 |
}); |
360 |
|
361 |
// Select a start date (3 days from today) |
362 |
const startDate = today.add(3, "day"); |
363 |
cy.selectFlatpickrDate("#period", startDate.toDate()); |
364 |
|
365 |
// Verify dates between today and start date are disabled |
366 |
cy.get(".flatpickr-calendar").within(() => { |
367 |
for ( |
368 |
let checkDate = today.add(1, "day"); |
369 |
checkDate.isBefore(startDate); |
370 |
checkDate = checkDate.add(1, "day") |
371 |
) { |
372 |
getDayElement(checkDate.toDate()).should( |
373 |
"have.class", |
374 |
"flatpickr-disabled" |
375 |
); |
376 |
} |
377 |
|
378 |
// Verify the selected start date itself is not disabled |
379 |
getDayElement(startDate.toDate()).should( |
380 |
"not.have.class", |
381 |
"flatpickr-disabled" |
382 |
); |
383 |
getDayElement(startDate.toDate()).should("have.class", "selected"); |
384 |
}); |
385 |
}); |
386 |
|
387 |
it("should handle lead and trail period hover highlighting", () => { |
388 |
// Open the booking modal and setup initial selections |
389 |
cy.get("#placebooking").click(); |
390 |
setupModalForDateTesting(); |
391 |
|
392 |
// Open the flatpickr |
393 |
cy.openFlatpickr("#period"); |
394 |
|
395 |
// Get a future date to hover over |
396 |
let hoverDate = dayjs(); |
397 |
hoverDate = hoverDate.add(5, "day"); |
398 |
|
399 |
// Hover over a date and check for lead/trail highlighting |
400 |
cy.get(".flatpickr-calendar").within(() => { |
401 |
getDayElement(hoverDate.toDate()).trigger("mouseover"); |
402 |
cy.wait(100); |
403 |
|
404 |
// Check for lead range classes (assuming 2-day lead period from circulation rules) |
405 |
cy.get(".leadRange, .leadRangeStart, .leadRangeEnd").should( |
406 |
"exist" |
407 |
); |
408 |
|
409 |
// Check for trail range classes (assuming 2-day trail period) |
410 |
cy.get(".trailRange, .trailRangeStart, .trailRangeEnd").should( |
411 |
"exist" |
412 |
); |
413 |
}); |
414 |
}); |
415 |
|
416 |
it("should disable click when lead/trail periods overlap with disabled dates", () => { |
417 |
// Open the booking modal and setup initial selections |
418 |
cy.get("#placebooking").click(); |
419 |
setupModalForDateTesting(); |
420 |
|
421 |
// Open the flatpickr |
422 |
cy.openFlatpickr("#period"); |
423 |
|
424 |
// Find a date that would have overlapping lead/trail with disabled dates |
425 |
const today = dayjs(); |
426 |
const problematicDate = today.add(7, "day"); // Just before a booked period |
427 |
|
428 |
cy.get(".flatpickr-calendar").within(() => { |
429 |
getDayElement(problematicDate.toDate()) |
430 |
.trigger("mouseover") |
431 |
.should($el => { |
432 |
expect( |
433 |
$el.hasClass("leadDisable") || |
434 |
$el.hasClass("trailDisable"), |
435 |
"element has either leadDisable or trailDisable" |
436 |
).to.be.true; |
437 |
}); |
438 |
}); |
439 |
}); |
440 |
|
441 |
it("should show event dots for dates with existing bookings", () => { |
442 |
// Open the booking modal and setup initial selections |
443 |
cy.get("#placebooking").click(); |
444 |
setupModalForDateTesting(); |
445 |
|
446 |
// Check for event dots on dates with bookings |
447 |
cy.get(".flatpickr-calendar").within(() => { |
448 |
cy.get(".event-dots").should("exist"); |
449 |
cy.get(".event-dots .event").should("exist"); |
450 |
}); |
451 |
}); |
452 |
|
453 |
it("should handle date selection and availability", () => { |
454 |
// Open the booking modal |
455 |
cy.get("#placebooking").click(); |
456 |
setupModalForDateTesting(); |
457 |
|
458 |
// Get today's date |
459 |
const startDate = new Date(); |
460 |
startDate.setDate(startDate.getDate() + 3); |
461 |
|
462 |
// Get end date (5 days from now) |
463 |
const endDate = new Date(startDate); |
464 |
endDate.setDate(startDate.getDate() + 4); |
465 |
|
466 |
cy.selectFlatpickrDateRange("#period", startDate, endDate); |
467 |
|
468 |
// Use should with retry capability instead of a simple assertion |
469 |
const format = date => date.toISOString().split("T")[0]; |
470 |
cy.get("#period").should( |
471 |
"have.value", |
472 |
`${format(startDate)} to ${format(endDate)}` |
473 |
); |
474 |
|
475 |
// Verify the flatpickr visible input also has value |
476 |
cy.getFlatpickr("#period").should( |
477 |
"have.value", |
478 |
`${format(startDate)} to ${format(endDate)}` |
479 |
); |
480 |
// Now check the hidden fields |
481 |
cy.get("#booking_start_date").should( |
482 |
"have.value", |
483 |
dayjs(startDate).startOf("day").toISOString() |
484 |
); |
485 |
cy.get("#booking_end_date").should( |
486 |
"have.value", |
487 |
dayjs(endDate).endOf("day").toISOString() |
488 |
); |
489 |
}); |
490 |
|
491 |
it("should submit a new booking successfully", () => { |
492 |
// Open the booking modal |
493 |
cy.get("#placebooking").click(); |
494 |
setupModalForDateTesting(); |
495 |
|
496 |
// Set dates with flatpickr |
497 |
cy.window().then(win => { |
498 |
const picker = win.document.getElementById("period")._flatpickr; |
499 |
const startDate = new Date(testData.startDate); |
500 |
const endDate = new Date(testData.endDate); |
501 |
picker.setDate([startDate, endDate], true); |
502 |
}); |
503 |
|
504 |
// Submit the form |
505 |
cy.get("#placeBookingForm").submit(); |
506 |
cy.wait("@createBooking"); |
507 |
|
508 |
// Check success message |
509 |
cy.get("#transient_result").should( |
510 |
"contain", |
511 |
"Booking successfully placed" |
512 |
); |
513 |
|
514 |
// Check modal closes |
515 |
cy.get("#placeBookingModal").should("not.be.visible"); |
516 |
}); |
517 |
|
518 |
it("should edit an existing booking successfully", () => { |
519 |
// Open edit booking modal |
520 |
cy.get("#placebooking") |
521 |
.invoke("attr", "data-booking", "1001") |
522 |
.invoke("attr", "data-patron", "456") |
523 |
.invoke("attr", "data-itemnumber", "789") |
524 |
.invoke("attr", "data-pickup_library", "1") |
525 |
.invoke("attr", "data-start_date", "2025-05-01T00:00:00.000Z") |
526 |
.invoke("attr", "data-end_date", "2025-05-05T23:59:59.999Z") |
527 |
.click(); |
528 |
cy.wait(300); |
529 |
|
530 |
// Check modal title for edit |
531 |
cy.get("#placeBookingLabel").should("contain", "Edit booking"); |
532 |
|
533 |
// Verify booking ID is set |
534 |
cy.get("#booking_id").should("have.value", "1001"); |
535 |
|
536 |
// Verify patron ID is set |
537 |
cy.get("#booking_patron_id").should("have.value", "456"); |
538 |
|
539 |
// Verify itemnumber is set |
540 |
cy.get("#booking_item_id").should("have.value", "789"); |
541 |
|
542 |
// Verify pickup_library is set |
543 |
cy.get("#booking_library_id").should("have.value", "1"); |
544 |
|
545 |
// Verify item_type is set |
546 |
cy.get("#booking_itemtype").should("have.value", "BK"); |
547 |
|
548 |
// Change pickup location |
549 |
cy.get("#pickup_library_id").select("2"); |
550 |
|
551 |
// Submit the form |
552 |
cy.get("#placeBookingForm").submit(); |
553 |
cy.wait("@updateBooking"); |
554 |
|
555 |
// Check success message |
556 |
cy.get("#transient_result").should( |
557 |
"contain", |
558 |
"Booking successfully updated" |
559 |
); |
560 |
|
561 |
// Check modal closes |
562 |
cy.get("#placeBookingModal").should("not.be.visible"); |
563 |
}); |
564 |
|
565 |
it("should handle booking failure gracefully", () => { |
566 |
// Override the create booking intercept to return an error |
567 |
cy.intercept("POST", "/api/v1/bookings", { |
568 |
statusCode: 400, |
569 |
body: { |
570 |
error: "Booking failed", |
571 |
}, |
572 |
}).as("failedBooking"); |
573 |
|
574 |
// Open the booking modal |
575 |
cy.get("#placebooking").click(); |
576 |
|
577 |
// Fill out the booking form |
578 |
cy.selectFromSelect2ByIndex("#booking_patron_id", 0, "John"); |
579 |
cy.wait("@getPickupLocations"); |
580 |
cy.selectFromSelect2ByIndex("#pickup_library_id", 0); |
581 |
cy.selectFromSelect2ByIndex("#booking_item_id", 1); |
582 |
cy.wait("@getCirculationRules"); |
583 |
|
584 |
// Set dates with flatpickr |
585 |
cy.window().then(win => { |
586 |
const picker = win.document.getElementById("period")._flatpickr; |
587 |
const startDate = new Date(testData.startDate); |
588 |
const endDate = new Date(testData.endDate); |
589 |
picker.setDate([startDate, endDate], true); |
590 |
}); |
591 |
|
592 |
// Submit the form |
593 |
cy.get("#placeBookingForm").submit(); |
594 |
cy.wait("@failedBooking"); |
595 |
|
596 |
// Check error message |
597 |
cy.get("#booking_result").should("contain", "Failure"); |
598 |
|
599 |
// Modal should remain open |
600 |
cy.get("#placeBookingModal").should("be.visible"); |
601 |
}); |
602 |
|
603 |
it("should reset form when modal is closed", () => { |
604 |
// Open the booking modal |
605 |
cy.get("#placebooking").click(); |
606 |
setupModalForDateTesting(); |
607 |
|
608 |
// Close the modal |
609 |
cy.get('#placeBookingModal button[data-bs-dismiss="modal"]') |
610 |
.first() |
611 |
.click(); |
612 |
|
613 |
// Re-open the modal |
614 |
cy.get("#placebooking").click(); |
615 |
|
616 |
// Check fields are reset |
617 |
cy.get("#booking_patron_id").should("have.value", null); |
618 |
cy.get("#pickup_library_id").should("be.disabled"); |
619 |
cy.get("#booking_itemtype").should("be.disabled"); |
620 |
cy.get("#booking_item_id").should("be.disabled"); |
621 |
cy.get("#period").should("be.disabled"); |
622 |
cy.get("#booking_start_date").should("have.value", ""); |
623 |
cy.get("#booking_end_date").should("have.value", ""); |
624 |
cy.get("#booking_id").should("have.value", ""); |
625 |
}); |
626 |
|
627 |
// Helper function to setup modal for date testing |
628 |
function setupModalForDateTesting() { |
629 |
// Select patron, pickup location and item |
630 |
cy.selectFromSelect2ByIndex("#booking_patron_id", 0, "John"); |
631 |
cy.wait("@getPickupLocations"); |
632 |
cy.selectFromSelect2ByIndex("#pickup_library_id", 0); |
633 |
cy.selectFromSelect2ByIndex("#booking_item_id", 1); |
634 |
cy.wait("@getCirculationRules"); |
635 |
|
636 |
// Wait for flatpickr to be enabled |
637 |
cy.get("#period").should("not.be.disabled"); |
638 |
} |
639 |
|
640 |
// Helper function to find the day element for a given date |
641 |
function getDayElement(targetDate) { |
642 |
const targetYear = targetDate.getFullYear(); |
643 |
const targetMonth = targetDate.getMonth(); |
644 |
const targetDay = targetDate.getDate(); |
645 |
|
646 |
const monthNames = [ |
647 |
"January", |
648 |
"February", |
649 |
"March", |
650 |
"April", |
651 |
"May", |
652 |
"June", |
653 |
"July", |
654 |
"August", |
655 |
"September", |
656 |
"October", |
657 |
"November", |
658 |
"December", |
659 |
]; |
660 |
|
661 |
// Format the aria-label for the target date |
662 |
const formattedDateLabel = `${monthNames[targetMonth]} ${targetDay}, ${targetYear}`; |
663 |
|
664 |
// Select the day using aria-label |
665 |
return cy.get(`.flatpickr-day[aria-label="${formattedDateLabel}"]`); |
666 |
} |
667 |
}); |