Bugzilla – Attachment 183375 Details for
Bug 40180
Missing Cypress tests for 'Holds to pull' library filters
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 40180: (bug 40118 follow-up) Add Cypress tests
Bug-40180-bug-40118-follow-up-Add-Cypress-tests.patch (text/plain), 18.27 KB, created by
Jonathan Druart
on 2025-06-19 10:53:06 UTC
(
hide
)
Description:
Bug 40180: (bug 40118 follow-up) Add Cypress tests
Filename:
MIME Type:
Creator:
Jonathan Druart
Created:
2025-06-19 10:53:06 UTC
Size:
18.27 KB
patch
obsolete
>From 5957b190a31137af9d298c46f0d5ea3c8e3b1791 Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Tue, 17 Jun 2025 16:09:59 +0200 >Subject: [PATCH] Bug 40180: (bug 40118 follow-up) Add Cypress tests > >This patch heavily reuses what has been introduced by the previous >patches in the tree, and add a insertSampleHold task to be reusable >easily in other tests. > >Test plan: >All Cypress tests must pass >--- > .../integration/KohaTable/Holdings_spec.ts | 4 +- > .../KohaTable/PendingHolds_spec.ts | 49 +++ > t/cypress/integration/t/insertData.ts | 12 +- > t/cypress/plugins/index.js | 12 +- > t/cypress/plugins/insertData.js | 282 +++++++++++++----- > 5 files changed, 278 insertions(+), 81 deletions(-) > create mode 100644 t/cypress/integration/KohaTable/PendingHolds_spec.ts > >diff --git a/t/cypress/integration/KohaTable/Holdings_spec.ts b/t/cypress/integration/KohaTable/Holdings_spec.ts >index 32fd124f74e..fda13f1951c 100644 >--- a/t/cypress/integration/KohaTable/Holdings_spec.ts >+++ b/t/cypress/integration/KohaTable/Holdings_spec.ts >@@ -24,7 +24,7 @@ describe("catalogue/detail/holdings_table with items", () => { > }); > > afterEach(function () { >- cy.task("deleteSampleObjects", this.objects); >+ cy.task("deleteSampleObjects", [this.objects]); > cy.set_syspref( > "AlwaysShowHoldingsTableFilters", > this.syspref_AlwaysShowHoldingsTableFilters >@@ -279,7 +279,7 @@ describe("catalogue/detail/holdings_table without items", () => { > }); > > afterEach(function () { >- cy.task("deleteSampleObjects", this.objects); >+ cy.task("deleteSampleObjects", [this.objects]); > cy.set_syspref( > "AlwaysShowHoldingsTableFilters", > this.syspref_AlwaysShowHoldingsTableFilters >diff --git a/t/cypress/integration/KohaTable/PendingHolds_spec.ts b/t/cypress/integration/KohaTable/PendingHolds_spec.ts >new file mode 100644 >index 00000000000..160c3d3e987 >--- /dev/null >+++ b/t/cypress/integration/KohaTable/PendingHolds_spec.ts >@@ -0,0 +1,49 @@ >+describe("circ/pendingreserves/holdst", () => { >+ const table_id = "holdst"; >+ beforeEach(() => { >+ cy.login(); >+ cy.title().should("eq", "Koha staff interface"); >+ cy.task("insertSampleBiblio", { item_count: 1 }).then( >+ objects_biblio_1 => { >+ cy.wrap(objects_biblio_1).as("objects_biblio_1"); >+ cy.task("insertSampleHold", { >+ item: objects_biblio_1.items[0], >+ }).then(objects_hold_1 => { >+ cy.wrap(objects_hold_1).as("objects_hold_1"); >+ }); >+ >+ cy.task("insertSampleHold", { >+ item: objects_biblio_1.items[0], >+ }).then(objects_hold_2 => { >+ cy.wrap(objects_hold_2).as("objects_hold_2"); >+ }); >+ } >+ ); >+ cy.task("insertSampleBiblio", { item_count: 1 }).then( >+ objects_biblio_2 => { >+ cy.wrap(objects_biblio_2).as("objects_biblio_2"); >+ cy.task("insertSampleHold", { >+ item: objects_biblio_2.items[0], >+ }).then(objects_hold_3 => { >+ cy.wrap(objects_hold_3).as("objects_hold_3"); >+ }); >+ } >+ ); >+ }); >+ >+ afterEach(function () { >+ cy.task("deleteSampleObjects", [ >+ this.objects_hold_1, >+ this.objects_hold_2, >+ this.objects_hold_3, >+ this.objects_biblio_2, >+ this.objects_biblio_1, >+ ]); >+ }); >+ >+ it("test", function () { >+ cy.visit( >+ "/cgi-bin/koha/circ/pendingreserves.pl?from=2000-01-01&to=2999-12-31&run_report=Submit" >+ ); >+ }); >+}); >diff --git a/t/cypress/integration/t/insertData.ts b/t/cypress/integration/t/insertData.ts >index ff163c606a7..9055067b541 100644 >--- a/t/cypress/integration/t/insertData.ts >+++ b/t/cypress/integration/t/insertData.ts >@@ -1,11 +1,13 @@ > const { query } = require("./../../plugins/db.js"); > > describe("insertSampleBiblio", () => { >- it("should generate library and item type", () => { >+ it("insertSampleBiblio - should generate library and item type", () => { > cy.task("insertSampleBiblio", { item_count: 3 }).then(objects => { >- const biblio_id = objects.biblio.biblio_id; >+ expect(typeof objects.biblio.biblio_id).to.be.equal("number"); >+ expect(typeof objects.biblio.title).to.be.equal("string"); >+ expect(typeof objects.biblio.author).to.be.equal("string"); > >- expect(typeof biblio_id).to.be.equal("number"); >+ const biblio_id = objects.biblio.biblio_id; > > cy.task("query", { > sql: "SELECT COUNT(*) as count FROM biblio WHERE biblionumber=?", >@@ -30,7 +32,7 @@ describe("insertSampleBiblio", () => { > > cy.task("query", { > sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- values: [objects.library.library_id], >+ values: [objects.libraries[0].library_id], > }).then(result => { > expect(result[0].count).to.be.equal(1); > }); >@@ -60,7 +62,7 @@ describe("insertSampleBiblio", () => { > > cy.task("query", { > sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?", >- values: [objects.library.library_id], >+ values: [objects.libraries[0].library_id], > }).then(result => { > expect(result[0].count).to.be.equal(0); > }); >diff --git a/t/cypress/plugins/index.js b/t/cypress/plugins/index.js >index ff52883306a..a3cbd0c908b 100644 >--- a/t/cypress/plugins/index.js >+++ b/t/cypress/plugins/index.js >@@ -4,6 +4,7 @@ const { buildSampleObject, buildSampleObjects } = require("./mockData.js"); > > const { > insertSampleBiblio, >+ insertSampleHold, > insertObject, > deleteSampleObjects, > } = require("./insertData.js"); >@@ -36,11 +37,14 @@ module.exports = (on, config) => { > }, > buildSampleObject, > buildSampleObjects, >- insertSampleBiblio({ item_count }) { >- return insertSampleBiblio(item_count, baseUrl, authHeader); >+ insertSampleBiblio(args) { >+ return insertSampleBiblio({ ...args, baseUrl, authHeader }); > }, >- insertObject({ type, object }) { >- return insertObject(type, object, baseUrl, authHeader); >+ insertSampleHold(args) { >+ return insertSampleHold({ ...args, baseUrl, authHeader }); >+ }, >+ insertObject(args) { >+ return insertObject({ ...args, baseUrl, authHeader }); > }, > deleteSampleObjects, > query, >diff --git a/t/cypress/plugins/insertData.js b/t/cypress/plugins/insertData.js >index 3063679d177..954a86a8fa5 100644 >--- a/t/cypress/plugins/insertData.js >+++ b/t/cypress/plugins/insertData.js >@@ -3,27 +3,23 @@ const { query } = require("./db.js"); > > const { apiGet, apiPost } = require("./api-client.js"); > >-const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { >- let generated_objects = {}; >- const objects = [{ object: "library" }, { object: "item_type" }]; >- for (const { object } of objects) { >- const attributes = await buildSampleObject({ object }); >- generated_objects[object] = attributes; >- } >+const insertSampleBiblio = async ({ >+ item_count, >+ options, >+ baseUrl, >+ authHeader, >+}) => { > >- const library = await insertObject( >- "library", >- generated_objects["library"], >+ const generatedItemType = await buildSampleObject({ object: "item_type" }); >+ const item_type = await insertObject({ >+ type: "item_type", >+ object: generatedItemType, > baseUrl, >- authHeader >- ); >- const item_type = await insertObject( >- "item_type", >- generated_objects["item_type"], >- baseUrl, >- authHeader >- ); >+ authHeader, >+ }); > >+ let title = "Some boring read"; >+ let author = "Some boring author"; > let biblio = { > leader: " nam a22 7a 4500", > fields: [ >@@ -32,14 +28,14 @@ const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { > 245: { > ind1: "", > ind2: "", >- subfields: [{ a: "Some boring read" }], >+ subfields: [{ a: title }], > }, > }, > { > 100: { > ind1: "", > ind2: "", >- subfields: [{ c: "Some boring author" }], >+ subfields: [{ c: author }], > }, > }, > { >@@ -66,6 +62,8 @@ const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { > // We might need to refine that in the future > biblio = { > biblio_id, >+ title, >+ author, > }; > > let items = buildSampleObjects({ >@@ -80,9 +78,8 @@ const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { > restricted_status: 0, > new_status: null, > issues: 0, >+ checked_out_date: null, > item_type_id: item_type.item_type_id, >- home_library_id: library.library_id, >- holding_library_id: library.library_id, > }, > }); > items = items.map( >@@ -123,59 +120,168 @@ const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { > }) => rest > ); > let createdItems = []; >- for (const item of items) { >- await apiPost({ >- endpoint: `/api/v1/biblios/${biblio_id}/items`, >- body: item, >+ let libraries = []; >+ let commonLibrary; >+ if (!options || !options.different_libraries) { >+ const generatedLibrary = await buildSampleObject({ object: "library" }); >+ commonLibrary = await insertObject({ >+ type: "library", >+ object: generatedLibrary, > baseUrl, > authHeader, >- }).then(i => createdItems.push(i)); >+ }); >+ libraries.push(commonLibrary); > } >- return { biblio, items: createdItems, library, item_type }; >+ for (const item of items) { >+ if (options?.different_libraries) { >+ const generatedLibrary = await buildSampleObject({ >+ object: "library", >+ }); >+ const library = await insertObject({ >+ type: "library", >+ object: generatedLibrary, >+ baseUrl, >+ authHeader, >+ }); >+ libraries.push(library); >+ item.home_library_id = library.library_id; >+ item.holding_library_id = library.library_id; >+ } else { >+ item.home_library_id = commonLibrary.library_id; >+ item.holding_library_id = commonLibrary.library_id; >+ } >+ >+ await apiPost({ >+ endpoint: `/api/v1/biblios/${biblio_id}/items`, >+ body: item, >+ baseUrl, >+ authHeader, >+ }) >+ .then(i => createdItems.push(i)); >+ } >+ return { biblio, items: createdItems, libraries, item_type }; >+}; >+ >+const insertSampleHold = async ({ >+ item, >+ biblio, >+ library_id, >+ baseUrl, >+ authHeader, >+}) => { >+ >+ library_id ||= item.home_library_id; >+ const generatedPatron = await buildSampleObject({ >+ object: "patron", >+ values: { library_id }, >+ }); >+ >+ const patron = await insertObject({ >+ type: "patron", >+ object: generatedPatron, >+ baseUrl, >+ authHeader, >+ }); >+ >+ const generatedHold = buildSampleObject({ >+ object: "hold", >+ values: { >+ patron_id: patron.patron_id, >+ biblio_id: item?.biblio_id || biblio.biblio_id, >+ pickup_library_id: library_id, >+ item_id: item?.item_id || null, >+ }, >+ }); >+ const hold = await insertObject({ >+ type: "hold", >+ object: generatedHold, >+ baseUrl, >+ authHeader, >+ }); >+ return { hold, patron }; > }; > >-const deleteSampleObjects = async objects => { >- const deletionOrder = ["items", "biblio", "library", "item_type"]; >+const deleteSampleObjects = async allObjects => { >+ if (!Array.isArray(allObjects)) { >+ allObjects = [allObjects]; >+ } >+ >+ const deletionOrder = [ >+ "hold", >+ "patron", >+ "item", >+ "items", >+ "biblio", >+ "library", >+ "libraries", >+ "item_type", >+ ]; >+ > for (const type of deletionOrder) { >- if (!objects.hasOwnProperty(type)) { >- continue; >- } >- if (Array.isArray(objects[type]) && objects[type].length == 0) { >- // Empty array >- continue; >- } >- switch (type) { >- case "biblio": >- await query({ >- sql: "DELETE FROM biblio WHERE biblionumber=?", >- values: [objects[type].biblio_id], >- }); >- break; >- case "items": >- let item_ids = objects[type].map(i => i.item_id); >- await query({ >- sql: `DELETE FROM items WHERE itemnumber IN (${item_ids.map(() => "?").join(",")})`, >- values: item_ids, >- }); >- break; >- case "item": >- await query({ >- sql: "DELETE FROM items WHERE itemnumber = ?", >- values: [objects[type].item_id], >- }); >- break; >- case "library": >- await query({ >- sql: "DELETE FROM branches WHERE branchcode = ?", >- values: [objects[type].library_id], >- }); >- break; >- case "item_type": >- await query({ >- sql: "DELETE FROM itemtypes WHERE itemtype = ?", >- values: [objects[type].item_type_id], >- }); >- break; >+ for (const objects of allObjects) { >+ if (!objects.hasOwnProperty(type)) { >+ continue; >+ } >+ if (Array.isArray(objects[type]) && objects[type].length == 0) { >+ // Empty array >+ continue; >+ } >+ switch (type) { >+ case "biblio": >+ await query({ >+ sql: "DELETE FROM biblio WHERE biblionumber=?", >+ values: [objects[type].biblio_id], >+ }); >+ break; >+ case "items": >+ let item_ids = objects[type].map(i => i.item_id); >+ await query({ >+ sql: `DELETE FROM items WHERE itemnumber IN (${item_ids.map(() => "?").join(",")})`, >+ values: item_ids, >+ }); >+ break; >+ case "item": >+ await query({ >+ sql: "DELETE FROM items WHERE itemnumber = ?", >+ values: [objects[type].item_id], >+ }); >+ break; >+ case "library": >+ await query({ >+ sql: "DELETE FROM branches WHERE branchcode = ?", >+ values: [objects[type].library_id], >+ }); >+ break; >+ case "libraries": >+ let library_ids = objects[type].map(i => i.library_id); >+ await query({ >+ sql: `DELETE FROM branches WHERE branchcode IN (${library_ids.map(() => "?").join(",")})`, >+ values: library_ids, >+ }); >+ break; >+ case "hold": >+ await query({ >+ sql: "DELETE FROM reserves WHERE reserve_id = ?", >+ values: [objects[type].hold_id], >+ }); >+ break; >+ case "item_type": >+ await query({ >+ sql: "DELETE FROM itemtypes WHERE itemtype = ?", >+ values: [objects[type].item_type_id], >+ }); >+ break; >+ case "patron": >+ await query({ >+ sql: "DELETE FROM borrowers WHERE borrowernumber = ?", >+ values: [objects[type].patron_id], >+ }); >+ break; >+ default: >+ console.log( >+ `Not implemented yet: cannot deleted object '${type}'` >+ ); >+ } > } > } > return true; >@@ -199,7 +305,7 @@ const insertLibrary = async (library, baseUrl, authHeader) => { > }); > }; > >-const insertObject = async (type, object, baseUrl, authHeader) => { >+const insertObject = async ({ type, object, baseUrl, authHeader }) => { > if (type == "patron") { > await query({ > sql: "SELECT COUNT(*) AS count FROM branches WHERE branchcode = ?", >@@ -274,6 +380,41 @@ const insertObject = async (type, object, baseUrl, authHeader) => { > }); > }) > .then(item_types => item_types[0]); >+ } else if (type == "hold") { >+ const { >+ hold_id, >+ deleted_biblio_id, >+ item_group_id, >+ desk_id, >+ cancellation_date, >+ cancellation_reason, >+ notes, >+ priority, >+ status, >+ timestamp, >+ waiting_date, >+ expiration_date, >+ lowest_priority, >+ suspended, >+ suspended_until, >+ non_priority, >+ item_type, >+ item_level, >+ cancellation_requested, >+ biblio, >+ deleted_biblio, >+ item, >+ pickup_library, >+ hold_date, >+ ...hold >+ } = object; >+ >+ return apiPost({ >+ endpoint: `/api/v1/holds`, >+ body: hold, >+ baseUrl, >+ authHeader, >+ }); > } else { > return false; > } >@@ -283,6 +424,7 @@ const insertObject = async (type, object, baseUrl, authHeader) => { > > module.exports = { > insertSampleBiblio, >+ insertSampleHold, > insertObject, > deleteSampleObjects, > }; >-- >2.34.1
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 40180
: 183375 |
183376