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

(-)a/t/cypress/integration/KohaTable/Holdings_spec.ts (-2 / +2 lines)
Lines 24-30 describe("catalogue/detail/holdings_table with items", () => { Link Here
24
    });
24
    });
25
25
26
    afterEach(function () {
26
    afterEach(function () {
27
        cy.task("deleteSampleObjects", this.objects);
27
        cy.task("deleteSampleObjects", [this.objects]);
28
        cy.set_syspref(
28
        cy.set_syspref(
29
            "AlwaysShowHoldingsTableFilters",
29
            "AlwaysShowHoldingsTableFilters",
30
            this.syspref_AlwaysShowHoldingsTableFilters
30
            this.syspref_AlwaysShowHoldingsTableFilters
Lines 279-285 describe("catalogue/detail/holdings_table without items", () => { Link Here
279
    });
279
    });
280
280
281
    afterEach(function () {
281
    afterEach(function () {
282
        cy.task("deleteSampleObjects", this.objects);
282
        cy.task("deleteSampleObjects", [this.objects]);
283
        cy.set_syspref(
283
        cy.set_syspref(
284
            "AlwaysShowHoldingsTableFilters",
284
            "AlwaysShowHoldingsTableFilters",
285
            this.syspref_AlwaysShowHoldingsTableFilters
285
            this.syspref_AlwaysShowHoldingsTableFilters
(-)a/t/cypress/integration/KohaTable/PendingHolds_spec.ts (+101 lines)
Line 0 Link Here
1
describe("circ/pendingreserves/holdst", () => {
2
    const table_id = "holdst";
3
    beforeEach(() => {
4
        cy.login();
5
        cy.title().should("eq", "Koha staff interface");
6
        cy.task("insertSampleBiblio", {
7
            item_count: 2,
8
            options: { different_libraries: true },
9
        }).then(objects_biblio_1 => {
10
            cy.wrap(objects_biblio_1).as("objects_biblio_1");
11
            const commonLibraryId = objects_biblio_1.items[0].home_library_id;
12
            cy.task("insertSampleHold", {
13
                biblio: objects_biblio_1.biblio,
14
                library_id: commonLibraryId,
15
            }).then(objects_hold_1 => {
16
                cy.wrap(objects_hold_1).as("objects_hold_1");
17
            });
18
            cy.task("insertSampleBiblio", { item_count: 1 }).then(
19
                objects_biblio_2 => {
20
                    cy.wrap(objects_biblio_2).as("objects_biblio_2");
21
                    cy.task("insertSampleHold", {
22
                        biblio: objects_biblio_2.biblio,
23
                        library_id: objects_biblio_2.items[0].home_library_id,
24
                    }).then(objects_hold_2 => {
25
                        cy.wrap(objects_hold_2).as("objects_hold_2");
26
                    });
27
                    cy.task("query", {
28
                        sql: "UPDATE items SET homebranch=?, holdingbranch=? WHERE itemnumber=?",
29
                        values: [
30
                            commonLibraryId,
31
                            commonLibraryId,
32
                            objects_biblio_2.items[0].item_id,
33
                        ],
34
                    });
35
                }
36
            );
37
        });
38
    });
39
40
    afterEach(function () {
41
        cy.task("deleteSampleObjects", [
42
            this.objects_hold_1,
43
            this.objects_hold_2,
44
            this.objects_biblio_1,
45
            this.objects_biblio_2,
46
        ]);
47
    });
48
49
    it("Should render library filters", function () {
50
        cy.visit(
51
            "/cgi-bin/koha/circ/pendingreserves.pl?from=2000-01-01&to=2999-12-31&run_report=Submit"
52
        );
53
54
        cy.get(`#${table_id} thead tr:eq(1) th:eq(5)`).should(
55
            "have.text",
56
            "Libraries"
57
        );
58
59
        // select has library names in value and text, this table is not using server-side processing
60
        let libraries = this.objects_biblio_1.libraries
61
            .map(library => library.name)
62
            .sort();
63
        cy.get(`#${table_id} thead tr:eq(1) th:eq(5) select`)
64
            .children()
65
            .should("have.length", 3)
66
            .then(options => {
67
                expect(options.eq(0).val()).to.eq("");
68
                expect(options.eq(1).val()).to.eq(libraries[0]);
69
                expect(options.eq(2).val()).to.eq(libraries[1]);
70
            });
71
    });
72
73
    it.only("Should filter table on library", function () {
74
        cy.visit(
75
            "/cgi-bin/koha/circ/pendingreserves.pl?from=2000-01-01&to=2999-12-31&run_report=Submit"
76
        );
77
78
        cy.get(`#${table_id} thead tr:eq(1) th:eq(5) select`).select(
79
            this.objects_biblio_1.libraries[0].name,
80
            { force: true }
81
        );
82
        cy.get(`#${table_id} tbody tr:eq(0) td:eq(4)`).should(
83
            "contain",
84
            this.objects_biblio_1.biblio.title
85
        );
86
        cy.get(`#${table_id} tbody tr:eq(1) td:eq(4)`).should(
87
            "contain",
88
            this.objects_biblio_2.biblio.title
89
        );
90
        cy.get(`#${table_id} tbody tr`).should("have.length", 2);
91
92
        cy.get(`#${table_id} thead tr:eq(1) th:eq(5) select`).select(
93
            this.objects_biblio_1.libraries[1].name
94
        );
95
        cy.get(`#${table_id} tbody tr:eq(0) td:eq(4)`).should(
96
            "contain",
97
            this.objects_biblio_1.biblio.title
98
        );
99
        cy.get(`#${table_id} tbody tr`).should("have.length", 1);
100
    });
101
});
(-)a/t/cypress/integration/t/insertData.ts (-5 / +7 lines)
Lines 1-11 Link Here
1
const { query } = require("./../../plugins/db.js");
1
const { query } = require("./../../plugins/db.js");
2
2
3
describe("insertSampleBiblio", () => {
3
describe("insertSampleBiblio", () => {
4
    it("should generate library and item type", () => {
4
    it("insertSampleBiblio - should generate library and item type", () => {
5
        cy.task("insertSampleBiblio", { item_count: 3 }).then(objects => {
5
        cy.task("insertSampleBiblio", { item_count: 3 }).then(objects => {
6
            const biblio_id = objects.biblio.biblio_id;
6
            expect(typeof objects.biblio.biblio_id).to.be.equal("number");
7
            expect(typeof objects.biblio.title).to.be.equal("string");
8
            expect(typeof objects.biblio.author).to.be.equal("string");
7
9
8
            expect(typeof biblio_id).to.be.equal("number");
10
            const biblio_id = objects.biblio.biblio_id;
9
11
10
            cy.task("query", {
12
            cy.task("query", {
11
                sql: "SELECT COUNT(*) as count FROM biblio WHERE biblionumber=?",
13
                sql: "SELECT COUNT(*) as count FROM biblio WHERE biblionumber=?",
Lines 30-36 describe("insertSampleBiblio", () => { Link Here
30
32
31
            cy.task("query", {
33
            cy.task("query", {
32
                sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?",
34
                sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?",
33
                values: [objects.library.library_id],
35
                values: [objects.libraries[0].library_id],
34
            }).then(result => {
36
            }).then(result => {
35
                expect(result[0].count).to.be.equal(1);
37
                expect(result[0].count).to.be.equal(1);
36
            });
38
            });
Lines 60-66 describe("insertSampleBiblio", () => { Link Here
60
62
61
            cy.task("query", {
63
            cy.task("query", {
62
                sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?",
64
                sql: "SELECT COUNT(*) as count FROM branches WHERE branchcode=?",
63
                values: [objects.library.library_id],
65
                values: [objects.libraries[0].library_id],
64
            }).then(result => {
66
            }).then(result => {
65
                expect(result[0].count).to.be.equal(0);
67
                expect(result[0].count).to.be.equal(0);
66
            });
68
            });
(-)a/t/cypress/plugins/index.js (-4 / +8 lines)
Lines 4-9 const { buildSampleObject, buildSampleObjects } = require("./mockData.js"); Link Here
4
4
5
const {
5
const {
6
    insertSampleBiblio,
6
    insertSampleBiblio,
7
    insertSampleHold,
7
    insertObject,
8
    insertObject,
8
    deleteSampleObjects,
9
    deleteSampleObjects,
9
} = require("./insertData.js");
10
} = require("./insertData.js");
Lines 36-46 module.exports = (on, config) => { Link Here
36
        },
37
        },
37
        buildSampleObject,
38
        buildSampleObject,
38
        buildSampleObjects,
39
        buildSampleObjects,
39
        insertSampleBiblio({ item_count }) {
40
        insertSampleBiblio(args) {
40
            return insertSampleBiblio(item_count, baseUrl, authHeader);
41
            return insertSampleBiblio({ ...args, baseUrl, authHeader });
41
        },
42
        },
42
        insertObject({ type, object }) {
43
        insertSampleHold(args) {
43
            return insertObject(type, object, baseUrl, authHeader);
44
            return insertSampleHold({ ...args, baseUrl, authHeader });
45
        },
46
        insertObject(args) {
47
            return insertObject({ ...args, baseUrl, authHeader });
44
        },
48
        },
45
        deleteSampleObjects,
49
        deleteSampleObjects,
46
        query,
50
        query,
(-)a/t/cypress/plugins/insertData.js (-67 / +205 lines)
Lines 3-29 const { query } = require("./db.js"); Link Here
3
3
4
const { apiGet, apiPost } = require("./api-client.js");
4
const { apiGet, apiPost } = require("./api-client.js");
5
5
6
const insertSampleBiblio = async (item_count, baseUrl, authHeader) => {
6
const insertSampleBiblio = async ({
7
    let generated_objects = {};
7
    item_count,
8
    const objects = [{ object: "library" }, { object: "item_type" }];
8
    options,
9
    for (const { object } of objects) {
9
    baseUrl,
10
        const attributes = await buildSampleObject({ object });
10
    authHeader,
11
        generated_objects[object] = attributes;
11
}) => {
12
    }
12
    const generatedItemType = await buildSampleObject({ object: "item_type" });
13
13
    const item_type = await insertObject({
14
    const library = await insertObject(
14
        type: "item_type",
15
        "library",
15
        object: generatedItemType,
16
        generated_objects["library"],
17
        baseUrl,
16
        baseUrl,
18
        authHeader
17
        authHeader,
19
    );
18
    });
20
    const item_type = await insertObject(
21
        "item_type",
22
        generated_objects["item_type"],
23
        baseUrl,
24
        authHeader
25
    );
26
19
20
    let title = "Some boring read";
21
    let author = "Some boring author";
27
    let biblio = {
22
    let biblio = {
28
        leader: "     nam a22     7a 4500",
23
        leader: "     nam a22     7a 4500",
29
        fields: [
24
        fields: [
Lines 32-45 const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { Link Here
32
                245: {
27
                245: {
33
                    ind1: "",
28
                    ind1: "",
34
                    ind2: "",
29
                    ind2: "",
35
                    subfields: [{ a: "Some boring read" }],
30
                    subfields: [{ a: title }],
36
                },
31
                },
37
            },
32
            },
38
            {
33
            {
39
                100: {
34
                100: {
40
                    ind1: "",
35
                    ind1: "",
41
                    ind2: "",
36
                    ind2: "",
42
                    subfields: [{ c: "Some boring author" }],
37
                    subfields: [{ c: author }],
43
                },
38
                },
44
            },
39
            },
45
            {
40
            {
Lines 66-71 const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { Link Here
66
    // We might need to refine that in the future
61
    // We might need to refine that in the future
67
    biblio = {
62
    biblio = {
68
        biblio_id,
63
        biblio_id,
64
        title,
65
        author,
69
    };
66
    };
70
67
71
    let items = buildSampleObjects({
68
    let items = buildSampleObjects({
Lines 80-88 const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { Link Here
80
            restricted_status: 0,
77
            restricted_status: 0,
81
            new_status: null,
78
            new_status: null,
82
            issues: 0,
79
            issues: 0,
80
            checked_out_date: null,
83
            item_type_id: item_type.item_type_id,
81
            item_type_id: item_type.item_type_id,
84
            home_library_id: library.library_id,
85
            holding_library_id: library.library_id,
86
        },
82
        },
87
    });
83
    });
88
    items = items.map(
84
    items = items.map(
Lines 123-129 const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { Link Here
123
        }) => rest
119
        }) => rest
124
    );
120
    );
125
    let createdItems = [];
121
    let createdItems = [];
122
    let libraries = [];
123
    let commonLibrary;
124
    if (!options || !options.different_libraries) {
125
        const generatedLibrary = await buildSampleObject({ object: "library" });
126
        commonLibrary = await insertObject({
127
            type: "library",
128
            object: generatedLibrary,
129
            baseUrl,
130
            authHeader,
131
        });
132
        libraries.push(commonLibrary);
133
    }
126
    for (const item of items) {
134
    for (const item of items) {
135
        if (options?.different_libraries) {
136
            const generatedLibrary = await buildSampleObject({
137
                object: "library",
138
            });
139
            const library = await insertObject({
140
                type: "library",
141
                object: generatedLibrary,
142
                baseUrl,
143
                authHeader,
144
            });
145
            libraries.push(library);
146
            item.home_library_id = library.library_id;
147
            item.holding_library_id = library.library_id;
148
        } else {
149
            item.home_library_id = commonLibrary.library_id;
150
            item.holding_library_id = commonLibrary.library_id;
151
        }
152
127
        await apiPost({
153
        await apiPost({
128
            endpoint: `/api/v1/biblios/${biblio_id}/items`,
154
            endpoint: `/api/v1/biblios/${biblio_id}/items`,
129
            body: item,
155
            body: item,
Lines 131-181 const insertSampleBiblio = async (item_count, baseUrl, authHeader) => { Link Here
131
            authHeader,
157
            authHeader,
132
        }).then(i => createdItems.push(i));
158
        }).then(i => createdItems.push(i));
133
    }
159
    }
134
    return { biblio, items: createdItems, library, item_type };
160
    return { biblio, items: createdItems, libraries, item_type };
135
};
161
};
136
162
137
const deleteSampleObjects = async objects => {
163
const insertSampleHold = async ({
138
    const deletionOrder = ["items", "biblio", "library", "item_type"];
164
    item,
165
    biblio,
166
    library_id,
167
    baseUrl,
168
    authHeader,
169
}) => {
170
    library_id ||= item.home_library_id;
171
    const generatedPatron = await buildSampleObject({
172
        object: "patron",
173
        values: { library_id, incorrect_address: null, patron_card_lost: null },
174
    });
175
176
    const patron = await insertObject({
177
        type: "patron",
178
        object: generatedPatron,
179
        baseUrl,
180
        authHeader,
181
    });
182
183
    const generatedHold = buildSampleObject({
184
        object: "hold",
185
        values: {
186
            patron_id: patron.patron_id,
187
            biblio_id: item?.biblio_id || biblio.biblio_id,
188
            pickup_library_id: library_id,
189
            item_id: item?.item_id || null,
190
        },
191
    });
192
    const hold = await insertObject({
193
        type: "hold",
194
        object: generatedHold,
195
        baseUrl,
196
        authHeader,
197
    });
198
    return { hold, patron };
199
};
200
201
const deleteSampleObjects = async allObjects => {
202
    if (!Array.isArray(allObjects)) {
203
        allObjects = [allObjects];
204
    }
205
206
    const deletionOrder = [
207
        "hold",
208
        "patron",
209
        "item",
210
        "items",
211
        "biblio",
212
        "library",
213
        "libraries",
214
        "item_type",
215
    ];
216
139
    for (const type of deletionOrder) {
217
    for (const type of deletionOrder) {
140
        if (!objects.hasOwnProperty(type)) {
218
        for (const objects of allObjects) {
141
            continue;
219
            if (!objects.hasOwnProperty(type)) {
142
        }
220
                continue;
143
        if (Array.isArray(objects[type]) && objects[type].length == 0) {
221
            }
144
            // Empty array
222
            if (Array.isArray(objects[type]) && objects[type].length == 0) {
145
            continue;
223
                // Empty array
146
        }
224
                continue;
147
        switch (type) {
225
            }
148
            case "biblio":
226
            switch (type) {
149
                await query({
227
                case "biblio":
150
                    sql: "DELETE FROM biblio WHERE biblionumber=?",
228
                    await query({
151
                    values: [objects[type].biblio_id],
229
                        sql: "DELETE FROM biblio WHERE biblionumber=?",
152
                });
230
                        values: [objects[type].biblio_id],
153
                break;
231
                    });
154
            case "items":
232
                    break;
155
                let item_ids = objects[type].map(i => i.item_id);
233
                case "items":
156
                await query({
234
                    let item_ids = objects[type].map(i => i.item_id);
157
                    sql: `DELETE FROM items WHERE itemnumber IN (${item_ids.map(() => "?").join(",")})`,
235
                    await query({
158
                    values: item_ids,
236
                        sql: `DELETE FROM items WHERE itemnumber IN (${item_ids.map(() => "?").join(",")})`,
159
                });
237
                        values: item_ids,
160
                break;
238
                    });
161
            case "item":
239
                    break;
162
                await query({
240
                case "item":
163
                    sql: "DELETE FROM items WHERE itemnumber = ?",
241
                    await query({
164
                    values: [objects[type].item_id],
242
                        sql: "DELETE FROM items WHERE itemnumber = ?",
165
                });
243
                        values: [objects[type].item_id],
166
                break;
244
                    });
167
            case "library":
245
                    break;
168
                await query({
246
                case "library":
169
                    sql: "DELETE FROM branches WHERE branchcode = ?",
247
                    await query({
170
                    values: [objects[type].library_id],
248
                        sql: "DELETE FROM branches WHERE branchcode = ?",
171
                });
249
                        values: [objects[type].library_id],
172
                break;
250
                    });
173
            case "item_type":
251
                    break;
174
                await query({
252
                case "libraries":
175
                    sql: "DELETE FROM itemtypes WHERE itemtype = ?",
253
                    let library_ids = objects[type].map(i => i.library_id);
176
                    values: [objects[type].item_type_id],
254
                    await query({
177
                });
255
                        sql: `DELETE FROM branches WHERE branchcode IN (${library_ids.map(() => "?").join(",")})`,
178
                break;
256
                        values: library_ids,
257
                    });
258
                    break;
259
                case "hold":
260
                    await query({
261
                        sql: "DELETE FROM reserves WHERE reserve_id = ?",
262
                        values: [objects[type].hold_id],
263
                    });
264
                    break;
265
                case "item_type":
266
                    await query({
267
                        sql: "DELETE FROM itemtypes WHERE itemtype = ?",
268
                        values: [objects[type].item_type_id],
269
                    });
270
                    break;
271
                case "patron":
272
                    await query({
273
                        sql: "DELETE FROM borrowers WHERE borrowernumber = ?",
274
                        values: [objects[type].patron_id],
275
                    });
276
                    break;
277
                default:
278
                    console.log(
279
                        `Not implemented yet: cannot deleted object '${type}'`
280
                    );
281
            }
179
        }
282
        }
180
    }
283
    }
181
    return true;
284
    return true;
Lines 199-205 const insertLibrary = async (library, baseUrl, authHeader) => { Link Here
199
    });
302
    });
200
};
303
};
201
304
202
const insertObject = async (type, object, baseUrl, authHeader) => {
305
const insertObject = async ({ type, object, baseUrl, authHeader }) => {
203
    if (type == "patron") {
306
    if (type == "patron") {
204
        await query({
307
        await query({
205
            sql: "SELECT COUNT(*) AS count FROM branches WHERE branchcode = ?",
308
            sql: "SELECT COUNT(*) AS count FROM branches WHERE branchcode = ?",
Lines 274-279 const insertObject = async (type, object, baseUrl, authHeader) => { Link Here
274
                });
377
                });
275
            })
378
            })
276
            .then(item_types => item_types[0]);
379
            .then(item_types => item_types[0]);
380
    } else if (type == "hold") {
381
        const {
382
            hold_id,
383
            deleted_biblio_id,
384
            item_group_id,
385
            desk_id,
386
            cancellation_date,
387
            cancellation_reason,
388
            notes,
389
            priority,
390
            status,
391
            timestamp,
392
            waiting_date,
393
            expiration_date,
394
            lowest_priority,
395
            suspended,
396
            suspended_until,
397
            non_priority,
398
            item_type,
399
            item_level,
400
            cancellation_requested,
401
            biblio,
402
            deleted_biblio,
403
            item,
404
            pickup_library,
405
            hold_date,
406
            ...hold
407
        } = object;
408
409
        return apiPost({
410
            endpoint: `/api/v1/holds`,
411
            body: hold,
412
            baseUrl,
413
            authHeader,
414
        });
277
    } else {
415
    } else {
278
        return false;
416
        return false;
279
    }
417
    }
Lines 283-288 const insertObject = async (type, object, baseUrl, authHeader) => { Link Here
283
421
284
module.exports = {
422
module.exports = {
285
    insertSampleBiblio,
423
    insertSampleBiblio,
424
    insertSampleHold,
286
    insertObject,
425
    insertObject,
287
    deleteSampleObjects,
426
    deleteSampleObjects,
288
};
427
};
289
- 

Return to bug 40180