Bugzilla – Attachment 193928 Details for
Bug 41129
Migrate place_booking.js to a Vue island.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 41129: Allow calendar readiness in "any item" default state
Bug-41129-Allow-calendar-readiness-in-any-item-def.patch (text/plain), 30.42 KB, created by
Paul Derscheid
on 2026-02-25 17:20:53 UTC
(
hide
)
Description:
Bug 41129: Allow calendar readiness in "any item" default state
Filename:
MIME Type:
Creator:
Paul Derscheid
Created:
2026-02-25 17:20:53 UTC
Size:
30.42 KB
patch
obsolete
>From 167de2681918b43bae08b4a5d9aebbeab27429a9 Mon Sep 17 00:00:00 2001 >From: Paul Derscheid <paul.derscheid@lmscloud.de> >Date: Wed, 25 Feb 2026 17:17:55 +0100 >Subject: [PATCH] Bug 41129: Allow calendar readiness in "any item" default > state >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >- Remove item type and item ID requirement from formPrefilterValid > and canProceedToStep3; the "any item" default is valid when > bookable items exist >- Remove unused store refs and UI flags from useBookingValidation > >Sponsored-by: Büchereizentrale Schleswig-Holstein <https://www.bz-sh.de/> >--- > .../vue/components/Bookings/BookingModal.vue | 148 +++++++++++------- > .../composables/useBookingValidation.mjs | 9 +- > .../Bookings/lib/booking/validation.mjs | 23 +-- > .../Circulation/bookingsModalBasic_spec.ts | 138 +++++++++------- > 4 files changed, 175 insertions(+), 143 deletions(-) > >diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/BookingModal.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/BookingModal.vue >index a31e4fba0cf..fa1770320c7 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/BookingModal.vue >+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/BookingModal.vue >@@ -1,14 +1,6 @@ > <template> >- <div >- ref="modalElement" >- class="modal fade" >- tabindex="-1" >- role="dialog" >- > >- <div >- class="modal-dialog modal-lg" >- role="document" >- > >+ <div ref="modalElement" class="modal fade" tabindex="-1" role="dialog"> >+ <div class="modal-dialog modal-lg" role="document"> > <div class="modal-content"> > <div class="modal-header"> > <h1 class="modal-title fs-5"> >@@ -164,7 +156,10 @@ import { Modal } from "bootstrap"; > import { appendHiddenInputs } from "./lib/adapters/form.mjs"; > import { calculateStepNumbers } from "./lib/ui/steps.mjs"; > import { useBookingValidation } from "./composables/useBookingValidation.mjs"; >-import { calculateMaxBookingPeriod, getAvailableItemsForPeriod } from "./lib/booking/availability.mjs"; >+import { >+ calculateMaxBookingPeriod, >+ getAvailableItemsForPeriod, >+} from "./lib/booking/availability.mjs"; > import { useFormDefaults } from "./composables/useFormDefaults.mjs"; > import { buildNoItemsAvailableMessage } from "./lib/ui/selection-message.mjs"; > import { useRulesFetcher } from "./composables/useRulesFetcher.mjs"; >@@ -191,7 +186,11 @@ interface AdditionalFieldsInstance { > destroy?: () => void; > } > >-type DateRangeConstraintType = "issuelength" | "issuelength_with_renewals" | "custom" | null; >+type DateRangeConstraintType = >+ | "issuelength" >+ | "issuelength_with_renewals" >+ | "custom" >+ | null; > type SubmitType = "api" | "form-submission"; > > const props = withDefaults( >@@ -217,7 +216,9 @@ const props = withDefaults( > authorizedValues?: Record<string, unknown> | null; > showAdditionalFields?: boolean; > dateRangeConstraint?: DateRangeConstraintType; >- customDateRangeFormula?: ((rules: CirculationRule) => number | null) | null; >+ customDateRangeFormula?: >+ | ((rules: CirculationRule) => number | null) >+ | null; > opacDefaultBookingLibraryEnabled?: boolean | string | null; > opacDefaultBookingLibrary?: string | null; > }>(), >@@ -288,7 +289,8 @@ const modalTitle = computed( > > const showPickupLocationSelect = computed(() => { > if (props.opacDefaultBookingLibraryEnabled !== null) { >- const enabled = props.opacDefaultBookingLibraryEnabled === true || >+ const enabled = >+ props.opacDefaultBookingLibraryEnabled === true || > String(props.opacDefaultBookingLibraryEnabled) === "1"; > return !enabled; > } >@@ -297,7 +299,6 @@ const showPickupLocationSelect = computed(() => { > > const { canSubmit: canSubmitReactive } = useBookingValidation(store, { > showPatronSelect: computed(() => props.showPatronSelect), >- showItemDetailsSelects: computed(() => props.showItemDetailsSelects), > showPickupLocationSelect, > }); > >@@ -315,9 +316,7 @@ const submitLabel = computed(() => > bookingId.value ? $__("Update booking") : $__("Place booking") > ); > >-const isFormSubmission = computed( >- () => props.submitType === "form-submission" >-); >+const isFormSubmission = computed(() => props.submitType === "form-submission"); > > const constraints = computed(() => { > const pickup = constrainPickupLocations( >@@ -353,13 +352,27 @@ const constraints = computed(() => { > }); > > const constrainedFlags = computed(() => constraints.value.flags); >-const constrainedPickupLocations = computed(() => constraints.value.pickupLocations.filtered); >-const constrainedBookableItems = computed(() => constraints.value.bookableItems.filtered); >-const constrainedItemTypes = computed(() => constraints.value.itemTypes.filtered); >-const pickupLocationsFilteredOut = computed(() => constraints.value.pickupLocations.filteredOutCount); >-const pickupLocationsTotal = computed(() => constraints.value.pickupLocations.total); >-const bookableItemsFilteredOut = computed(() => constraints.value.bookableItems.filteredOutCount); >-const bookableItemsTotal = computed(() => constraints.value.bookableItems.total); >+const constrainedPickupLocations = computed( >+ () => constraints.value.pickupLocations.filtered >+); >+const constrainedBookableItems = computed( >+ () => constraints.value.bookableItems.filtered >+); >+const constrainedItemTypes = computed( >+ () => constraints.value.itemTypes.filtered >+); >+const pickupLocationsFilteredOut = computed( >+ () => constraints.value.pickupLocations.filteredOutCount >+); >+const pickupLocationsTotal = computed( >+ () => constraints.value.pickupLocations.total >+); >+const bookableItemsFilteredOut = computed( >+ () => constraints.value.bookableItems.filteredOutCount >+); >+const bookableItemsTotal = computed( >+ () => constraints.value.bookableItems.total >+); > > const maxBookingPeriod = computed(() => > calculateMaxBookingPeriod( >@@ -397,21 +410,16 @@ const dataReady = computed( > !loading.value.checkouts && > (bookableItems.value?.length ?? 0) > 0 > ); >-const formPrefilterValid = computed(() => { >- const requireTypeOrItem = !!props.showItemDetailsSelects; >- const hasTypeOrItem = >- !!bookingItemId.value || !!bookingItemtypeId.value; >- const patronOk = !props.showPatronSelect || !!bookingPatron.value; >- return patronOk && (requireTypeOrItem ? hasTypeOrItem : true); >-}); >+const formPrefilterValid = computed( >+ () => !props.showPatronSelect || !!bookingPatron.value >+); > const hasAvailableItems = computed( > () => constrainedBookableItems.value.length > 0 > ); > > const isCalendarReady = computed(() => { >- const basicReady = dataReady.value && >- formPrefilterValid.value && >- hasAvailableItems.value; >+ const basicReady = >+ dataReady.value && formPrefilterValid.value && hasAvailableItems.value; > if (!basicReady) return false; > if (loading.value.circulationRules) return true; > >@@ -471,15 +479,18 @@ watch( > > if (props.patronId) { > const patron = await store.fetchPatron(props.patronId); >- await store.fetchPickupLocations( >- biblionumber, >- props.patronId >- ); >+ await store.fetchPickupLocations(biblionumber, props.patronId); > > bookingPatron.value = patron; > } > >- bookingItemId.value = (props.itemId != null) ? normalizeIdType(bookableItems.value?.[0]?.item_id, props.itemId) : null; >+ bookingItemId.value = >+ props.itemId != null >+ ? normalizeIdType( >+ bookableItems.value?.[0]?.item_id, >+ props.itemId >+ ) >+ : null; > if (props.itemtypeId) { > bookingItemtypeId.value = props.itemtypeId; > } >@@ -552,7 +563,10 @@ watch( > () => loading.value.pickupLocations, > ], > ([availableItems, patron, pickupLibrary, itemtypeId, isDataReady]) => { >- const pickupLocationsReady = !pickupLibrary || (!loading.value.pickupLocations && pickupLocations.value.length > 0); >+ const pickupLocationsReady = >+ !pickupLibrary || >+ (!loading.value.pickupLocations && >+ pickupLocations.value.length > 0); > const circulationRulesReady = !loading.value.circulationRules; > > if ( >@@ -625,7 +639,6 @@ function clearErrors() { > store.resetErrors(); > } > >- > function resetModalState() { > bookingPatron.value = null; > pickupLibraryId.value = null; >@@ -654,7 +667,10 @@ async function handleSubmit(event) { > const selectedDates = selectedDateRange.value; > > if (!selectedDates || selectedDates.length === 0) { >- store.setUiError($__("Please select a valid date range"), "invalid_date_range"); >+ store.setUiError( >+ $__("Please select a valid date range"), >+ "invalid_date_range" >+ ); > return; > } > >@@ -663,7 +679,10 @@ async function handleSubmit(event) { > const endDateRaw = > selectedDates.length >= 2 ? selectedDates[1] : selectedDates[0]; > // Apply endOf("day") to end date to match upstream storage format (23:59:59) >- const end = BookingDate.from(endDateRaw, { preserveTime: true }).toDayjs().endOf("day").toISOString(); >+ const end = BookingDate.from(endDateRaw, { preserveTime: true }) >+ .toDayjs() >+ .endOf("day") >+ .toISOString(); > const bookingData: Record<string, unknown> = { > booking_id: props.bookingId ?? undefined, > start_date: start, >@@ -709,7 +728,9 @@ async function handleSubmit(event) { > > if (isFormSubmission.value) { > const form = event.target as HTMLFormElement; >- const csrfToken = document.querySelector('[name="csrf_token"]') as HTMLInputElement | null; >+ const csrfToken = document.querySelector( >+ '[name="csrf_token"]' >+ ) as HTMLInputElement | null; > > const dataToSubmit: Record<string, unknown> = { ...bookingData }; > if (dataToSubmit.extended_attributes) { >@@ -718,14 +739,11 @@ async function handleSubmit(event) { > ); > } > >- appendHiddenInputs( >- form, >- [ >- ...Object.entries(dataToSubmit), >- [csrfToken?.name, csrfToken?.value], >- ['op', 'cud-add'], >- ] >- ); >+ appendHiddenInputs(form, [ >+ ...Object.entries(dataToSubmit), >+ [csrfToken?.name, csrfToken?.value], >+ ["op", "cud-add"], >+ ]); > form.submit(); > return; > } >@@ -733,8 +751,12 @@ async function handleSubmit(event) { > try { > // Remove extended_attributes before API call â not yet supported upstream > const { extended_attributes, ...apiData } = bookingData; >- const result = await store.saveOrUpdateBooking(apiData) >- updateExternalDependents(result, bookingPatron.value, !!props.bookingId); >+ const result = await store.saveOrUpdateBooking(apiData); >+ updateExternalDependents( >+ result, >+ bookingPatron.value, >+ !!props.bookingId >+ ); > handleClose(); > } catch (errorObj) { > store.setUiError(processApiError(errorObj), "api"); >@@ -763,7 +785,6 @@ onUnmounted(() => { > } > bsModal?.dispose(); > }); >- > </script> > > <style> >@@ -907,7 +928,8 @@ hr { > border: var(--booking-border-width) solid var(--booking-neutral-300); > border-radius: var(--booking-border-radius-sm); > font-size: var(--booking-text-base); >- transition: border-color var(--booking-transition-fast), >+ transition: >+ border-color var(--booking-transition-fast), > box-shadow var(--booking-transition-fast); > } > >@@ -1075,11 +1097,17 @@ hr { > overflow: hidden; > margin-top: 0; > margin-bottom: 0; >- border-radius: 0 0 var(--booking-border-radius-sm) var(--booking-border-radius-sm); >+ border-radius: 0 0 var(--booking-border-radius-sm) >+ var(--booking-border-radius-sm); > font-size: var(--booking-text-sm); > text-align: center; >- transition: max-height 100ms ease, opacity 100ms ease, padding 100ms ease, >- margin-top 100ms ease, background-color 100ms ease, color 100ms ease; >+ transition: >+ max-height 100ms ease, >+ opacity 100ms ease, >+ padding 100ms ease, >+ margin-top 100ms ease, >+ background-color 100ms ease, >+ color 100ms ease; > } > > .booking-hover-feedback--visible { >diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/composables/useBookingValidation.mjs b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/composables/useBookingValidation.mjs >index b7c88d51d5e..9c697db2634 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/composables/useBookingValidation.mjs >+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/composables/useBookingValidation.mjs >@@ -10,16 +10,13 @@ import { canSubmitBooking } from "../lib/booking/validation.mjs"; > /** > * Composable for booking validation with reactive state > * @param {Object} store - Pinia booking store instance >- * @param {{ showPatronSelect: import('vue').Ref<boolean>|boolean, showItemDetailsSelects: import('vue').Ref<boolean>|boolean, showPickupLocationSelect: import('vue').Ref<boolean>|boolean }} uiFlags - Reactive UI visibility flags (props or refs) >+ * @param {{ showPatronSelect: import('vue').Ref<boolean>|boolean, showPickupLocationSelect: import('vue').Ref<boolean>|boolean }} uiFlags - Reactive UI visibility flags (props or refs) > * @returns {{ canSubmit: import('vue').ComputedRef<boolean> }} > */ > export function useBookingValidation(store, uiFlags = {}) { > const { > bookingPatron, > pickupLibraryId, >- bookingItemtypeId, >- itemTypes, >- bookingItemId, > bookableItems, > selectedDateRange, > } = storeToRefs(store); >@@ -28,12 +25,8 @@ export function useBookingValidation(store, uiFlags = {}) { > const validationData = { > showPatronSelect: uiFlags.showPatronSelect?.value ?? uiFlags.showPatronSelect ?? false, > bookingPatron: bookingPatron.value, >- showItemDetailsSelects: uiFlags.showItemDetailsSelects?.value ?? uiFlags.showItemDetailsSelects ?? false, > showPickupLocationSelect: uiFlags.showPickupLocationSelect?.value ?? uiFlags.showPickupLocationSelect ?? false, > pickupLibraryId: pickupLibraryId.value, >- bookingItemtypeId: bookingItemtypeId.value, >- itemtypeOptions: itemTypes.value, >- bookingItemId: bookingItemId.value, > bookableItems: bookableItems.value, > }; > return canSubmitBooking(validationData, selectedDateRange.value); >diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/lib/booking/validation.mjs b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/lib/booking/validation.mjs >index a354872356a..7297ca22a23 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/lib/booking/validation.mjs >+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Bookings/lib/booking/validation.mjs >@@ -1,6 +1,5 @@ > /** > * Pure functions for booking validation logic >- * Extracted from BookingValidationService to eliminate store coupling > */ > > /** >@@ -8,12 +7,8 @@ > * @param {Object} validationData - All required data for validation > * @param {boolean} validationData.showPatronSelect - Whether patron selection is required > * @param {Object} validationData.bookingPatron - Selected booking patron >- * @param {boolean} validationData.showItemDetailsSelects - Whether item details are required > * @param {boolean} validationData.showPickupLocationSelect - Whether pickup location is required > * @param {string} validationData.pickupLibraryId - Selected pickup library ID >- * @param {string} validationData.bookingItemtypeId - Selected item type ID >- * @param {Array} validationData.itemtypeOptions - Available item type options >- * @param {string} validationData.bookingItemId - Selected item ID > * @param {Array} validationData.bookableItems - Available bookable items > * @returns {boolean} Whether the user can proceed to step 3 > */ >@@ -21,12 +16,8 @@ export function canProceedToStep3(validationData) { > const { > showPatronSelect, > bookingPatron, >- showItemDetailsSelects, > showPickupLocationSelect, > pickupLibraryId, >- bookingItemtypeId, >- itemtypeOptions, >- bookingItemId, > bookableItems, > } = validationData; > >@@ -34,18 +25,8 @@ export function canProceedToStep3(validationData) { > return false; > } > >- if (showItemDetailsSelects || showPickupLocationSelect) { >- if (showPickupLocationSelect && !pickupLibraryId) { >- return false; >- } >- if (showItemDetailsSelects) { >- if (!bookingItemtypeId && itemtypeOptions.length > 0) { >- return false; >- } >- if (!bookingItemId && bookableItems.length > 0) { >- return false; >- } >- } >+ if (showPickupLocationSelect && !pickupLibraryId) { >+ return false; > } > > if (!bookableItems || bookableItems.length === 0) { >diff --git a/t/cypress/integration/Circulation/bookingsModalBasic_spec.ts b/t/cypress/integration/Circulation/bookingsModalBasic_spec.ts >index ceb370e3b45..a446c3e1f5c 100644 >--- a/t/cypress/integration/Circulation/bookingsModalBasic_spec.ts >+++ b/t/cypress/integration/Circulation/bookingsModalBasic_spec.ts >@@ -25,16 +25,23 @@ describe("Booking Modal Basic Tests", () => { > testData = objects; > > // Update items to be bookable with different itemtypes >- return cy.task("query", { >- sql: "UPDATE items SET bookable = 1, itype = 'BK', homebranch = 'CPL', enumchron = 'A', dateaccessioned = '2024-12-03' WHERE itemnumber = ?", >- values: [objects.items[0].item_id], >- }).then(() => cy.task("query", { >- sql: "UPDATE items SET bookable = 1, itype = 'CF', homebranch = 'CPL', enumchron = 'B', dateaccessioned = '2024-12-02' WHERE itemnumber = ?", >- values: [objects.items[1].item_id], >- })).then(() => cy.task("query", { >- sql: "UPDATE items SET bookable = 1, itype = 'BK', homebranch = 'CPL', enumchron = 'C', dateaccessioned = '2024-12-01' WHERE itemnumber = ?", >- values: [objects.items[2].item_id], >- })); >+ return cy >+ .task("query", { >+ sql: "UPDATE items SET bookable = 1, itype = 'BK', homebranch = 'CPL', enumchron = 'A', dateaccessioned = '2024-12-03' WHERE itemnumber = ?", >+ values: [objects.items[0].item_id], >+ }) >+ .then(() => >+ cy.task("query", { >+ sql: "UPDATE items SET bookable = 1, itype = 'CF', homebranch = 'CPL', enumchron = 'B', dateaccessioned = '2024-12-02' WHERE itemnumber = ?", >+ values: [objects.items[1].item_id], >+ }) >+ ) >+ .then(() => >+ cy.task("query", { >+ sql: "UPDATE items SET bookable = 1, itype = 'BK', homebranch = 'CPL', enumchron = 'C', dateaccessioned = '2024-12-01' WHERE itemnumber = ?", >+ values: [objects.items[2].item_id], >+ }) >+ ); > }) > .then(() => { > // Create a test patron using upstream pattern >@@ -92,13 +99,13 @@ describe("Booking Modal Basic Tests", () => { > cy.get("#catalog_detail").should("be.visible"); > > // The "Place booking" button should appear for bookable items >- cy.get("[data-booking-modal]") >- .should("exist") >- .and("be.visible"); >+ cy.get("[data-booking-modal]").should("exist").and("be.visible"); > > // Click to open the booking modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > > // Wait for modal to appear > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( >@@ -122,13 +129,10 @@ describe("Booking Modal Basic Tests", () => { > cy.vueSelectShouldBeDisabled("booking_item_id"); > > // Period should be disabled initially >- cy.get("#booking_period") >- .should("exist") >- .and("be.disabled"); >+ cy.get("#booking_period").should("exist").and("be.disabled"); > > // Verify form and submit button exist >- cy.get('button[form="form-booking"][type="submit"]') >- .should("exist"); >+ cy.get('button[form="form-booking"][type="submit"]').should("exist"); > > cy.get(".btn-close").should("exist"); > }); >@@ -149,7 +153,9 @@ describe("Booking Modal Basic Tests", () => { > > // Open the modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -175,33 +181,32 @@ describe("Booking Modal Basic Tests", () => { > cy.vueSelectShouldBeEnabled("pickup_library_id"); > cy.vueSelectShouldBeEnabled("booking_itemtype"); > cy.vueSelectShouldBeEnabled("booking_item_id"); >- cy.get("#booking_period").should("be.disabled"); // Still disabled until itemtype/item selected >- >- // Step 4: Select pickup location >- cy.vueSelectByIndex("pickup_library_id", 0); >- >- // Step 5: Select item type - this triggers circulation rules API call >- cy.vueSelectByIndex("booking_itemtype", 0); // Select first available itemtype > > // Wait for circulation rules API call to complete > cy.wait("@getCirculationRules"); > >- // After itemtype selection and circulation rules load, period should be enabled >+ // "Any item" is a valid default â period enables without requiring >+ // a specific item type or item selection > cy.get("#booking_period").should("not.be.disabled"); > >- // Step 6: Test clearing item type disables period again (comprehensive workflow) >- cy.vueSelectClear("booking_itemtype"); >- cy.get("#booking_period").should("be.disabled"); >+ // Step 4: Select pickup location >+ cy.vueSelectByIndex("pickup_library_id", 0); > >- // Step 7: Select item instead of itemtype - this also triggers circulation rules >- cy.vueSelectByIndex("booking_item_id", 1); // Skip "Any item" option >+ // Step 5: Select item type >+ cy.vueSelectByIndex("booking_itemtype", 0); > >- // Wait for circulation rules API call (item selection also triggers this) >- cy.wait("@getCirculationRules"); >+ // Period remains enabled after itemtype selection >+ cy.get("#booking_period").should("not.be.disabled"); > >- // Period should be enabled after item selection and circulation rules load >+ // Step 6: Clearing item type keeps period enabled ("any item" still valid) >+ cy.vueSelectClear("booking_itemtype"); > cy.get("#booking_period").should("not.be.disabled"); > >+ // Step 7: Select item instead of itemtype >+ cy.vueSelectByIndex("booking_item_id", 1); >+ >+ // Period stays enabled after item selection >+ cy.get("#booking_period").should("not.be.disabled"); > }); > > it("should handle item type and item dependencies correctly", () => { >@@ -220,7 +225,9 @@ describe("Booking Modal Basic Tests", () => { > > // Open the modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -327,7 +334,9 @@ describe("Booking Modal Basic Tests", () => { > > // Open the modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -348,7 +357,9 @@ describe("Booking Modal Basic Tests", () => { > > // Open the modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -416,7 +427,9 @@ describe("Booking Modal Basic Tests", () => { > > // Open the modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -505,7 +518,9 @@ describe("Booking Modal Basic Tests", () => { > > // Open the modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -562,7 +577,9 @@ describe("Booking Modal Basic Tests", () => { > > // Open booking modal > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -596,8 +613,12 @@ describe("Booking Modal Basic Tests", () => { > cy.get("#booking_period").should($el => { > const fp = $el[0]._flatpickr; > expect(fp.selectedDates.length).to.eq(2); >- expect(dayjs(fp.selectedDates[0]).format("YYYY-MM-DD")).to.eq(startDate.format("YYYY-MM-DD")); >- expect(dayjs(fp.selectedDates[1]).format("YYYY-MM-DD")).to.eq(endDate.format("YYYY-MM-DD")); >+ expect(dayjs(fp.selectedDates[0]).format("YYYY-MM-DD")).to.eq( >+ startDate.format("YYYY-MM-DD") >+ ); >+ expect(dayjs(fp.selectedDates[1]).format("YYYY-MM-DD")).to.eq( >+ endDate.format("YYYY-MM-DD") >+ ); > }); > > // Verify the period field is populated >@@ -686,10 +707,7 @@ describe("Booking Modal Basic Tests", () => { > cy.log("â Edit modal opened with pre-populated data"); > > // Verify core edit fields are pre-populated >- cy.vueSelectShouldHaveValue( >- "booking_patron", >- testData.patron.surname >- ); >+ cy.vueSelectShouldHaveValue("booking_patron", testData.patron.surname); > cy.log("â Patron field pre-populated correctly"); > > // Test that the booking can be retrieved via the real API >@@ -799,7 +817,9 @@ describe("Booking Modal Basic Tests", () => { > `/cgi-bin/koha/catalogue/detail.pl?biblionumber=${testData.biblio.biblio_id}` > ); > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -899,7 +919,9 @@ describe("Booking Modal Basic Tests", () => { > ); > > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -919,7 +941,9 @@ describe("Booking Modal Basic Tests", () => { > cy.get("body").should("not.have.class", "modal-open"); > > // Reopen and verify state is reset >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal.show", { timeout: 10000 }).should( > "be.visible" > ); >@@ -960,7 +984,9 @@ describe("Booking Modal Basic Tests", () => { > ); > > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -1007,7 +1033,9 @@ describe("Booking Modal Basic Tests", () => { > ); > > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >@@ -1058,7 +1086,9 @@ describe("Booking Modal Basic Tests", () => { > ); > > cy.get("booking-modal-island .modal").should("exist"); >- cy.get("[data-booking-modal]").first().then($btn => $btn[0].click()); >+ cy.get("[data-booking-modal]") >+ .first() >+ .then($btn => $btn[0].click()); > cy.get("booking-modal-island .modal", { timeout: 10000 }).should( > "be.visible" > ); >-- >2.53.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 41129
:
188546
|
188551
|
189348
|
189350
|
193909
|
193910
|
193911
|
193912
|
193913
|
193925
|
193926
|
193927
| 193928 |
193929