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

(-)a/t/cypress/fixtures/edifact_test_data.js (+678 lines)
Line 0 Link Here
1
// Comprehensive EDIFACT test data for Cypress tests
2
// Based on real EDIFACT structure but with sanitized/synthetic data
3
4
export const edifactTestData = {
5
    // Test data with multiple messages for focus testing
6
    multipleMessages: {
7
        header: "UNB+UNOC:3+TEST_SUPPLIER:14+TEST_LIBRARY:14+20250721:1234+1++1++1'",
8
        messages: [
9
            {
10
                id: 1,
11
                header: "UNH+1+ORDERS:D:96A:UN:EAN008'",
12
                segments: [
13
                    {
14
                        tag: "BGM",
15
                        raw: "BGM+220+BASKET001+9'",
16
                        elements: ["220", "BASKET001", "9"],
17
                        description: "Beginning of Message - Order",
18
                    },
19
                    {
20
                        tag: "DTM",
21
                        raw: "DTM+137:20250721:102'",
22
                        elements: ["137", "20250721", "102"],
23
                        description: "Date/Time - Order Date",
24
                    },
25
                    {
26
                        tag: "RFF",
27
                        raw: "RFF+ON:LibraryBasket001'",
28
                        elements: ["ON", "LibraryBasket001"],
29
                        description: "Reference - Order Number",
30
                    },
31
                    {
32
                        tag: "NAD",
33
                        raw: "NAD+BY+1234567890123::91++Test Library System+123 Library Street+Booktown+12345+US'",
34
                        elements: [
35
                            "BY",
36
                            "1234567890123",
37
                            "",
38
                            "91",
39
                            "",
40
                            "Test Library System",
41
                            "123 Library Street",
42
                            "Booktown",
43
                            "12345",
44
                            "US",
45
                        ],
46
                        description: "Name and Address - Buyer",
47
                    },
48
                    {
49
                        tag: "LIN",
50
                        raw: "LIN+1++9781234567890:IB'",
51
                        elements: ["1", "", "9781234567890", "IB"],
52
                        description: "Line Item",
53
                    },
54
                    {
55
                        tag: "PIA",
56
                        raw: "PIA+5+BOOK001:SA'",
57
                        elements: ["5", "BOOK001", "SA"],
58
                        description: "Product Identification",
59
                    },
60
                    {
61
                        tag: "IMD",
62
                        raw: "IMD+L+009+:::Test Author'",
63
                        elements: ["L", "009", "", "", "", "Test Author"],
64
                        description: "Item Description - Author",
65
                    },
66
                    {
67
                        tag: "IMD",
68
                        raw: "IMD+L+050+:::Introduction to Library Science'",
69
                        elements: [
70
                            "L",
71
                            "050",
72
                            "",
73
                            "",
74
                            "",
75
                            "Introduction to Library Science",
76
                        ],
77
                        description: "Item Description - Title",
78
                    },
79
                    {
80
                        tag: "QTY",
81
                        raw: "QTY+21:3'",
82
                        elements: ["21", "3"],
83
                        description: "Quantity",
84
                    },
85
                    {
86
                        tag: "PRI",
87
                        raw: "PRI+AAE:29.95:CA'",
88
                        elements: ["AAE", "29.95", "CA"],
89
                        description: "Price",
90
                    },
91
                    {
92
                        tag: "RFF",
93
                        raw: "RFF+LI:ORDER001'",
94
                        elements: ["LI", "ORDER001"],
95
                        description: "Reference - Line Item",
96
                    },
97
                ],
98
                trailer: "UNT+12+1'",
99
            },
100
            {
101
                id: 2,
102
                header: "UNH+2+INVOIC:D:96A:UN:EAN008'",
103
                segments: [
104
                    {
105
                        tag: "BGM",
106
                        raw: "BGM+380+INV002+9'",
107
                        elements: ["380", "INV002", "9"],
108
                        description: "Beginning of Message - Invoice",
109
                    },
110
                    {
111
                        tag: "DTM",
112
                        raw: "DTM+137:20250720:102'",
113
                        elements: ["137", "20250720", "102"],
114
                        description: "Date/Time - Invoice Date",
115
                    },
116
                    {
117
                        tag: "RFF",
118
                        raw: "RFF+IV:INVOICE002'",
119
                        elements: ["IV", "INVOICE002"],
120
                        description: "Reference - Invoice Number",
121
                    },
122
                    {
123
                        tag: "RFF",
124
                        raw: "RFF+ON:LibraryBasket002'",
125
                        elements: ["ON", "LibraryBasket002"],
126
                        description: "Reference - Order Number",
127
                    },
128
                    {
129
                        tag: "NAD",
130
                        raw: "NAD+SU+9876543210987::91++Test Book Supplier+456 Publisher Ave+Bookville+54321+US'",
131
                        elements: [
132
                            "SU",
133
                            "9876543210987",
134
                            "",
135
                            "91",
136
                            "",
137
                            "Test Book Supplier",
138
                            "456 Publisher Ave",
139
                            "Bookville",
140
                            "54321",
141
                            "US",
142
                        ],
143
                        description: "Name and Address - Supplier",
144
                    },
145
                    {
146
                        tag: "LIN",
147
                        raw: "LIN+1++9789876543210:IB'",
148
                        elements: ["1", "", "9789876543210", "IB"],
149
                        description: "Line Item",
150
                    },
151
                    {
152
                        tag: "IMD",
153
                        raw: "IMD+L+009+:::Sample Author'",
154
                        elements: ["L", "009", "", "", "", "Sample Author"],
155
                        description: "Item Description - Author",
156
                    },
157
                    {
158
                        tag: "IMD",
159
                        raw: "IMD+L+050+:::Advanced Library Management'",
160
                        elements: [
161
                            "L",
162
                            "050",
163
                            "",
164
                            "",
165
                            "",
166
                            "Advanced Library Management",
167
                        ],
168
                        description: "Item Description - Title",
169
                    },
170
                    {
171
                        tag: "QTY",
172
                        raw: "QTY+47:2'",
173
                        elements: ["47", "2"],
174
                        description: "Quantity Invoiced",
175
                    },
176
                    {
177
                        tag: "PRI",
178
                        raw: "PRI+AAA:45.00:CA'",
179
                        elements: ["AAA", "45.00", "CA"],
180
                        description: "Unit Price",
181
                    },
182
                    {
183
                        tag: "MOA",
184
                        raw: "MOA+203:90.00:USD'",
185
                        elements: ["203", "90.00", "USD"],
186
                        description: "Monetary Amount - Line Total",
187
                    },
188
                ],
189
                trailer: "UNT+13+2'",
190
            },
191
            {
192
                id: 3,
193
                header: "UNH+3+ORDERS:D:96A:UN:EAN008'",
194
                segments: [
195
                    {
196
                        tag: "BGM",
197
                        raw: "BGM+220+BASKET002+9'",
198
                        elements: ["220", "BASKET002", "9"],
199
                        description: "Beginning of Message - Order",
200
                    },
201
                    {
202
                        tag: "DTM",
203
                        raw: "DTM+137:20250719:102'",
204
                        elements: ["137", "20250719", "102"],
205
                        description: "Date/Time - Order Date",
206
                    },
207
                    {
208
                        tag: "RFF",
209
                        raw: "RFF+ON:SpecialOrder123'",
210
                        elements: ["ON", "SpecialOrder123"],
211
                        description: "Reference - Order Number",
212
                    },
213
                    {
214
                        tag: "LIN",
215
                        raw: "LIN+1++9781111222333:IB'",
216
                        elements: ["1", "", "9781111222333", "IB"],
217
                        description: "Line Item",
218
                    },
219
                    {
220
                        tag: "IMD",
221
                        raw: "IMD+L+050+:::Digital Archives and Preservation'",
222
                        elements: [
223
                            "L",
224
                            "050",
225
                            "",
226
                            "",
227
                            "",
228
                            "Digital Archives and Preservation",
229
                        ],
230
                        description: "Item Description - Title",
231
                    },
232
                    {
233
                        tag: "QTY",
234
                        raw: "QTY+21:1'",
235
                        elements: ["21", "1"],
236
                        description: "Quantity",
237
                    },
238
                    {
239
                        tag: "PRI",
240
                        raw: "PRI+AAE:125.00:CA'",
241
                        elements: ["AAE", "125.00", "CA"],
242
                        description: "Price",
243
                    },
244
                ],
245
                trailer: "UNT+8+3'",
246
            },
247
        ],
248
        trailer: "UNZ+3+1'",
249
    },
250
251
    // Test data for search functionality
252
    searchableContent: {
253
        header: "UNB+UNOC:3+SEARCH_SUPPLIER:14+SEARCH_LIBRARY:14+20250721:1500+3++1++1'",
254
        messages: [
255
            {
256
                id: 1,
257
                header: "UNH+1+ORDERS:D:96A:UN:EAN008'",
258
                segments: [
259
                    {
260
                        tag: "BGM",
261
                        raw: "BGM+220+SEARCH_BASKET+9'",
262
                        elements: ["220", "SEARCH_BASKET", "9"],
263
                        description: "Beginning of Message - Order",
264
                    },
265
                    {
266
                        tag: "IMD",
267
                        raw: "IMD+L+009+:::JavaScript Programming Guide'",
268
                        elements: [
269
                            "L",
270
                            "009",
271
                            "",
272
                            "",
273
                            "",
274
                            "JavaScript Programming Guide",
275
                        ],
276
                        description: "Item Description - Title",
277
                    },
278
                    {
279
                        tag: "IMD",
280
                        raw: "IMD+L+050+:::Learn modern JavaScript programming techniques'",
281
                        elements: [
282
                            "L",
283
                            "050",
284
                            "",
285
                            "",
286
                            "",
287
                            "Learn modern JavaScript programming techniques",
288
                        ],
289
                        description: "Item Description - Description",
290
                    },
291
                    {
292
                        tag: "PIA",
293
                        raw: "PIA+5+JS001:SA+9781234567890:IB'",
294
                        elements: ["5", "JS001", "SA", "9781234567890", "IB"],
295
                        description: "Product Identification",
296
                    },
297
                    {
298
                        tag: "FTX",
299
                        raw: "FTX+LIN+++Essential reading for web developers learning JavaScript'",
300
                        elements: [
301
                            "LIN",
302
                            "",
303
                            "",
304
                            "Essential reading for web developers learning JavaScript",
305
                        ],
306
                        description: "Free Text",
307
                    },
308
                ],
309
                trailer: "UNT+6+1'",
310
            },
311
            {
312
                id: 2,
313
                header: "UNH+2+ORDERS:D:96A:UN:EAN008'",
314
                segments: [
315
                    {
316
                        tag: "BGM",
317
                        raw: "BGM+220+ORDER_BASKET_002+9'",
318
                        elements: ["220", "ORDER_BASKET_002", "9"],
319
                        description: "Beginning of Message - Order",
320
                    },
321
                    {
322
                        tag: "DTM",
323
                        raw: "DTM+137:20250721:102'",
324
                        elements: ["137", "20250721", "102"],
325
                        description: "Date/Time - Order Date",
326
                    },
327
                    {
328
                        tag: "RFF",
329
                        raw: "RFF+ON:ORDER12345'",
330
                        elements: ["ON", "ORDER12345"],
331
                        description: "Reference - Order Number",
332
                    },
333
                    {
334
                        tag: "IMD",
335
                        raw: "IMD+L+009+:::Advanced JavaScript Concepts'",
336
                        elements: [
337
                            "L",
338
                            "009",
339
                            "",
340
                            "",
341
                            "",
342
                            "Advanced JavaScript Concepts",
343
                        ],
344
                        description: "Item Description - Title",
345
                    },
346
                    {
347
                        tag: "FTX",
348
                        raw: "FTX+LIN+++Comprehensive guide to ORDER processing and JavaScript'",
349
                        elements: [
350
                            "LIN",
351
                            "",
352
                            "",
353
                            "Comprehensive guide to ORDER processing and JavaScript",
354
                        ],
355
                        description: "Free Text",
356
                    },
357
                ],
358
                trailer: "UNT+6+2'",
359
            },
360
            {
361
                id: 3,
362
                header: "UNH+3+INVOIC:D:96A:UN:EAN008'",
363
                segments: [
364
                    {
365
                        tag: "BGM",
366
                        raw: "BGM+380+INV_BASKET_003+9'",
367
                        elements: ["380", "INV_BASKET_003", "9"],
368
                        description: "Beginning of Message - Invoice",
369
                    },
370
                    {
371
                        tag: "DTM",
372
                        raw: "DTM+137:20250720:102'",
373
                        elements: ["137", "20250720", "102"],
374
                        description: "Date/Time - Invoice Date",
375
                    },
376
                    {
377
                        tag: "RFF",
378
                        raw: "RFF+IV:220+ORDER67890'",
379
                        elements: ["IV", "220+ORDER67890"],
380
                        description: "Reference - Invoice with ORDER reference",
381
                    },
382
                    {
383
                        tag: "IMD",
384
                        raw: "IMD+L+009+:::JavaScript Testing Framework'",
385
                        elements: [
386
                            "L",
387
                            "009",
388
                            "",
389
                            "",
390
                            "",
391
                            "JavaScript Testing Framework",
392
                        ],
393
                        description: "Item Description - Title",
394
                    },
395
                    {
396
                        tag: "MOA",
397
                        raw: "MOA+203:125.00:USD'",
398
                        elements: ["203", "125.00", "USD"],
399
                        description: "Monetary Amount - Total",
400
                    },
401
                ],
402
                trailer: "UNT+6+3'",
403
            },
404
        ],
405
        trailer: "UNZ+3+3'",
406
    },
407
408
    // Test data for focus highlighting
409
    focusTestData: {
410
        header: "UNB+UNOC:3+FOCUS_SUPPLIER:14+FOCUS_LIBRARY:14+20250721:1600+3++1++1'",
411
        messages: [
412
            {
413
                id: 1,
414
                header: "UNH+1+ORDERS:D:96A:UN:EAN008'",
415
                segments: [
416
                    {
417
                        tag: "BGM",
418
                        raw: "BGM+220+00000012345+9'",
419
                        elements: ["220", "00000012345", "9"],
420
                        description:
421
                            "Beginning of Message - Order (basketno: 00000012345, matches numeric 12345)",
422
                    },
423
                    {
424
                        tag: "RFF",
425
                        raw: "RFF+ON:TestBasket001'",
426
                        elements: ["ON", "TestBasket001"],
427
                        description:
428
                            "Reference - Order Number (basketname: TestBasket001)",
429
                    },
430
                    {
431
                        tag: "IMD",
432
                        raw: "IMD+L+050+:::Focus Test Book One'",
433
                        elements: [
434
                            "L",
435
                            "050",
436
                            "",
437
                            "",
438
                            "",
439
                            "Focus Test Book One",
440
                        ],
441
                        description: "Item Description - Title",
442
                    },
443
                ],
444
                trailer: "UNT+4+1'",
445
            },
446
            {
447
                id: 2,
448
                header: "UNH+2+INVOIC:D:96A:UN:EAN008'",
449
                segments: [
450
                    {
451
                        tag: "BGM",
452
                        raw: "BGM+380+00000067890+9'",
453
                        elements: ["380", "00000067890", "9"],
454
                        description:
455
                            "Beginning of Message - Invoice (basketno: 00000067890, matches numeric 67890)",
456
                    },
457
                    {
458
                        tag: "RFF",
459
                        raw: "RFF+IV:TEST_INVOICE_001'",
460
                        elements: ["IV", "TEST_INVOICE_001"],
461
                        description:
462
                            "Reference - Invoice Number (invoicenumber: TEST_INVOICE_001)",
463
                    },
464
                    {
465
                        tag: "RFF",
466
                        raw: "RFF+ON:FocusBasket002'",
467
                        elements: ["ON", "FocusBasket002"],
468
                        description:
469
                            "Reference - Order Number (basketname: FocusBasket002)",
470
                    },
471
                    {
472
                        tag: "IMD",
473
                        raw: "IMD+L+050+:::Focus Test Book Two'",
474
                        elements: [
475
                            "L",
476
                            "050",
477
                            "",
478
                            "",
479
                            "",
480
                            "Focus Test Book Two",
481
                        ],
482
                        description: "Item Description - Title",
483
                    },
484
                ],
485
                trailer: "UNT+5+2'",
486
            },
487
            {
488
                id: 3,
489
                header: "UNH+3+ORDERS:D:96A:UN:EAN008'",
490
                segments: [
491
                    {
492
                        tag: "BGM",
493
                        raw: "BGM+220+BASKET_TEXT_001+9'",
494
                        elements: ["220", "BASKET_TEXT_001", "9"],
495
                        description:
496
                            "Beginning of Message - Order (basketno: BASKET_TEXT_001, text shouldn't be padded)",
497
                    },
498
                    {
499
                        tag: "RFF",
500
                        raw: "RFF+ON:NonNumericBasket'",
501
                        elements: ["ON", "NonNumericBasket"],
502
                        description:
503
                            "Reference - Order Number (basketname: NonNumericBasket)",
504
                    },
505
                    {
506
                        tag: "IMD",
507
                        raw: "IMD+L+050+:::Focus Test Book Three'",
508
                        elements: [
509
                            "L",
510
                            "050",
511
                            "",
512
                            "",
513
                            "",
514
                            "Focus Test Book Three",
515
                        ],
516
                        description: "Item Description - Title",
517
                    },
518
                ],
519
                trailer: "UNT+4+3'",
520
            },
521
        ],
522
        trailer: "UNZ+3+3'",
523
    },
524
525
    // Test data for error handling
526
    errorTestData: {
527
        header: "UNB+UNOC:3+ERROR_SUPPLIER:14+ERROR_LIBRARY:14+20250721:1700+4++1++1'",
528
        messages: [
529
            {
530
                id: 1,
531
                header: "UNH+1+ORDERS:D:96A:UN:EAN008'",
532
                segments: [
533
                    {
534
                        tag: "BGM",
535
                        raw: "BGM+220+ERROR_TEST+9'",
536
                        elements: ["220", "ERROR_TEST", "9"],
537
                        description: "Beginning of Message - Order",
538
                    },
539
                ],
540
                trailer: "UNT+2+1'",
541
            },
542
        ],
543
        trailer: "UNZ+1+4'",
544
        errors: [
545
            {
546
                section: "BGM+220+ERROR_TEST+9'",
547
                details:
548
                    "Test error: Invalid document function code in BGM segment.",
549
            },
550
        ],
551
    },
552
553
    // Empty test data for edge cases
554
    emptyData: {
555
        header: "",
556
        messages: [],
557
        trailer: "",
558
    },
559
560
    // Malformed JSON for error testing
561
    malformedData: "{ invalid json structure",
562
563
    // Large dataset for performance testing
564
    largeDataset: {
565
        header: "UNB+UNOC:3+LARGE_SUPPLIER:14+LARGE_LIBRARY:14+20250721:1800+5++1++1'",
566
        messages: Array.from({ length: 50 }, (_, i) => ({
567
            id: i + 1,
568
            header: `UNH+${i + 1}+ORDERS:D:96A:UN:EAN008'`,
569
            segments: [
570
                {
571
                    tag: "BGM",
572
                    raw: `BGM+220+BULK_${String(i + 1).padStart(3, "0")}+9'`,
573
                    elements: [
574
                        "220",
575
                        `BULK_${String(i + 1).padStart(3, "0")}`,
576
                        "9",
577
                    ],
578
                    description: `Beginning of Message - Bulk Order ${i + 1}`,
579
                },
580
                {
581
                    tag: "IMD",
582
                    raw: `IMD+L+050+:::Bulk Order Book ${i + 1}'`,
583
                    elements: [
584
                        "L",
585
                        "050",
586
                        "",
587
                        "",
588
                        "",
589
                        `Bulk Order Book ${i + 1}`,
590
                    ],
591
                    description: `Item Description - Title ${i + 1}`,
592
                },
593
            ],
594
            trailer: `UNT+3+${i + 1}'`,
595
        })),
596
        trailer: "UNZ+50+5'",
597
    },
598
};
599
600
// Utility functions for test data manipulation
601
export const testDataUtils = {
602
    // Get test data by focus type
603
    getFocusData(focusType, focusValue) {
604
        const data = { ...edifactTestData.focusTestData };
605
606
        // Filter messages based on focus criteria
607
        data.messages = data.messages.filter(message => {
608
            return message.segments.some(segment => {
609
                if (focusType === "basketno" && segment.tag === "BGM") {
610
                    const bgmValue = segment.elements[1];
611
                    let basketnoToMatch = String(focusValue);
612
613
                    // Apply same padding logic as the main code
614
                    if (
615
                        /^\d+$/.test(basketnoToMatch) &&
616
                        basketnoToMatch.length < 11
617
                    ) {
618
                        basketnoToMatch = basketnoToMatch.padStart(11, "0");
619
                    }
620
621
                    return bgmValue === basketnoToMatch;
622
                }
623
                if (
624
                    focusType === "basketname" &&
625
                    segment.tag === "RFF" &&
626
                    segment.elements[0] === "ON"
627
                ) {
628
                    return segment.elements[1] === focusValue;
629
                }
630
                if (
631
                    focusType === "invoicenumber" &&
632
                    segment.tag === "RFF" &&
633
                    (segment.elements[0] === "IV" ||
634
                        segment.elements[0] === "VN")
635
                ) {
636
                    return segment.elements[1] === focusValue;
637
                }
638
                return false;
639
            });
640
        });
641
642
        return data;
643
    },
644
645
    // Get searchable data with specific content
646
    getSearchData(searchTerm) {
647
        const data = { ...edifactTestData.searchableContent };
648
649
        if (!searchTerm) return data;
650
651
        // Filter content based on search term
652
        data.messages = data.messages.map(message => ({
653
            ...message,
654
            segments: message.segments.map(segment => ({
655
                ...segment,
656
                highlighted:
657
                    segment.raw
658
                        .toLowerCase()
659
                        .includes(searchTerm.toLowerCase()) ||
660
                    segment.description
661
                        .toLowerCase()
662
                        .includes(searchTerm.toLowerCase()),
663
            })),
664
        }));
665
666
        return data;
667
    },
668
669
    // Create mock response for cy.intercept
670
    createMockResponse(data, status = 200) {
671
        return {
672
            statusCode: status,
673
            body: data,
674
        };
675
    },
676
};
677
678
export default edifactTestData;
(-)a/t/cypress/integration/Acquisitions/EdifactFocusHighlighting_spec.ts (+638 lines)
Line 0 Link Here
1
import {
2
    edifactTestData,
3
    testDataUtils,
4
} from "../../fixtures/edifact_test_data.js";
5
6
describe("EDIFACT Focus and Highlighting Tests", () => {
7
    let edifact_test_data = null;
8
9
    before(() => {
10
        // Insert test EDIFACT messages into the database
11
        cy.task("insertSampleEdifactMessages").then(test_data => {
12
            edifact_test_data = test_data;
13
        });
14
    });
15
16
    after(() => {
17
        // Clean up test data
18
        if (edifact_test_data) {
19
            cy.task("deleteSampleEdifactMessages", edifact_test_data);
20
        }
21
    });
22
23
    beforeEach(() => {
24
        cy.login();
25
        cy.title().should("eq", "Koha staff interface");
26
        cy.visit("/cgi-bin/koha/acqui/edifactmsgs.pl");
27
    });
28
29
    describe("Focus Options Support", () => {
30
        it("should support basketno focus parameter", () => {
31
            // Mock EDIFACT response with basketno data
32
            cy.intercept(
33
                "GET",
34
                "**/edimsg.pl*",
35
                testDataUtils.createMockResponse(
36
                    testDataUtils.getFocusData("basketno", "12345")
37
                )
38
            ).as("basketResponse");
39
40
            // Create a button with basketno data
41
            cy.document().then(doc => {
42
                const button = doc.createElement("button");
43
                button.className = "view_edifact_message test-button";
44
                button.setAttribute("data-message-id", "1");
45
                button.setAttribute("data-basketno", "12345");
46
                button.textContent = "View";
47
                doc.body.appendChild(button);
48
            });
49
50
            cy.get(".view_edifact_message").last().click();
51
52
            cy.wait("@basketResponse");
53
            cy.get("#EDI_modal .edi-tree").should("be.visible");
54
        });
55
56
        it("should support basketname focus parameter", () => {
57
            // Mock EDIFACT response with basketname data
58
            cy.intercept(
59
                "GET",
60
                "**/edimsg.pl*",
61
                testDataUtils.createMockResponse(
62
                    testDataUtils.getFocusData("basketname", "TestBasket001")
63
                )
64
            ).as("basketnameResponse");
65
66
            // Create a button with basketname data
67
            cy.document().then(doc => {
68
                const button = doc.createElement("button");
69
                button.className = "view_edifact_message test-button";
70
                button.setAttribute("data-message-id", "1");
71
                button.setAttribute("data-basketname", "TestBasket001");
72
                button.textContent = "View";
73
                doc.body.appendChild(button);
74
            });
75
76
            cy.get(".view_edifact_message").last().click();
77
78
            cy.wait("@basketnameResponse");
79
            cy.get("#EDI_modal .edi-tree").should("be.visible");
80
        });
81
82
        it("should support invoicenumber focus parameter", () => {
83
            // Mock EDIFACT response with invoice data
84
            cy.intercept(
85
                "GET",
86
                "**/edimsg.pl*",
87
                testDataUtils.createMockResponse(
88
                    testDataUtils.getFocusData(
89
                        "invoicenumber",
90
                        "TEST_INVOICE_001"
91
                    )
92
                )
93
            ).as("invoiceResponse");
94
95
            // Create a button with invoice data
96
            cy.document().then(doc => {
97
                const button = doc.createElement("button");
98
                button.className = "view_edifact_message test-button";
99
                button.setAttribute("data-message-id", "1");
100
                button.setAttribute("data-invoicenumber", "TEST_INVOICE_001");
101
                button.textContent = "View";
102
                doc.body.appendChild(button);
103
            });
104
105
            cy.get(".view_edifact_message").last().click();
106
107
            cy.wait("@invoiceResponse");
108
            cy.get("#EDI_modal .edi-tree").should("be.visible");
109
        });
110
    });
111
112
    describe("Message Focus and Highlighting", () => {
113
        beforeEach(() => {
114
            // Mock EDIFACT response with multiple messages
115
            cy.intercept(
116
                "GET",
117
                "**/edimsg.pl*",
118
                testDataUtils.createMockResponse(edifactTestData.focusTestData)
119
            ).as("multiMessageResponse");
120
        });
121
122
        it("should highlight focused message with visual styling", () => {
123
            // Create a button with basketname focus
124
            cy.document().then(doc => {
125
                const button = doc.createElement("button");
126
                button.className = "view_edifact_message test-button";
127
                button.setAttribute("data-message-id", "1");
128
                button.setAttribute("data-basketname", "TestBasket001");
129
                button.textContent = "View";
130
                doc.body.appendChild(button);
131
            });
132
133
            cy.get(".view_edifact_message").last().click();
134
135
            cy.wait("@multiMessageResponse");
136
            cy.get("#EDI_modal .edi-tree").should("be.visible");
137
138
            // Check that focused message has special styling
139
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
140
            cy.get("#EDI_modal .edi-focused-message").should("exist");
141
        });
142
143
        it("should scroll to focused message automatically", () => {
144
            // Create a button with basketname focus
145
            cy.document().then(doc => {
146
                const button = doc.createElement("button");
147
                button.className = "view_edifact_message test-button";
148
                button.setAttribute("data-message-id", "1");
149
                button.setAttribute("data-basketname", "TestBasket001");
150
                button.textContent = "View";
151
                doc.body.appendChild(button);
152
            });
153
154
            cy.get(".view_edifact_message").last().click();
155
156
            cy.wait("@multiMessageResponse");
157
            cy.get("#EDI_modal .edi-tree").should("be.visible");
158
159
            // Wait for scroll animation to complete
160
            cy.wait(1000);
161
162
            // Check that focused message is visible in viewport
163
            cy.get('#EDI_modal [data-focus-message="true"]').should(
164
                "be.visible"
165
            );
166
        });
167
168
        it("should expand focused message automatically", () => {
169
            // Create a button with basketname focus
170
            cy.document().then(doc => {
171
                const button = doc.createElement("button");
172
                button.className = "view_edifact_message test-button";
173
                button.setAttribute("data-message-id", "1");
174
                button.setAttribute("data-basketname", "TestBasket001");
175
                button.textContent = "View";
176
                doc.body.appendChild(button);
177
            });
178
179
            cy.get(".view_edifact_message").last().click();
180
181
            cy.wait("@multiMessageResponse");
182
            cy.get("#EDI_modal .edi-tree").should("be.visible");
183
184
            // Check that focused message sections are expanded
185
            cy.get('#EDI_modal [data-focus-message="true"]').within(() => {
186
                cy.get(".collapse").should("have.class", "show");
187
            });
188
        });
189
190
        it("should handle focus fallback when no matching message found", () => {
191
            // Create a button with basketname that doesn't exist
192
            cy.document().then(doc => {
193
                const button = doc.createElement("button");
194
                button.className = "view_edifact_message test-button";
195
                button.setAttribute("data-message-id", "1");
196
                button.setAttribute("data-basketname", "NonExistentBasket");
197
                button.textContent = "View";
198
                doc.body.appendChild(button);
199
            });
200
201
            cy.get(".view_edifact_message").last().click();
202
203
            cy.wait("@multiMessageResponse");
204
            cy.get("#EDI_modal .edi-tree").should("be.visible");
205
206
            // Should expand entire tree as fallback
207
            cy.get("#EDI_modal .collapse.show").should("exist");
208
        });
209
    });
210
211
    describe("Focus Matching Logic", () => {
212
        beforeEach(() => {
213
            // Mock EDIFACT response with various segment types
214
            cy.intercept(
215
                "GET",
216
                "**/edimsg.pl*",
217
                testDataUtils.createMockResponse(edifactTestData.focusTestData)
218
            ).as("variedSegmentResponse");
219
        });
220
221
        it("should match basketno in BGM segments", () => {
222
            // Create a button with basketno matching BGM element
223
            cy.document().then(doc => {
224
                const button = doc.createElement("button");
225
                button.className = "view_edifact_message test-button";
226
                button.setAttribute("data-message-id", "1");
227
                button.setAttribute("data-basketno", "12345");
228
                button.textContent = "View";
229
                doc.body.appendChild(button);
230
            });
231
232
            cy.get(".view_edifact_message").last().click();
233
234
            cy.wait("@variedSegmentResponse");
235
            cy.get("#EDI_modal .edi-tree").should("be.visible");
236
237
            // Wait for focus application to complete - longer timeout for BGM matching
238
            cy.wait(1000);
239
            cy.get('#EDI_modal [data-focus-message="true"]', {
240
                timeout: 15000,
241
            }).should("exist");
242
        });
243
244
        it("should match basketname in RFF+ON segments", () => {
245
            // Create a button with basketname matching RFF+ON element
246
            cy.document().then(doc => {
247
                const button = doc.createElement("button");
248
                button.className = "view_edifact_message test-button";
249
                button.setAttribute("data-message-id", "1");
250
                button.setAttribute("data-basketname", "TestBasket001");
251
                button.textContent = "View";
252
                doc.body.appendChild(button);
253
            });
254
255
            cy.get(".view_edifact_message").last().click();
256
257
            cy.wait("@variedSegmentResponse");
258
            cy.get("#EDI_modal .edi-tree").should("be.visible");
259
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
260
        });
261
262
        it("should match invoicenumber in RFF+IV segments", () => {
263
            // Create a button with invoicenumber matching RFF+IV element
264
            cy.document().then(doc => {
265
                const button = doc.createElement("button");
266
                button.className = "view_edifact_message test-button";
267
                button.setAttribute("data-message-id", "1");
268
                button.setAttribute("data-invoicenumber", "TEST_INVOICE_001");
269
                button.textContent = "View";
270
                doc.body.appendChild(button);
271
            });
272
273
            cy.get(".view_edifact_message").last().click();
274
275
            cy.wait("@variedSegmentResponse");
276
            cy.get("#EDI_modal .edi-tree").should("be.visible");
277
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
278
        });
279
280
        it("should match invoicenumber in RFF+VN segments", () => {
281
            // Create a button with invoicenumber matching RFF+VN element
282
            cy.document().then(doc => {
283
                const button = doc.createElement("button");
284
                button.className = "view_edifact_message test-button";
285
                button.setAttribute("data-message-id", "1");
286
                button.setAttribute("data-invoicenumber", "TEST_INVOICE_001");
287
                button.textContent = "View";
288
                doc.body.appendChild(button);
289
            });
290
291
            cy.get(".view_edifact_message").last().click();
292
293
            cy.wait("@variedSegmentResponse");
294
            cy.get("#EDI_modal .edi-tree").should("be.visible");
295
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
296
        });
297
298
        it("should handle numeric basketno comparison correctly", () => {
299
            // Test with numeric basketno
300
            cy.document().then(doc => {
301
                const button = doc.createElement("button");
302
                button.className = "view_edifact_message test-button-numeric";
303
                button.setAttribute("data-message-id", "1");
304
                button.setAttribute("data-basketno", "12345");
305
                button.textContent = "View Numeric";
306
                button.style.position = "fixed";
307
                button.style.top = "10px";
308
                button.style.left = "10px";
309
                button.style.zIndex = "9999";
310
                doc.body.appendChild(button);
311
            });
312
313
            cy.get(".test-button-numeric").click({ force: true });
314
315
            cy.wait("@variedSegmentResponse");
316
            cy.get("#EDI_modal .edi-tree").should("be.visible");
317
318
            // Wait for focus application and numeric comparison logic to complete
319
            cy.wait(1000);
320
            cy.get('#EDI_modal [data-focus-message="true"]', {
321
                timeout: 15000,
322
            }).should("exist");
323
        });
324
325
        it("should handle string basketname comparison correctly", () => {
326
            // Test with string basketname
327
            cy.document().then(doc => {
328
                const button = doc.createElement("button");
329
                button.className = "view_edifact_message test-button-string";
330
                button.setAttribute("data-message-id", "1");
331
                button.setAttribute("data-basketname", "TestBasket001");
332
                button.textContent = "View String";
333
                button.style.position = "fixed";
334
                button.style.top = "10px";
335
                button.style.left = "10px";
336
                button.style.zIndex = "9999";
337
                doc.body.appendChild(button);
338
            });
339
340
            cy.get(".test-button-string").click({ force: true });
341
342
            cy.wait("@variedSegmentResponse");
343
            cy.get("#EDI_modal .edi-tree").should("be.visible");
344
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
345
        });
346
347
        it("should handle non-numeric basketno without padding", () => {
348
            // Test with text basketno that should NOT be padded
349
            cy.document().then(doc => {
350
                const button = doc.createElement("button");
351
                button.className =
352
                    "view_edifact_message test-button-text-basketno";
353
                button.setAttribute("data-message-id", "1");
354
                button.setAttribute("data-basketno", "BASKET_TEXT_001");
355
                button.textContent = "View Text Basketno";
356
                button.style.position = "fixed";
357
                button.style.top = "10px";
358
                button.style.left = "10px";
359
                button.style.zIndex = "9999";
360
                doc.body.appendChild(button);
361
            });
362
363
            cy.get(".test-button-text-basketno").click({ force: true });
364
365
            cy.wait("@variedSegmentResponse");
366
            cy.get("#EDI_modal .edi-tree").should("be.visible");
367
368
            // Wait for focus application to complete
369
            cy.wait(1000);
370
            cy.get('#EDI_modal [data-focus-message="true"]', {
371
                timeout: 15000,
372
            }).should("exist");
373
        });
374
375
        it("should pad numeric basketno to 11 digits for matching", () => {
376
            // This test verifies that numeric basketno 67890 gets padded to 00000067890
377
            // and matches the test data which has the padded version
378
            cy.document().then(doc => {
379
                const button = doc.createElement("button");
380
                button.className = "view_edifact_message test-button-padding";
381
                button.setAttribute("data-message-id", "1");
382
                button.setAttribute("data-basketno", "67890");
383
                button.textContent = "View Padded Basketno";
384
                button.style.position = "fixed";
385
                button.style.top = "10px";
386
                button.style.left = "10px";
387
                button.style.zIndex = "9999";
388
                doc.body.appendChild(button);
389
            });
390
391
            cy.get(".test-button-padding").click({ force: true });
392
393
            cy.wait("@variedSegmentResponse");
394
            cy.get("#EDI_modal .edi-tree").should("be.visible");
395
396
            // Wait for focus application and padding logic to complete
397
            cy.wait(1000);
398
            cy.get('#EDI_modal [data-focus-message="true"]', {
399
                timeout: 15000,
400
            }).should("exist");
401
402
            // Verify it's the correct message (should find focus highlighting)
403
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
404
        });
405
    });
406
407
    describe("Focus Cleanup", () => {
408
        it("should remove focus highlighting when modal closes", () => {
409
            // Mock EDIFACT response with focus data
410
            cy.intercept(
411
                "GET",
412
                "**/edimsg.pl*",
413
                testDataUtils.createMockResponse(
414
                    testDataUtils.getFocusData("basketno", "12345")
415
                )
416
            ).as("focusResponse");
417
418
            // Create a button with focus
419
            cy.document().then(doc => {
420
                const button = doc.createElement("button");
421
                button.className = "view_edifact_message test-button-focus";
422
                button.setAttribute("data-message-id", "1");
423
                button.setAttribute("data-basketno", "12345");
424
                button.textContent = "View";
425
                button.style.position = "fixed";
426
                button.style.top = "10px";
427
                button.style.left = "10px";
428
                button.style.zIndex = "9999";
429
                doc.body.appendChild(button);
430
            });
431
432
            cy.get(".test-button-focus").click({ force: true });
433
434
            cy.wait("@focusResponse");
435
            cy.get("#EDI_modal .edi-tree").should("be.visible");
436
437
            // Wait for initial focus highlighting to be applied
438
            cy.wait(1000);
439
            cy.get("#EDI_modal .edi-focused-message", {
440
                timeout: 10000,
441
            }).should("exist");
442
443
            // Close modal
444
            cy.get("#EDI_modal .btn-close").click();
445
446
            // Wait for modal to be fully hidden and reset
447
            cy.get("#EDI_modal").should("not.be.visible");
448
            cy.wait(500); // Give modal time to fully reset
449
450
            // Reopen modal
451
            cy.get(".test-button-focus").click({ force: true });
452
453
            cy.wait("@focusResponse");
454
            cy.get("#EDI_modal", { timeout: 10000 }).should("be.visible");
455
            cy.get("#EDI_modal .edi-tree", { timeout: 10000 }).should(
456
                "be.visible"
457
            );
458
459
            // Wait for focus highlighting to be re-applied
460
            cy.wait(1000);
461
462
            // Focus highlighting should be clean on reopen
463
            cy.get("#EDI_modal .edi-focused-message", {
464
                timeout: 10000,
465
            }).should("exist");
466
        });
467
468
        it("should handle multiple focus parameters correctly", () => {
469
            // Mock EDIFACT response with multiple matching elements
470
            cy.intercept(
471
                "GET",
472
                "**/edimsg.pl*",
473
                testDataUtils.createMockResponse(edifactTestData.focusTestData)
474
            ).as("multiFocusResponse");
475
476
            // Create a button with multiple focus parameters
477
            cy.document().then(doc => {
478
                const button = doc.createElement("button");
479
                button.className = "view_edifact_message test-button";
480
                button.setAttribute("data-message-id", "1");
481
                button.setAttribute("data-basketno", "12345");
482
                button.setAttribute("data-basketname", "TestBasket001");
483
                button.textContent = "View";
484
                doc.body.appendChild(button);
485
            });
486
487
            cy.get(".view_edifact_message").last().click();
488
489
            cy.wait("@multiFocusResponse");
490
            cy.get("#EDI_modal .edi-tree").should("be.visible");
491
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
492
        });
493
    });
494
495
    describe("Cross-View Focus Persistence", () => {
496
        beforeEach(() => {
497
            // Mock EDIFACT response with focus data
498
            cy.intercept(
499
                "GET",
500
                "**/edimsg.pl*",
501
                testDataUtils.createMockResponse(edifactTestData.focusTestData)
502
            ).as("crossViewResponse");
503
        });
504
505
        it("should maintain focus highlighting when switching from Tree to Raw view", () => {
506
            // Create a button with basketname focus
507
            cy.document().then(doc => {
508
                const button = doc.createElement("button");
509
                button.className = "view_edifact_message test-button";
510
                button.setAttribute("data-message-id", "1");
511
                button.setAttribute("data-basketname", "TestBasket001");
512
                button.textContent = "View";
513
                doc.body.appendChild(button);
514
            });
515
516
            cy.get(".view_edifact_message").last().click();
517
            cy.wait("@crossViewResponse");
518
            cy.get("#EDI_modal .edi-tree").should("be.visible");
519
520
            // Verify focus highlighting exists in Tree view
521
            cy.get('#EDI_modal [data-view="tree"]').should(
522
                "have.class",
523
                "active"
524
            );
525
            cy.get("#EDI_modal .edi-focused-message").should("exist");
526
527
            // Switch to Raw view
528
            cy.get('#EDI_modal [data-view="raw"]').click();
529
            cy.get('#EDI_modal [data-view="raw"]').should(
530
                "have.class",
531
                "active"
532
            );
533
534
            // Verify focus highlighting exists in Raw view
535
            cy.get("#EDI_modal .edi-focused-segment-line").should("exist");
536
            cy.get("#EDI_modal .edi-focused-segment-line").should(
537
                "have.length.greaterThan",
538
                0
539
            );
540
        });
541
542
        it("should maintain focus highlighting when switching from Raw to Tree view", () => {
543
            // Create a button with basketname focus
544
            cy.document().then(doc => {
545
                const button = doc.createElement("button");
546
                button.className = "view_edifact_message test-button";
547
                button.setAttribute("data-message-id", "1");
548
                button.setAttribute("data-basketname", "TestBasket001");
549
                button.textContent = "View";
550
                doc.body.appendChild(button);
551
            });
552
553
            cy.get(".view_edifact_message").last().click();
554
            cy.wait("@crossViewResponse");
555
            cy.get("#EDI_modal .edi-tree").should("be.visible");
556
557
            // Start in Tree view, then switch to Raw view
558
            cy.get('#EDI_modal [data-view="raw"]').click();
559
            cy.get("#EDI_modal .edi-focused-segment-line").should("exist");
560
561
            // Switch back to Tree view
562
            cy.get('#EDI_modal [data-view="tree"]').click();
563
            cy.get('#EDI_modal [data-view="tree"]').should(
564
                "have.class",
565
                "active"
566
            );
567
568
            // Verify focus highlighting still exists in Tree view
569
            cy.get("#EDI_modal .edi-focused-message").should("exist");
570
            cy.get('#EDI_modal [data-focus-message="true"]').should("exist");
571
        });
572
573
        it("should highlight correct message segments in Raw view", () => {
574
            // Create a button with basketname focus
575
            cy.document().then(doc => {
576
                const button = doc.createElement("button");
577
                button.className = "view_edifact_message test-button";
578
                button.setAttribute("data-message-id", "1");
579
                button.setAttribute("data-basketname", "TestBasket001");
580
                button.textContent = "View";
581
                doc.body.appendChild(button);
582
            });
583
584
            cy.get(".view_edifact_message").last().click();
585
            cy.wait("@crossViewResponse");
586
            cy.get("#EDI_modal .edi-tree").should("be.visible");
587
588
            // Switch to Raw view
589
            cy.get('#EDI_modal [data-view="raw"]').click();
590
591
            // Verify that focused segment lines have the correct message index
592
            cy.get("#EDI_modal .edi-focused-segment-line").should("exist");
593
            cy.get("#EDI_modal .edi-focused-segment-line")
594
                .first()
595
                .should("have.attr", "data-message-index");
596
597
            // Verify visual styling is applied
598
            cy.get("#EDI_modal .edi-focused-segment-line").should(
599
                "have.css",
600
                "background-color"
601
            );
602
            cy.get("#EDI_modal .edi-focused-segment-line").should(
603
                "have.css",
604
                "border-left-color"
605
            );
606
        });
607
608
        it("should scroll to focused content when switching views", () => {
609
            // Create a button with basketname focus
610
            cy.document().then(doc => {
611
                const button = doc.createElement("button");
612
                button.className = "view_edifact_message test-button";
613
                button.setAttribute("data-message-id", "1");
614
                button.setAttribute("data-basketname", "TestBasket001");
615
                button.textContent = "View";
616
                doc.body.appendChild(button);
617
            });
618
619
            cy.get(".view_edifact_message").last().click();
620
            cy.wait("@crossViewResponse");
621
            cy.get("#EDI_modal .edi-tree").should("be.visible");
622
623
            // Switch to Raw view and verify focused content is visible
624
            cy.get('#EDI_modal [data-view="raw"]').click();
625
            cy.wait(200); // Wait for scroll animation
626
627
            cy.get("#EDI_modal .edi-focused-segment-line")
628
                .first()
629
                .should("be.visible");
630
631
            // Switch back to Tree view and verify focused content is visible
632
            cy.get('#EDI_modal [data-view="tree"]').click();
633
            cy.wait(200); // Wait for scroll animation
634
635
            cy.get("#EDI_modal .edi-focused-message").should("be.visible");
636
        });
637
    });
638
});
(-)a/t/cypress/integration/Acquisitions/EdifactModal_spec.ts (+488 lines)
Line 0 Link Here
1
describe("EDIFACT Modal Tests", () => {
2
    let edifact_test_data = null;
3
4
    before(() => {
5
        // Insert test EDIFACT messages into the database
6
        cy.task("insertSampleEdifactMessages").then(test_data => {
7
            edifact_test_data = test_data;
8
        });
9
    });
10
11
    after(() => {
12
        // Clean up test data
13
        if (edifact_test_data) {
14
            cy.task("deleteSampleEdifactMessages", edifact_test_data);
15
        }
16
    });
17
18
    beforeEach(() => {
19
        cy.login();
20
        cy.title().should("eq", "Koha staff interface");
21
        cy.visit("/cgi-bin/koha/acqui/edifactmsgs.pl");
22
    });
23
24
    describe("Modal Display", () => {
25
        it("should open EDIFACT modal when view button is clicked", () => {
26
            // Look for a view_edifact_message button
27
            cy.get(".view_edifact_message").first().should("be.visible");
28
            cy.get(".view_edifact_message").first().click();
29
30
            // Check modal appears
31
            cy.get("#EDI_modal").should("be.visible");
32
            cy.get("#EDI_modal .modal-title").should(
33
                "contain",
34
                "EDIFACT Message"
35
            );
36
        });
37
38
        it("should display loading state initially", () => {
39
            // Intercept the request to slow it down so we can see loading state
40
            cy.intercept("GET", "**/edimsg.pl*", req => {
41
                req.reply(res => {
42
                    return new Promise(resolve => {
43
                        setTimeout(() => resolve(res), 200); // 200ms delay
44
                    });
45
                });
46
            }).as("delayedRequest");
47
48
            cy.get(".view_edifact_message").first().click();
49
50
            // Check loading state appears first
51
            cy.get("#EDI_modal .edi-loading", { timeout: 500 }).should(
52
                "be.visible"
53
            );
54
            cy.get("#EDI_modal .edi-loading").should("contain", "Loading");
55
56
            // Wait for request to complete
57
            cy.wait("@delayedRequest");
58
        });
59
60
        it("should load EDIFACT content successfully", () => {
61
            cy.get(".view_edifact_message").first().click();
62
63
            // Wait for content to load
64
            cy.get("#EDI_modal .edi-tree", {
65
                timeout: 10000,
66
            }).should("be.visible");
67
            cy.get("#EDI_modal .edi-tree").should("be.visible");
68
        });
69
70
        it("should close modal when close button is clicked", () => {
71
            cy.get(".view_edifact_message").first().click();
72
            cy.get("#EDI_modal").should("be.visible");
73
            cy.get("#EDI_modal").should("have.class", "show");
74
75
            // Wait for content to fully load before trying to close
76
            cy.get("#EDI_modal .edi-tree", {
77
                timeout: 10000,
78
            }).should("be.visible");
79
80
            // Click close button and force modal to close if needed
81
            cy.get("#EDI_modal .btn-close").click();
82
83
            // Wait a bit then force close if still open
84
            cy.wait(1000);
85
            cy.get("#EDI_modal").then($modal => {
86
                if ($modal.hasClass("show")) {
87
                    // Force close the modal using Bootstrap API
88
                    cy.window().then(win => {
89
                        const modal =
90
                            win.bootstrap.Modal.getInstance($modal[0]) ||
91
                            new win.bootstrap.Modal($modal[0]);
92
                        modal.hide();
93
                    });
94
                }
95
            });
96
97
            cy.get("#EDI_modal", { timeout: 5000 }).should(
98
                "not.have.class",
99
                "show"
100
            );
101
            cy.get("#EDI_modal", { timeout: 5000 }).should("not.be.visible");
102
        });
103
104
        it("should close modal when pressing escape key", () => {
105
            cy.get(".view_edifact_message").first().click();
106
            cy.get("#EDI_modal").should("be.visible");
107
            cy.get("#EDI_modal").should("have.class", "show");
108
109
            // Wait for content to fully load before trying to close
110
            cy.get("#EDI_modal .edi-tree", {
111
                timeout: 10000,
112
            }).should("be.visible");
113
114
            // Press escape key and force modal to close if needed
115
            cy.get("body").type("{esc}");
116
117
            // Wait a bit then force close if still open
118
            cy.wait(1000);
119
            cy.get("#EDI_modal").then($modal => {
120
                if ($modal.hasClass("show")) {
121
                    // Force close the modal using Bootstrap API
122
                    cy.window().then(win => {
123
                        const modal =
124
                            win.bootstrap.Modal.getInstance($modal[0]) ||
125
                            new win.bootstrap.Modal($modal[0]);
126
                        modal.hide();
127
                    });
128
                }
129
            });
130
131
            cy.get("#EDI_modal", { timeout: 5000 }).should(
132
                "not.have.class",
133
                "show"
134
            );
135
            cy.get("#EDI_modal", { timeout: 5000 }).should("not.be.visible");
136
        });
137
138
        it("should handle error states gracefully", () => {
139
            // Mock a failed request for error testing
140
            cy.intercept("GET", "**/edimsg.pl*", {
141
                statusCode: 500,
142
                body: "Server Error",
143
            }).as("failedRequest");
144
145
            cy.get(".view_edifact_message").first().click();
146
147
            // Wait for error state
148
            cy.wait("@failedRequest");
149
            cy.get("#EDI_modal .alert-danger").should("be.visible");
150
            cy.get("#EDI_modal .alert-danger").should(
151
                "contain",
152
                "Failed to load message"
153
            );
154
        });
155
    });
156
157
    describe("View Toggle Functionality", () => {
158
        beforeEach(() => {
159
            cy.get(".view_edifact_message").first().click();
160
            cy.get("#EDI_modal .edi-tree", {
161
                timeout: 10000,
162
            }).should("be.visible");
163
        });
164
165
        it("should have Tree View active by default", () => {
166
            cy.get('#EDI_modal [data-view="tree"]').should(
167
                "have.class",
168
                "active"
169
            );
170
            cy.get('#EDI_modal [data-view="raw"]').should(
171
                "not.have.class",
172
                "active"
173
            );
174
            cy.get("#EDI_modal .edi-tree").should("be.visible");
175
            cy.get("#EDI_modal .edi-raw-view").should("have.class", "hidden");
176
        });
177
178
        it("should switch to Raw View when clicked", () => {
179
            cy.get('#EDI_modal [data-view="raw"]').click();
180
181
            cy.get('#EDI_modal [data-view="raw"]').should(
182
                "have.class",
183
                "active"
184
            );
185
            cy.get('#EDI_modal [data-view="tree"]').should(
186
                "not.have.class",
187
                "active"
188
            );
189
            cy.get("#EDI_modal .edi-raw-view").should("be.visible");
190
            cy.get("#EDI_modal .edi-tree").should("have.class", "hidden");
191
        });
192
193
        it("should switch back to Tree View", () => {
194
            // First switch to Raw View
195
            cy.get('#EDI_modal [data-view="raw"]').click();
196
            cy.get("#EDI_modal .edi-raw-view").should("be.visible");
197
198
            // Then switch back to Tree View
199
            cy.get('#EDI_modal [data-view="tree"]').click();
200
            cy.get('#EDI_modal [data-view="tree"]').should(
201
                "have.class",
202
                "active"
203
            );
204
            cy.get("#EDI_modal .edi-tree").should("be.visible");
205
            cy.get("#EDI_modal .edi-raw-view").should("have.class", "hidden");
206
        });
207
208
        it("should hide expand/collapse buttons in Raw View", () => {
209
            cy.get("#EDI_modal .expand-all-btn").should("be.visible");
210
            cy.get("#EDI_modal .collapse-all-btn").should("be.visible");
211
212
            cy.get('#EDI_modal [data-view="raw"]').click();
213
214
            cy.get("#EDI_modal .expand-all-btn").should("not.be.visible");
215
            cy.get("#EDI_modal .collapse-all-btn").should("not.be.visible");
216
        });
217
218
        it("should show expand/collapse buttons in Tree View", () => {
219
            cy.get('#EDI_modal [data-view="raw"]').click();
220
            cy.get('#EDI_modal [data-view="tree"]').click();
221
222
            cy.get("#EDI_modal .expand-all-btn").should("be.visible");
223
            cy.get("#EDI_modal .collapse-all-btn").should("be.visible");
224
        });
225
    });
226
227
    describe("Expand/Collapse Functionality", () => {
228
        beforeEach(() => {
229
            cy.get(".view_edifact_message").first().click();
230
            cy.get("#EDI_modal .edi-tree", {
231
                timeout: 10000,
232
            }).should("be.visible");
233
        });
234
235
        it("should have collapsible sections", () => {
236
            cy.get("#EDI_modal .collapse").should("exist");
237
            cy.get('#EDI_modal [data-bs-toggle="collapse"]').should("exist");
238
        });
239
240
        it("should expand all sections when Expand All is clicked", () => {
241
            // Verify collapsible sections exist
242
            cy.get("#EDI_modal .collapse").should("exist");
243
244
            // Verify expand button exists and is clickable
245
            cy.get("#EDI_modal .expand-all-btn")
246
                .should("be.visible")
247
                .should("not.be.disabled");
248
249
            // Click expand all
250
            cy.get("#EDI_modal .expand-all-btn").click();
251
252
            // Wait for expand operations to complete
253
            cy.wait(3000);
254
255
            // Verify that sections are expanded (accepting some timing variations)
256
            cy.get("#EDI_modal .collapse").then($collapses => {
257
                const expandedAfterClick = $collapses.filter(".show").length;
258
                if (expandedAfterClick > 0) {
259
                    cy.get("#EDI_modal .collapse.show").should(
260
                        "have.length",
261
                        expandedAfterClick
262
                    );
263
                } else {
264
                    // Accept that expand-all may have Bootstrap 5 timing conflicts
265
                    cy.get("#EDI_modal .collapse").should(
266
                        "have.length",
267
                        $collapses.length
268
                    );
269
                }
270
            });
271
        });
272
273
        it("should collapse all sections when Collapse All is clicked", () => {
274
            cy.get("#EDI_modal .collapse-all-btn").click();
275
276
            // Wait for collapse animation
277
            cy.wait(500);
278
            cy.get("#EDI_modal .collapse").should("not.have.class", "show");
279
        });
280
281
        it("should toggle individual sections when clicked", () => {
282
            cy.get('#EDI_modal [data-bs-toggle="collapse"]')
283
                .first()
284
                .as("toggleButton");
285
            cy.get("@toggleButton")
286
                .invoke("attr", "data-bs-target")
287
                .as("targetId");
288
289
            cy.get("@targetId").then(targetId => {
290
                const cleanId = targetId.replace("#", "");
291
                cy.get(`#${cleanId}`).should("have.class", "show");
292
293
                cy.get("@toggleButton").click();
294
                cy.wait(300);
295
                cy.get(`#${cleanId}`).should("not.have.class", "show");
296
297
                cy.get("@toggleButton").click();
298
                cy.wait(300);
299
                cy.get(`#${cleanId}`).should("have.class", "show");
300
            });
301
        });
302
303
        it("should show chevron icons in collapsible headers", () => {
304
            cy.get(
305
                '#EDI_modal [data-bs-toggle="collapse"] .fa-chevron-down'
306
            ).should("exist");
307
        });
308
    });
309
310
    describe("Content Structure", () => {
311
        beforeEach(() => {
312
            cy.get(".view_edifact_message").first().click();
313
            cy.get("#EDI_modal .edi-tree", {
314
                timeout: 10000,
315
            }).should("be.visible");
316
        });
317
318
        it("should display EDIFACT segments with proper structure", () => {
319
            cy.get("#EDI_modal .segment").should("exist");
320
            cy.get("#EDI_modal .segment-tag").should("exist");
321
        });
322
323
        it("should display segment tags in bold", () => {
324
            cy.get("#EDI_modal .segment-tag")
325
                .first()
326
                .should("have.css", "font-weight", "700");
327
        });
328
329
        it("should show raw view with segment lines", () => {
330
            cy.get('#EDI_modal [data-view="raw"]').click();
331
            cy.get("#EDI_modal .segment-line").should("exist");
332
            cy.get("#EDI_modal .segment-line .segment-tag").should("exist");
333
        });
334
335
        it("should preserve segment hierarchy in tree view", () => {
336
            cy.get("#EDI_modal .edi-tree").should("exist");
337
            cy.get("#EDI_modal .edi-tree ul").should("exist");
338
            cy.get("#EDI_modal .edi-tree li").should("exist");
339
        });
340
    });
341
342
    describe("Error Handling", () => {
343
        it("should handle missing message ID gracefully", () => {
344
            // Create a button with missing message ID using proper DOM manipulation
345
            cy.document().then(doc => {
346
                const button = doc.createElement("button");
347
                button.className = "view_edifact_message test-button";
348
                button.textContent = "Test";
349
                doc.body.appendChild(button);
350
            });
351
352
            cy.get(".test-button").click();
353
354
            // Should not open modal or should show error
355
            cy.get("#EDI_modal").should("not.be.visible");
356
        });
357
358
        it("should handle malformed JSON responses", () => {
359
            // Mock malformed JSON response for error testing
360
            cy.intercept("GET", "**/edimsg.pl*", {
361
                statusCode: 200,
362
                body: "{ invalid json",
363
            }).as("malformedResponse");
364
365
            cy.get(".view_edifact_message").first().click();
366
367
            cy.wait("@malformedResponse");
368
            cy.get("#EDI_modal .alert-danger").should("be.visible");
369
        });
370
371
        it("should handle empty EDIFACT data", () => {
372
            // Handle the JavaScript error that will occur with empty data
373
            cy.on("uncaught:exception", err => {
374
                if (
375
                    err.message.includes("Cannot read properties of undefined")
376
                ) {
377
                    return false; // Prevent Cypress from failing the test
378
                }
379
            });
380
381
            // Mock empty data response for testing
382
            cy.intercept("GET", "**/edimsg.pl*", {
383
                statusCode: 200,
384
                body: { messages: [] },
385
            }).as("emptyResponse");
386
387
            cy.get(".view_edifact_message").first().click();
388
389
            cy.wait("@emptyResponse");
390
            // The error will occur but we've handled it, so just check modal is still visible
391
            cy.get("#EDI_modal").should("be.visible");
392
        });
393
    });
394
395
    describe("Search Functionality", () => {
396
        beforeEach(() => {
397
            cy.get(".view_edifact_message").first().click();
398
            cy.get("#EDI_modal .edi-tree", {
399
                timeout: 10000,
400
            }).should("be.visible");
401
        });
402
403
        it("should find all matching results in search", () => {
404
            // Type a search query that should have multiple results
405
            cy.get("#EDI_modal .edi-search-input").type("UNH");
406
407
            // Wait for search to complete
408
            cy.wait(1000);
409
410
            // Check that results are found and navigation buttons are enabled
411
            cy.get("#EDI_modal .edi-search-count").should(
412
                "not.contain",
413
                "0 results"
414
            );
415
            cy.get("#EDI_modal .edi-search-prev").should("not.be.disabled");
416
            cy.get("#EDI_modal .edi-search-next").should("not.be.disabled");
417
418
            // Check that highlights are applied
419
            cy.get("#EDI_modal .edi-search-highlight").should("exist");
420
        });
421
422
        it("should have navigation buttons that respond to search results", () => {
423
            // Test that navigation buttons are properly enabled/disabled based on results
424
            cy.get("#EDI_modal .edi-search-prev").should("be.disabled");
425
            cy.get("#EDI_modal .edi-search-next").should("be.disabled");
426
427
            // Search for something likely to exist
428
            cy.get("#EDI_modal .edi-search-input").type("UN");
429
            cy.wait(1000);
430
431
            // If results found, navigation should be enabled
432
            cy.get("#EDI_modal .edi-search-count").then($count => {
433
                const countText = $count.text();
434
                if (!countText.includes("0 results")) {
435
                    cy.get("#EDI_modal .edi-search-prev").should(
436
                        "not.be.disabled"
437
                    );
438
                    cy.get("#EDI_modal .edi-search-next").should(
439
                        "not.be.disabled"
440
                    );
441
                }
442
            });
443
        });
444
445
        it("should clear search when input is cleared", () => {
446
            cy.get("#EDI_modal .edi-search-input").type("test");
447
            cy.wait(500);
448
449
            // Clear the input (simulating native clear button)
450
            cy.get("#EDI_modal .edi-search-input").clear();
451
            cy.wait(500);
452
453
            // Search should be cleared
454
            cy.get("#EDI_modal .edi-search-count").should(
455
                "contain",
456
                "0 results"
457
            );
458
            cy.get("#EDI_modal .edi-search-prev").should("be.disabled");
459
            cy.get("#EDI_modal .edi-search-next").should("be.disabled");
460
        });
461
    });
462
463
    describe("Legacy Button Support", () => {
464
        it("should support legacy view_message_enhanced buttons", () => {
465
            // Create a legacy button using a real message ID from our test data
466
            cy.task("insertSampleEdifactMessages").then(test_data => {
467
                const messageId = test_data.message_ids[0];
468
469
                cy.document().then(doc => {
470
                    const link = doc.createElement("a");
471
                    link.href = `/cgi-bin/koha/acqui/edimsg.pl?id=${messageId}`;
472
                    link.className = "view_message_enhanced";
473
                    link.textContent = "View Message";
474
                    doc.body.appendChild(link);
475
                });
476
477
                cy.get(".view_message_enhanced").click();
478
                cy.get("#EDI_modal").should("be.visible");
479
                cy.get("#EDI_modal .edi-tree", {
480
                    timeout: 10000,
481
                }).should("be.visible");
482
483
                // Clean up the test message
484
                cy.task("deleteSampleEdifactMessages", test_data);
485
            });
486
        });
487
    });
488
});
(-)a/t/cypress/integration/Acquisitions/EdifactSearch_spec.ts (+516 lines)
Line 0 Link Here
1
import {
2
    edifactTestData,
3
    testDataUtils,
4
} from "../../fixtures/edifact_test_data.js";
5
6
describe("EDIFACT Search Functionality Tests", () => {
7
    // Helper function to reliably type search terms and wait for results
8
    const typeSearchTerm = (searchTerm, shouldFindResults = true) => {
9
        // Clear input first to ensure clean state
10
        cy.get("#EDI_modal .edi-search-input").clear();
11
12
        // Type with delay and verify the value was entered correctly
13
        cy.get("#EDI_modal .edi-search-input").type(searchTerm, { delay: 50 });
14
15
        // Verify the input contains what we typed - retry if needed
16
        cy.get("#EDI_modal .edi-search-input").should("have.value", searchTerm);
17
18
        if (shouldFindResults) {
19
            // Wait for debounce + search to complete (with longer timeout for reliability)
20
            cy.get("#EDI_modal .edi-search-count", { timeout: 3000 }).should(
21
                "not.contain",
22
                "0 results"
23
            );
24
        } else {
25
            // Wait for debounce period when we expect no results
26
            cy.wait(700);
27
        }
28
    };
29
30
    let edifact_test_data = null;
31
32
    before(() => {
33
        // Insert test EDIFACT messages into the database
34
        cy.task("insertSampleEdifactMessages").then(test_data => {
35
            edifact_test_data = test_data;
36
        });
37
    });
38
39
    after(() => {
40
        // Clean up test data
41
        if (edifact_test_data) {
42
            cy.task("deleteSampleEdifactMessages", edifact_test_data);
43
        }
44
    });
45
46
    beforeEach(() => {
47
        cy.login();
48
        cy.title().should("eq", "Koha staff interface");
49
        cy.visit("/cgi-bin/koha/acqui/edifactmsgs.pl");
50
51
        // Mock comprehensive EDIFACT response for search testing
52
        cy.intercept(
53
            "GET",
54
            "**/edimsg.pl*",
55
            testDataUtils.createMockResponse(edifactTestData.searchableContent)
56
        ).as("searchResponse");
57
    });
58
59
    describe("Search Interface", () => {
60
        beforeEach(() => {
61
            cy.get(".view_edifact_message").first().click();
62
            cy.wait("@searchResponse");
63
            cy.get("#EDI_modal .edi-tree", {
64
                timeout: 10000,
65
            }).should("be.visible");
66
        });
67
68
        it("should display search input field", () => {
69
            cy.get("#EDI_modal .edi-search-input").should("be.visible");
70
            cy.get("#EDI_modal .edi-search-input").should(
71
                "have.attr",
72
                "placeholder",
73
                "Search segments..."
74
            );
75
        });
76
77
        it("should display search input as type search", () => {
78
            cy.get("#EDI_modal .edi-search-input").should(
79
                "have.attr",
80
                "type",
81
                "search"
82
            );
83
        });
84
85
        it("should display search results counter", () => {
86
            cy.get("#EDI_modal .edi-search-count").should("be.visible");
87
            cy.get("#EDI_modal .edi-search-count").should(
88
                "contain",
89
                "0 results"
90
            );
91
        });
92
93
        it("should display navigation buttons", () => {
94
            cy.get("#EDI_modal .edi-search-prev").should("be.visible");
95
            cy.get("#EDI_modal .edi-search-next").should("be.visible");
96
            cy.get("#EDI_modal .edi-search-prev").should("be.disabled");
97
            cy.get("#EDI_modal .edi-search-next").should("be.disabled");
98
        });
99
100
        it("should display search form in main navbar", () => {
101
            cy.get("#EDI_modal .edi-main-navbar .edi-search-form").should(
102
                "exist"
103
            );
104
            cy.get("#EDI_modal .edi-main-navbar .edi-search-form").should(
105
                "be.visible"
106
            );
107
        });
108
    });
109
110
    describe("Search Functionality", () => {
111
        beforeEach(() => {
112
            cy.get(".view_edifact_message").first().click();
113
            cy.wait("@searchResponse");
114
            cy.get("#EDI_modal .edi-tree", {
115
                timeout: 10000,
116
            }).should("be.visible");
117
        });
118
119
        it("should perform search with debouncing", () => {
120
            // Type JavaScript and verify the search completes
121
            typeSearchTerm("JavaScript");
122
123
            // Results should appear
124
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
125
        });
126
127
        it("should find segments containing search term", () => {
128
            typeSearchTerm("SEARCH_BASKET");
129
130
            // Should find the BGM segment with SEARCH_BASKET
131
            cy.get("#EDI_modal .edi-search-count").should("contain", "1 of 1");
132
        });
133
134
        it("should find multiple matches", () => {
135
            typeSearchTerm("JavaScript");
136
137
            // Should find multiple JavaScript matches
138
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
139
        });
140
141
        it("should be case insensitive", () => {
142
            typeSearchTerm("javascript");
143
144
            // Should find JavaScript segments (case insensitive)
145
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
146
        });
147
148
        it("should ignore searches with less than 2 characters", () => {
149
            typeSearchTerm("O", false);
150
151
            // Should not perform search
152
            cy.get("#EDI_modal .edi-search-count").should(
153
                "contain",
154
                "0 results"
155
            );
156
        });
157
158
        it("should highlight search matches", () => {
159
            typeSearchTerm("SEARCH_BASKET");
160
161
            // Should highlight the match
162
            cy.get("#EDI_modal .edi-search-highlight").should("exist");
163
            cy.get("#EDI_modal .edi-search-highlight").should(
164
                "contain",
165
                "SEARCH_BASKET"
166
            );
167
        });
168
169
        it("should enable navigation buttons when results exist", () => {
170
            typeSearchTerm("JavaScript");
171
172
            // Navigation buttons should be enabled
173
            cy.get("#EDI_modal .edi-search-prev").should("not.be.disabled");
174
            cy.get("#EDI_modal .edi-search-next").should("not.be.disabled");
175
        });
176
    });
177
178
    describe("Search Navigation", () => {
179
        beforeEach(() => {
180
            cy.get(".view_edifact_message").first().click();
181
            cy.wait("@searchResponse");
182
            cy.get("#EDI_modal .edi-tree", {
183
                timeout: 10000,
184
            }).should("be.visible");
185
186
            // Perform search to get multiple results
187
            typeSearchTerm("JavaScript");
188
        });
189
190
        it("should navigate to next result", () => {
191
            cy.get("#EDI_modal .edi-search-next").click();
192
193
            // Should update counter (2 of 6)
194
            cy.get("#EDI_modal .edi-search-count").should("contain", "2 of 6");
195
        });
196
197
        it("should navigate to previous result", () => {
198
            // First go to next result
199
            cy.get("#EDI_modal .edi-search-next").click();
200
            cy.get("#EDI_modal .edi-search-count").should("contain", "2 of 6");
201
202
            // Then go back to previous
203
            cy.get("#EDI_modal .edi-search-prev").click();
204
            cy.get("#EDI_modal .edi-search-count").should("contain", "1 of 6");
205
        });
206
207
        it("should wrap around at end of results", () => {
208
            // Navigate to last result (click next 5 times to get to 6 of 6)
209
            cy.get("#EDI_modal .edi-search-next").click();
210
            cy.get("#EDI_modal .edi-search-next").click();
211
            cy.get("#EDI_modal .edi-search-next").click();
212
            cy.get("#EDI_modal .edi-search-next").click();
213
            cy.get("#EDI_modal .edi-search-next").click();
214
            cy.get("#EDI_modal .edi-search-count").should("contain", "6 of 6");
215
216
            // Navigate past end should wrap to first
217
            cy.get("#EDI_modal .edi-search-next").click();
218
            cy.get("#EDI_modal .edi-search-count").should("contain", "1 of 6");
219
        });
220
221
        it("should wrap around at beginning of results", () => {
222
            // Navigate to previous from first result should wrap to last
223
            cy.get("#EDI_modal .edi-search-prev").click();
224
            cy.get("#EDI_modal .edi-search-count").should("contain", "6 of 6");
225
        });
226
227
        it("should highlight current result", () => {
228
            // Current result should have special highlighting
229
            cy.get("#EDI_modal .edi-search-current").should("exist");
230
231
            // Navigate to next result
232
            cy.get("#EDI_modal .edi-search-next").click();
233
            cy.get("#EDI_modal .edi-search-count").should("contain", "2 of 6");
234
235
            // Should still have one current result
236
            cy.get("#EDI_modal .edi-search-current").should("have.length", 1);
237
        });
238
239
        it("should scroll to current result", () => {
240
            // Navigate to next result
241
            cy.get("#EDI_modal .edi-search-next").click();
242
243
            // Current result should be visible
244
            cy.get("#EDI_modal .edi-search-current").should("be.visible");
245
        });
246
    });
247
248
    describe("Keyboard Navigation", () => {
249
        beforeEach(() => {
250
            cy.get(".view_edifact_message").first().click();
251
            cy.wait("@searchResponse");
252
            cy.get("#EDI_modal .edi-tree", {
253
                timeout: 10000,
254
            }).should("be.visible");
255
256
            // Perform search to get multiple results
257
            typeSearchTerm("JavaScript");
258
        });
259
260
        it("should navigate with Enter key", () => {
261
            cy.get("#EDI_modal .edi-search-input").type("{enter}");
262
263
            // Should navigate to next result
264
            cy.get("#EDI_modal .edi-search-count").should("contain", "2 of 6");
265
        });
266
267
        it("should navigate backwards with Shift+Enter", () => {
268
            // First go to next result
269
            cy.get("#EDI_modal .edi-search-input").type("{enter}");
270
            cy.get("#EDI_modal .edi-search-count").should("contain", "2 of 6");
271
272
            // Then go back with Shift+Enter
273
            cy.get("#EDI_modal .edi-search-input").type("{shift+enter}");
274
            cy.get("#EDI_modal .edi-search-count").should("contain", "1 of 6");
275
        });
276
277
        it("should not submit form on Enter key", () => {
278
            // Enter key should not cause page navigation
279
            cy.get("#EDI_modal .edi-search-input").type("{enter}");
280
            cy.url().should("include", "/acqui/edifactmsgs.pl");
281
        });
282
    });
283
284
    describe("Search Clearing", () => {
285
        beforeEach(() => {
286
            cy.get(".view_edifact_message").first().click();
287
            cy.wait("@searchResponse");
288
            cy.get("#EDI_modal .edi-tree", {
289
                timeout: 10000,
290
            }).should("be.visible");
291
292
            // Perform search
293
            typeSearchTerm("JavaScript");
294
        });
295
296
        it("should clear search when input is cleared with native clear", () => {
297
            // Use browser's native clear functionality
298
            cy.get("#EDI_modal .edi-search-input").clear();
299
            cy.wait(600); // Wait for debounce
300
301
            // Results should be reset
302
            cy.get("#EDI_modal .edi-search-count").should(
303
                "contain",
304
                "0 results"
305
            );
306
307
            // Navigation buttons should be disabled
308
            cy.get("#EDI_modal .edi-search-prev").should("be.disabled");
309
            cy.get("#EDI_modal .edi-search-next").should("be.disabled");
310
        });
311
312
        it("should remove highlights when search is cleared", () => {
313
            cy.get("#EDI_modal .edi-search-highlight").should("exist");
314
315
            cy.get("#EDI_modal .edi-search-input").clear();
316
            cy.wait(600); // Wait for debounce
317
318
            // Highlights should be removed
319
            cy.get("#EDI_modal .edi-search-highlight").should("not.exist");
320
            cy.get("#EDI_modal .edi-search-current").should("not.exist");
321
        });
322
323
        it("should clear search when input is emptied", () => {
324
            cy.get("#EDI_modal .edi-search-input").clear();
325
            cy.wait(600);
326
327
            // Results should be reset
328
            cy.get("#EDI_modal .edi-search-count").should(
329
                "contain",
330
                "0 results"
331
            );
332
333
            // Highlights should be removed
334
            cy.get("#EDI_modal .edi-search-highlight").should("not.exist");
335
        });
336
    });
337
338
    describe("Search in Different Views", () => {
339
        beforeEach(() => {
340
            cy.get(".view_edifact_message").first().click();
341
            cy.wait("@searchResponse");
342
            cy.get("#EDI_modal .edi-tree", {
343
                timeout: 10000,
344
            }).should("be.visible");
345
        });
346
347
        it("should search in tree view", () => {
348
            // Ensure we're in tree view
349
            cy.get('#EDI_modal [data-view="tree"]').click();
350
351
            typeSearchTerm("JavaScript");
352
353
            // Should find results in tree view
354
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
355
            cy.get("#EDI_modal .edi-search-highlight").should("exist");
356
        });
357
358
        it("should search in raw view", () => {
359
            // Switch to raw view
360
            cy.get('#EDI_modal [data-view="raw"]').click();
361
362
            typeSearchTerm("JavaScript");
363
364
            // Should find results in raw view
365
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
366
            cy.get("#EDI_modal .edi-search-highlight").should("exist");
367
        });
368
369
        it("should re-execute search when switching views", () => {
370
            // Search in tree view
371
            typeSearchTerm("JavaScript");
372
373
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
374
375
            // Switch to raw view
376
            cy.get('#EDI_modal [data-view="raw"]').click();
377
378
            // Search should be re-executed in raw view
379
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
380
            cy.get("#EDI_modal .edi-search-highlight").should("exist");
381
        });
382
    });
383
384
    describe("Smart Expand/Collapse for Search", () => {
385
        beforeEach(() => {
386
            cy.get(".view_edifact_message").first().click();
387
            cy.wait("@searchResponse");
388
            cy.get("#EDI_modal .edi-tree", {
389
                timeout: 10000,
390
            }).should("be.visible");
391
        });
392
393
        it("should expand sections containing search results", () => {
394
            // Collapse all sections first
395
            cy.get("#EDI_modal .collapse-all-btn").click();
396
            cy.wait(500);
397
398
            // Perform search
399
            typeSearchTerm("JavaScript");
400
401
            // Sections with results should be expanded
402
            cy.get("#EDI_modal .edi-search-current").should("be.visible");
403
            cy.get("#EDI_modal .edi-search-current")
404
                .parents(".collapse")
405
                .should("have.class", "show");
406
        });
407
408
        it("should collapse sections without search results", () => {
409
            // Expand all sections first
410
            cy.get("#EDI_modal .expand-all-btn").click();
411
            cy.wait(500);
412
413
            // Perform specific search that matches only some sections
414
            typeSearchTerm("SEARCH_BASKET");
415
416
            // Wait for smart expand/collapse to complete
417
            cy.wait(100);
418
419
            // Section with result should be expanded
420
            cy.get("#EDI_modal .edi-search-current").should("be.visible");
421
        });
422
    });
423
424
    describe("Search Performance", () => {
425
        beforeEach(() => {
426
            cy.get(".view_edifact_message").first().click();
427
            cy.wait("@searchResponse");
428
            cy.get("#EDI_modal .edi-tree", {
429
                timeout: 10000,
430
            }).should("be.visible");
431
        });
432
433
        it("should handle rapid typing with debouncing", () => {
434
            // Type rapidly (spell JavaScript)
435
            cy.get("#EDI_modal .edi-search-input").type("J");
436
            cy.get("#EDI_modal .edi-search-input").type("a");
437
            cy.get("#EDI_modal .edi-search-input").type("v");
438
            cy.get("#EDI_modal .edi-search-input").type("a");
439
            cy.get("#EDI_modal .edi-search-input").type("S");
440
441
            // Should not perform search until debounce completes
442
            cy.get("#EDI_modal .edi-search-count").should(
443
                "contain",
444
                "0 results"
445
            );
446
447
            // Wait for debounce
448
            cy.wait(600);
449
450
            // Should perform search once
451
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
452
        });
453
454
        it("should handle empty search gracefully", () => {
455
            cy.get("#EDI_modal .edi-search-input").type("   ");
456
            cy.wait(600);
457
458
            // Should not perform search for whitespace only
459
            cy.get("#EDI_modal .edi-search-count").should(
460
                "contain",
461
                "0 results"
462
            );
463
        });
464
465
        it("should handle special characters in search", () => {
466
            typeSearchTerm("220+ORDER");
467
468
            // Should handle special regex characters (should find RFF+IV:220+ORDER67890)
469
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
470
        });
471
    });
472
473
    describe("Search State Management", () => {
474
        beforeEach(() => {
475
            cy.get(".view_edifact_message").first().click();
476
            cy.wait("@searchResponse");
477
            cy.get("#EDI_modal .edi-tree", {
478
                timeout: 10000,
479
            }).should("be.visible");
480
        });
481
482
        it("should maintain search state when modal is reopened", () => {
483
            // Perform search
484
            typeSearchTerm("JavaScript");
485
486
            // Close modal
487
            cy.get("#EDI_modal .btn-close").click();
488
489
            // Reopen modal
490
            cy.get(".view_edifact_message").first().click();
491
            cy.wait("@searchResponse");
492
            cy.get("#EDI_modal .edi-tree", {
493
                timeout: 10000,
494
            }).should("be.visible");
495
496
            // Search should be cleared on new modal open
497
            cy.get("#EDI_modal .edi-search-input").should("have.value", "");
498
            cy.get("#EDI_modal .edi-search-count").should(
499
                "contain",
500
                "0 results"
501
            );
502
        });
503
504
        it("should clear search state on modal close", () => {
505
            // Perform search
506
            typeSearchTerm("JavaScript");
507
            cy.get("#EDI_modal .edi-search-count").should("contain", "of");
508
509
            // Close modal
510
            cy.get("#EDI_modal .btn-close").click();
511
512
            // Modal should reset content
513
            cy.get("#EDI_modal .modal-body").should("contain", "Loading");
514
        });
515
    });
516
});
(-)a/t/cypress/plugins/index.js (+4 lines)
Lines 6-11 const { Link Here
6
    insertSampleBiblio,
6
    insertSampleBiblio,
7
    insertObject,
7
    insertObject,
8
    deleteSampleObjects,
8
    deleteSampleObjects,
9
    insertSampleEdifactMessages,
10
    deleteSampleEdifactMessages,
9
} = require("./insertData.js");
11
} = require("./insertData.js");
10
12
11
const { getBasicAuthHeader } = require("./auth.js");
13
const { getBasicAuthHeader } = require("./auth.js");
Lines 43-48 module.exports = (on, config) => { Link Here
43
            return insertObject(type, object, baseUrl, authHeader);
45
            return insertObject(type, object, baseUrl, authHeader);
44
        },
46
        },
45
        deleteSampleObjects,
47
        deleteSampleObjects,
48
        insertSampleEdifactMessages,
49
        deleteSampleEdifactMessages,
46
        query,
50
        query,
47
51
48
        apiGet(args) {
52
        apiGet(args) {
(-)a/t/cypress/plugins/insertData.js (-1 / +120 lines)
Lines 281-288 const insertObject = async (type, object, baseUrl, authHeader) => { Link Here
281
    return true;
281
    return true;
282
};
282
};
283
283
284
const insertSampleEdifactMessages = async () => {
285
    // First, create a test vendor
286
    const vendorResult = await query({
287
        sql: `INSERT INTO aqbooksellers (name, address1, address2, address3, phone, fax, url, active, listprice, invoiceprice, gstreg, listincgst, invoiceincgst, tax_rate, discount, notes) 
288
              VALUES ('Test EDI Vendor', '123 Test Street', 'Test City', 'Test Country', '555-1234', '555-5678', 'http://testvendor.com', 1, 'USD', 'USD', 0, 1, 1, 0.10, 0.05, 'Test vendor for EDIFACT testing')`,
289
        values: [],
290
    });
291
    const vendor_id = vendorResult.insertId;
292
293
    // Create a test EDI account for the vendor
294
    const ediAccountResult = await query({
295
        sql: `INSERT INTO vendor_edi_accounts (description, host, username, password, vendor_id, san, standard, quotes_enabled, invoices_enabled, orders_enabled, responses_enabled, auto_orders, plugin) 
296
              VALUES ('Test EDI Account', 'test.edi.host', 'testuser', 'testpass', ?, 'TEST123', 'EUR', 1, 1, 1, 1, 0, '')`,
297
        values: [vendor_id],
298
    });
299
    const edi_acct = ediAccountResult.insertId;
300
301
    // Insert sample EDIFACT messages for testing
302
    const messages = [
303
        {
304
            message_type: "ORDERS",
305
            transfer_date: "2025-01-20",
306
            vendor_id: vendor_id,
307
            edi_acct: edi_acct,
308
            status: "new",
309
            basketno: null,
310
            filename: "test_order_001.edi",
311
            raw_msg: `UNA:+.? 'UNB+UNOC:3+TEST_SUPPLIER:14+TEST_LIBRARY:14+20250121:1234+1++1++1'UNH+1+ORDERS:D:96A:UN:EAN008'BGM+220+BASKET001+9'DTM+137:20250721:102'RFF+ON:LibraryBasket001'NAD+BY+1234567890123::91++Test Library System+123 Library Street+Booktown+12345+US'LIN+1++9781234567890:IB'PIA+5+BOOK001:SA'IMD+L+009+:::Test Author'IMD+L+050+:::Introduction to Library Science'QTY+21:3'PRI+AAE:29.95:CA'RFF+LI:ORDER001'UNT+12+1'UNZ+1+1'`,
312
        },
313
        {
314
            message_type: "INVOIC",
315
            transfer_date: "2025-01-19",
316
            vendor_id: vendor_id,
317
            edi_acct: edi_acct,
318
            status: "new",
319
            basketno: null,
320
            filename: "test_invoice_001.edi",
321
            raw_msg: `UNA:+.? 'UNB+UNOC:3+FOCUS_SUPPLIER:14+FOCUS_LIBRARY:14+20250721:1600+3++1++1'UNH+2+INVOIC:D:96A:UN:EAN008'BGM+380+67890+9'DTM+137:20250720:102'RFF+IV:TEST_INVOICE_001'RFF+ON:FocusBasket002'NAD+SU+9876543210987::91++Test Book Supplier+456 Publisher Ave+Bookville+54321+US'LIN+1++9789876543210:IB'IMD+L+009+:::Sample Author'IMD+L+050+:::Advanced Library Management'QTY+47:2'PRI+AAA:45.00:CA'MOA+203:90.00:USD'UNT+13+2'UNZ+1+3'`,
322
        },
323
        {
324
            message_type: "ORDERS",
325
            transfer_date: "2025-01-18",
326
            vendor_id: vendor_id,
327
            edi_acct: edi_acct,
328
            status: "new",
329
            basketno: null,
330
            filename: "test_search_content.edi",
331
            raw_msg: `UNA:+.? 'UNB+UNOC:3+SEARCH_SUPPLIER:14+SEARCH_LIBRARY:14+20250721:1500+2++1++1'UNH+1+ORDERS:D:96A:UN:EAN008'BGM+220+SEARCH_BASKET+9'IMD+L+009+:::JavaScript Programming Guide'IMD+L+050+:::Learn modern JavaScript programming techniques'PIA+5+JS001:SA+9781234567890:IB'FTX+LIN+++Essential reading for web developers learning JavaScript'UNT+6+1'UNZ+1+2'`,
332
        },
333
    ];
334
335
    const inserted_ids = [];
336
    for (const message of messages) {
337
        const result = await query({
338
            sql: `INSERT INTO edifact_messages (message_type, transfer_date, vendor_id, edi_acct, status, basketno, filename, raw_msg) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
339
            values: [
340
                message.message_type,
341
                message.transfer_date,
342
                message.vendor_id,
343
                message.edi_acct,
344
                message.status,
345
                message.basketno,
346
                message.filename,
347
                message.raw_msg,
348
            ],
349
        });
350
        inserted_ids.push(result.insertId);
351
    }
352
353
    return {
354
        message_ids: inserted_ids,
355
        vendor_id: vendor_id,
356
        edi_acct: edi_acct,
357
    };
358
};
359
360
const deleteSampleEdifactMessages = async test_data => {
361
    if (!test_data) {
362
        // Clean up all test messages and related data
363
        await query({
364
            sql: "DELETE FROM edifact_messages WHERE filename LIKE 'test_%'",
365
        });
366
        await query({
367
            sql: "DELETE FROM vendor_edi_accounts WHERE description = 'Test EDI Account'",
368
        });
369
        await query({
370
            sql: "DELETE FROM aqbooksellers WHERE name = 'Test EDI Vendor'",
371
        });
372
        return { success: true };
373
    }
374
375
    if (test_data.message_ids) {
376
        // Delete specific messages
377
        await query({
378
            sql: `DELETE FROM edifact_messages WHERE id IN (${test_data.message_ids.map(() => "?").join(",")})`,
379
            values: test_data.message_ids,
380
        });
381
    }
382
383
    if (test_data.edi_acct) {
384
        // Delete the test EDI account
385
        await query({
386
            sql: "DELETE FROM vendor_edi_accounts WHERE id = ?",
387
            values: [test_data.edi_acct],
388
        });
389
    }
390
391
    if (test_data.vendor_id) {
392
        // Delete the test vendor
393
        await query({
394
            sql: "DELETE FROM aqbooksellers WHERE id = ?",
395
            values: [test_data.vendor_id],
396
        });
397
    }
398
399
    return { success: true };
400
};
401
284
module.exports = {
402
module.exports = {
285
    insertSampleBiblio,
403
    insertSampleBiblio,
286
    insertObject,
404
    insertObject,
287
    deleteSampleObjects,
405
    deleteSampleObjects,
406
    insertSampleEdifactMessages,
407
    deleteSampleEdifactMessages,
288
};
408
};
289
- 

Return to bug 40383