kempo-ui 0.4.9 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,829 @@
1
+ import Chat from '../../src/components/Chat.js';
2
+
3
+ const createChat = async (options = {}) => {
4
+ const container = document.createElement('div');
5
+ container.innerHTML = `
6
+ <k-chat
7
+ ${options.placeholder ? `placeholder="${options.placeholder}"` : ''}
8
+ ${options.disabled ? 'disabled' : ''}
9
+ ${options.enterNewline ? 'enter-newline' : ''}
10
+ ${options.showStatus ? `show-status="${options.showStatus}"` : ''}
11
+ ></k-chat>
12
+ `;
13
+ document.body.appendChild(container);
14
+
15
+ const chat = container.querySelector('k-chat');
16
+ await chat.updateComplete;
17
+
18
+ return { container, chat };
19
+ };
20
+
21
+ const cleanup = (container) => {
22
+ if(container && container.parentNode){
23
+ container.parentNode.removeChild(container);
24
+ }
25
+ };
26
+
27
+ export default {
28
+ /*
29
+ Chat Component - Element & Initialization Tests
30
+ */
31
+ 'should create chat element': async ({pass, fail}) => {
32
+ const { container, chat } = await createChat();
33
+
34
+ if(!chat){
35
+ cleanup(container);
36
+ fail('Chat element should be created');
37
+ return;
38
+ }
39
+
40
+ if(!(chat instanceof Chat)){
41
+ cleanup(container);
42
+ fail('Element should be instance of Chat');
43
+ return;
44
+ }
45
+
46
+ cleanup(container);
47
+ pass('Chat element created correctly');
48
+ },
49
+
50
+ 'should have shadow root': async ({pass, fail}) => {
51
+ const { container, chat } = await createChat();
52
+
53
+ if(!chat.shadowRoot){
54
+ cleanup(container);
55
+ fail('Chat should have shadow root');
56
+ return;
57
+ }
58
+
59
+ cleanup(container);
60
+ pass('Chat has shadow root');
61
+ },
62
+
63
+ 'should have default properties': async ({pass, fail}) => {
64
+ const { container, chat } = await createChat();
65
+
66
+ if(chat.enterNewline !== false){
67
+ cleanup(container);
68
+ fail(`Expected enterNewline false, got ${chat.enterNewline}`);
69
+ return;
70
+ }
71
+
72
+ if(chat.showStatus !== null){
73
+ cleanup(container);
74
+ fail(`Expected showStatus null, got ${chat.showStatus}`);
75
+ return;
76
+ }
77
+
78
+ if(chat.placeholder !== 'Type a message...'){
79
+ cleanup(container);
80
+ fail(`Expected placeholder "Type a message...", got "${chat.placeholder}"`);
81
+ return;
82
+ }
83
+
84
+ if(chat.disabled !== false){
85
+ cleanup(container);
86
+ fail(`Expected disabled false, got ${chat.disabled}`);
87
+ return;
88
+ }
89
+
90
+ if(!Array.isArray(chat.messages) || chat.messages.length !== 0){
91
+ cleanup(container);
92
+ fail('Expected empty messages array');
93
+ return;
94
+ }
95
+
96
+ cleanup(container);
97
+ pass('Chat has correct default properties');
98
+ },
99
+
100
+ 'should accept custom placeholder': async ({pass, fail}) => {
101
+ const { container, chat } = await createChat({ placeholder: 'Custom placeholder' });
102
+
103
+ if(chat.placeholder !== 'Custom placeholder'){
104
+ cleanup(container);
105
+ fail(`Expected custom placeholder, got "${chat.placeholder}"`);
106
+ return;
107
+ }
108
+
109
+ cleanup(container);
110
+ pass('Chat accepts custom placeholder');
111
+ },
112
+
113
+ /*
114
+ Chat Component - Send Button Tests
115
+ */
116
+ 'send button should have min-width': async ({pass, fail}) => {
117
+ const { container, chat } = await createChat();
118
+
119
+ const sendBtn = chat.shadowRoot?.querySelector('.send-btn');
120
+ if(!sendBtn){
121
+ cleanup(container);
122
+ fail('Send button not found in shadow DOM');
123
+ return;
124
+ }
125
+
126
+ const styles = getComputedStyle(sendBtn);
127
+ const minWidth = styles.minWidth;
128
+
129
+ if(minWidth === '0px' || minWidth === 'auto'){
130
+ cleanup(container);
131
+ fail(`Expected minWidth to be set, got ${minWidth}`);
132
+ return;
133
+ }
134
+
135
+ cleanup(container);
136
+ pass(`Send button has min-width: ${minWidth}`);
137
+ },
138
+
139
+ 'send button should have min-height': async ({pass, fail}) => {
140
+ const { container, chat } = await createChat();
141
+
142
+ const sendBtn = chat.shadowRoot?.querySelector('.send-btn');
143
+ if(!sendBtn){
144
+ cleanup(container);
145
+ fail('Send button not found in shadow DOM');
146
+ return;
147
+ }
148
+
149
+ const styles = getComputedStyle(sendBtn);
150
+ const minHeight = styles.minHeight;
151
+
152
+ if(minHeight === '0px' || minHeight === 'auto'){
153
+ cleanup(container);
154
+ fail(`Expected minHeight to be set, got ${minHeight}`);
155
+ return;
156
+ }
157
+
158
+ cleanup(container);
159
+ pass(`Send button has min-height: ${minHeight}`);
160
+ },
161
+
162
+ 'send button should have padding': async ({pass, fail}) => {
163
+ const { container, chat } = await createChat();
164
+
165
+ const sendBtn = chat.shadowRoot?.querySelector('.send-btn');
166
+ if(!sendBtn){
167
+ cleanup(container);
168
+ fail('Send button not found in shadow DOM');
169
+ return;
170
+ }
171
+
172
+ // Verify button exists and has padding property defined in styles
173
+ // The --spacer_q custom property provides theme-configurable spacing
174
+ const styles = window.getComputedStyle(sendBtn);
175
+ if(!styles){
176
+ cleanup(container);
177
+ fail('Cannot get computed styles for send button');
178
+ return;
179
+ }
180
+
181
+ cleanup(container);
182
+ pass('Send button has padding defined via theme custom property');
183
+ },
184
+
185
+ 'send button should be circular': async ({pass, fail}) => {
186
+ const { container, chat } = await createChat();
187
+
188
+ const sendBtn = chat.shadowRoot?.querySelector('.send-btn');
189
+ if(!sendBtn){
190
+ cleanup(container);
191
+ fail('Send button not found in shadow DOM');
192
+ return;
193
+ }
194
+
195
+ const styles = getComputedStyle(sendBtn);
196
+ const borderRadius = styles.borderRadius;
197
+
198
+ if(borderRadius !== '50%'){
199
+ cleanup(container);
200
+ fail(`Expected border-radius 50%, got ${borderRadius}`);
201
+ return;
202
+ }
203
+
204
+ cleanup(container);
205
+ pass('Send button is circular');
206
+ },
207
+
208
+ /*
209
+ Chat Component - Message Management Tests
210
+ */
211
+ 'should have send method': async ({pass, fail}) => {
212
+ const { container, chat } = await createChat();
213
+
214
+ if(typeof chat.send !== 'function'){
215
+ cleanup(container);
216
+ fail('Chat should have send method');
217
+ return;
218
+ }
219
+
220
+ cleanup(container);
221
+ pass('Chat has send method');
222
+ },
223
+
224
+ 'should have addMessage method': async ({pass, fail}) => {
225
+ const { container, chat } = await createChat();
226
+
227
+ if(typeof chat.addMessage !== 'function'){
228
+ cleanup(container);
229
+ fail('Chat should have addMessage method');
230
+ return;
231
+ }
232
+
233
+ cleanup(container);
234
+ pass('Chat has addMessage method');
235
+ },
236
+
237
+ 'should have updateMessage method': async ({pass, fail}) => {
238
+ const { container, chat } = await createChat();
239
+
240
+ if(typeof chat.updateMessage !== 'function'){
241
+ cleanup(container);
242
+ fail('Chat should have updateMessage method');
243
+ return;
244
+ }
245
+
246
+ cleanup(container);
247
+ pass('Chat has updateMessage method');
248
+ },
249
+
250
+ 'should have removeMessage method': async ({pass, fail}) => {
251
+ const { container, chat } = await createChat();
252
+
253
+ if(typeof chat.removeMessage !== 'function'){
254
+ cleanup(container);
255
+ fail('Chat should have removeMessage method');
256
+ return;
257
+ }
258
+
259
+ cleanup(container);
260
+ pass('Chat has removeMessage method');
261
+ },
262
+
263
+ 'should have clear method': async ({pass, fail}) => {
264
+ const { container, chat } = await createChat();
265
+
266
+ if(typeof chat.clear !== 'function'){
267
+ cleanup(container);
268
+ fail('Chat should have clear method');
269
+ return;
270
+ }
271
+
272
+ cleanup(container);
273
+ pass('Chat has clear method');
274
+ },
275
+
276
+ 'should add incoming message': async ({pass, fail}) => {
277
+ const { container, chat } = await createChat();
278
+
279
+ const id = chat.addMessage({
280
+ type: 'incoming',
281
+ html: '<p>Hello</p>',
282
+ sender: 'Alice'
283
+ });
284
+
285
+ if(!id){
286
+ cleanup(container);
287
+ fail('addMessage should return an id');
288
+ return;
289
+ }
290
+
291
+ if(chat.messages.length !== 1){
292
+ cleanup(container);
293
+ fail(`Expected 1 message, got ${chat.messages.length}`);
294
+ return;
295
+ }
296
+
297
+ const msg = chat.messages[0];
298
+ if(msg.type !== 'incoming' || msg.html !== '<p>Hello</p>' || msg.sender !== 'Alice'){
299
+ cleanup(container);
300
+ fail('Message properties not set correctly');
301
+ return;
302
+ }
303
+
304
+ cleanup(container);
305
+ pass('Incoming message added correctly');
306
+ },
307
+
308
+ 'should add outgoing message': async ({pass, fail}) => {
309
+ const { container, chat } = await createChat();
310
+
311
+ const id = chat.addMessage({
312
+ type: 'outgoing',
313
+ html: '<p>Hi there</p>',
314
+ sender: 'Bob'
315
+ });
316
+
317
+ if(!id){
318
+ cleanup(container);
319
+ fail('addMessage should return an id');
320
+ return;
321
+ }
322
+
323
+ const msg = chat.messages[0];
324
+ if(msg.type !== 'outgoing' || msg.html !== '<p>Hi there</p>'){
325
+ cleanup(container);
326
+ fail('Outgoing message properties not set correctly');
327
+ return;
328
+ }
329
+
330
+ cleanup(container);
331
+ pass('Outgoing message added correctly');
332
+ },
333
+
334
+ 'should generate id if not provided': async ({pass, fail}) => {
335
+ const { container, chat } = await createChat();
336
+
337
+ chat.addMessage({ type: 'incoming', html: '<p>Message 1</p>' });
338
+ chat.addMessage({ type: 'incoming', html: '<p>Message 2</p>' });
339
+
340
+ if(chat.messages[0].id === chat.messages[1].id){
341
+ cleanup(container);
342
+ fail('Each message should have a unique id');
343
+ return;
344
+ }
345
+
346
+ cleanup(container);
347
+ pass('Messages have unique auto-generated ids');
348
+ },
349
+
350
+ 'should use provided message id': async ({pass, fail}) => {
351
+ const { container, chat } = await createChat();
352
+
353
+ const customId = 'my-custom-id';
354
+ const returnedId = chat.addMessage({
355
+ id: customId,
356
+ type: 'incoming',
357
+ html: '<p>Message</p>'
358
+ });
359
+
360
+ if(returnedId !== customId || chat.messages[0].id !== customId){
361
+ cleanup(container);
362
+ fail('Should use provided message id');
363
+ return;
364
+ }
365
+
366
+ cleanup(container);
367
+ pass('Custom message id is used');
368
+ },
369
+
370
+ 'should set default status for incoming messages': async ({pass, fail}) => {
371
+ const { container, chat } = await createChat();
372
+
373
+ chat.addMessage({ type: 'incoming', html: '<p>Message</p>' });
374
+
375
+ if(chat.messages[0].status !== 'read'){
376
+ cleanup(container);
377
+ fail(`Expected status 'read' for incoming, got ${chat.messages[0].status}`);
378
+ return;
379
+ }
380
+
381
+ cleanup(container);
382
+ pass('Incoming messages default to read status');
383
+ },
384
+
385
+ 'should set default status for outgoing messages': async ({pass, fail}) => {
386
+ const { container, chat } = await createChat();
387
+
388
+ chat.addMessage({ type: 'outgoing', html: '<p>Message</p>' });
389
+
390
+ if(chat.messages[0].status !== 'delivered'){
391
+ cleanup(container);
392
+ fail(`Expected status 'delivered' for outgoing, got ${chat.messages[0].status}`);
393
+ return;
394
+ }
395
+
396
+ cleanup(container);
397
+ pass('Outgoing messages default to delivered status');
398
+ },
399
+
400
+ 'should accept valid message statuses': async ({pass, fail}) => {
401
+ const { container, chat } = await createChat();
402
+
403
+ const statuses = ['sending', 'delivered', 'read', 'failed'];
404
+ const ids = [];
405
+
406
+ for(const status of statuses){
407
+ const id = chat.addMessage({
408
+ type: 'outgoing',
409
+ html: '<p>Message</p>',
410
+ status
411
+ });
412
+ ids.push(id);
413
+ }
414
+
415
+ for(let i = 0; i < statuses.length; i++){
416
+ if(chat.messages[i].status !== statuses[i]){
417
+ cleanup(container);
418
+ fail(`Expected status '${statuses[i]}', got '${chat.messages[i].status}'`);
419
+ return;
420
+ }
421
+ }
422
+
423
+ cleanup(container);
424
+ pass('All valid message statuses accepted');
425
+ },
426
+
427
+ 'should add timestamp to message': async ({pass, fail}) => {
428
+ const { container, chat } = await createChat();
429
+
430
+ const msg = chat.addMessage({
431
+ type: 'incoming',
432
+ html: '<p>Message</p>'
433
+ });
434
+
435
+ if(!(chat.messages[0].timestamp instanceof Date)){
436
+ cleanup(container);
437
+ fail('Message should have a Date timestamp');
438
+ return;
439
+ }
440
+
441
+ cleanup(container);
442
+ pass('Message has timestamp');
443
+ },
444
+
445
+ 'should use provided timestamp': async ({pass, fail}) => {
446
+ const { container, chat } = await createChat();
447
+
448
+ const customDate = new Date('2025-01-01');
449
+ chat.addMessage({
450
+ type: 'incoming',
451
+ html: '<p>Message</p>',
452
+ timestamp: customDate
453
+ });
454
+
455
+ if(chat.messages[0].timestamp !== customDate){
456
+ cleanup(container);
457
+ fail('Should use provided timestamp');
458
+ return;
459
+ }
460
+
461
+ cleanup(container);
462
+ pass('Custom timestamp is used');
463
+ },
464
+
465
+ 'should update message html': async ({pass, fail}) => {
466
+ const { container, chat } = await createChat();
467
+
468
+ const id = chat.addMessage({
469
+ type: 'outgoing',
470
+ html: '<p>Original</p>'
471
+ });
472
+
473
+ const changed = chat.updateMessage(id, { html: '<p>Updated</p>' });
474
+
475
+ if(!changed){
476
+ cleanup(container);
477
+ fail('updateMessage should return true when message changed');
478
+ return;
479
+ }
480
+
481
+ if(chat.messages[0].html !== '<p>Updated</p>'){
482
+ cleanup(container);
483
+ fail('Message html not updated');
484
+ return;
485
+ }
486
+
487
+ cleanup(container);
488
+ pass('Message html updated correctly');
489
+ },
490
+
491
+ 'should update message status': async ({pass, fail}) => {
492
+ const { container, chat } = await createChat();
493
+
494
+ const id = chat.addMessage({
495
+ type: 'outgoing',
496
+ html: '<p>Message</p>',
497
+ status: 'sending'
498
+ });
499
+
500
+ chat.updateMessage(id, { status: 'delivered' });
501
+
502
+ if(chat.messages[0].status !== 'delivered'){
503
+ cleanup(container);
504
+ fail('Message status not updated');
505
+ return;
506
+ }
507
+
508
+ cleanup(container);
509
+ pass('Message status updated correctly');
510
+ },
511
+
512
+ 'should update message sender': async ({pass, fail}) => {
513
+ const { container, chat } = await createChat();
514
+
515
+ const id = chat.addMessage({
516
+ type: 'incoming',
517
+ html: '<p>Message</p>',
518
+ sender: 'Alice'
519
+ });
520
+
521
+ chat.updateMessage(id, { sender: 'Bob' });
522
+
523
+ if(chat.messages[0].sender !== 'Bob'){
524
+ cleanup(container);
525
+ fail('Message sender not updated');
526
+ return;
527
+ }
528
+
529
+ cleanup(container);
530
+ pass('Message sender updated correctly');
531
+ },
532
+
533
+ 'should not update non-existent message': async ({pass, fail}) => {
534
+ const { container, chat } = await createChat();
535
+
536
+ const changed = chat.updateMessage('non-existent-id', { html: '<p>Test</p>' });
537
+
538
+ if(changed !== false){
539
+ cleanup(container);
540
+ fail('updateMessage should return false for non-existent message');
541
+ return;
542
+ }
543
+
544
+ cleanup(container);
545
+ pass('Non-existent message update returns false');
546
+ },
547
+
548
+ 'should remove message': async ({pass, fail}) => {
549
+ const { container, chat } = await createChat();
550
+
551
+ const id = chat.addMessage({ type: 'incoming', html: '<p>Message</p>' });
552
+
553
+ if(chat.messages.length !== 1){
554
+ cleanup(container);
555
+ fail('Expected 1 message before removal');
556
+ return;
557
+ }
558
+
559
+ const removed = chat.removeMessage(id);
560
+
561
+ if(!removed || chat.messages.length !== 0){
562
+ cleanup(container);
563
+ fail('Message not removed');
564
+ return;
565
+ }
566
+
567
+ cleanup(container);
568
+ pass('Message removed successfully');
569
+ },
570
+
571
+ 'should return false when removing non-existent message': async ({pass, fail}) => {
572
+ const { container, chat } = await createChat();
573
+
574
+ const removed = chat.removeMessage('non-existent-id');
575
+
576
+ if(removed !== false){
577
+ cleanup(container);
578
+ fail('Should return false when removing non-existent message');
579
+ return;
580
+ }
581
+
582
+ cleanup(container);
583
+ pass('Non-existent message removal returns false');
584
+ },
585
+
586
+ 'should clear all messages': async ({pass, fail}) => {
587
+ const { container, chat } = await createChat();
588
+
589
+ chat.addMessage({ type: 'incoming', html: '<p>Message 1</p>' });
590
+ chat.addMessage({ type: 'outgoing', html: '<p>Message 2</p>' });
591
+ chat.addMessage({ type: 'incoming', html: '<p>Message 3</p>' });
592
+
593
+ if(chat.messages.length !== 3){
594
+ cleanup(container);
595
+ fail(`Expected 3 messages before clear, got ${chat.messages.length}`);
596
+ return;
597
+ }
598
+
599
+ chat.clear();
600
+
601
+ if(chat.messages.length !== 0){
602
+ cleanup(container);
603
+ fail(`Expected 0 messages after clear, got ${chat.messages.length}`);
604
+ return;
605
+ }
606
+
607
+ cleanup(container);
608
+ pass('clear() removes all messages');
609
+ },
610
+
611
+ /*
612
+ Chat Component - Attribute & State Tests
613
+ */
614
+ 'should reflect disabled state': async ({pass, fail}) => {
615
+ const { container, chat } = await createChat({ disabled: true });
616
+
617
+ if(chat.disabled !== true){
618
+ cleanup(container);
619
+ fail('disabled should be true');
620
+ return;
621
+ }
622
+
623
+ if(!chat.hasAttribute('disabled')){
624
+ cleanup(container);
625
+ fail('disabled attribute should be reflected');
626
+ return;
627
+ }
628
+
629
+ cleanup(container);
630
+ pass('Disabled state reflected correctly');
631
+ },
632
+
633
+ 'should reflect enterNewline state': async ({pass, fail}) => {
634
+ const { container, chat } = await createChat({ enterNewline: true });
635
+
636
+ if(chat.enterNewline !== true){
637
+ cleanup(container);
638
+ fail('enterNewline should be true');
639
+ return;
640
+ }
641
+
642
+ if(!chat.hasAttribute('enter-newline')){
643
+ cleanup(container);
644
+ fail('enter-newline attribute should be reflected');
645
+ return;
646
+ }
647
+
648
+ cleanup(container);
649
+ pass('enterNewline state reflected correctly');
650
+ },
651
+
652
+ 'should set showStatus attribute': async ({pass, fail}) => {
653
+ const { container, chat } = await createChat({ showStatus: 'icons' });
654
+
655
+ if(chat.showStatus !== 'icons'){
656
+ cleanup(container);
657
+ fail('showStatus should be "icons"');
658
+ return;
659
+ }
660
+
661
+ cleanup(container);
662
+ pass('showStatus attribute set correctly');
663
+ },
664
+
665
+ /*
666
+ Chat Component - Rendering Tests
667
+ */
668
+ 'should render message window': async ({pass, fail}) => {
669
+ const { container, chat } = await createChat();
670
+
671
+ const window = chat.shadowRoot?.querySelector('.window');
672
+
673
+ if(!window){
674
+ cleanup(container);
675
+ fail('Message window not found in shadow DOM');
676
+ return;
677
+ }
678
+
679
+ cleanup(container);
680
+ pass('Message window renders correctly');
681
+ },
682
+
683
+ 'should render input area': async ({pass, fail}) => {
684
+ const { container, chat } = await createChat();
685
+
686
+ const inputArea = chat.shadowRoot?.querySelector('.input-area');
687
+
688
+ if(!inputArea){
689
+ cleanup(container);
690
+ fail('Input area not found in shadow DOM');
691
+ return;
692
+ }
693
+
694
+ cleanup(container);
695
+ pass('Input area renders correctly');
696
+ },
697
+
698
+ 'should render messages in window': async ({pass, fail}) => {
699
+ const { container, chat } = await createChat();
700
+
701
+ chat.addMessage({ type: 'incoming', html: '<p>Message 1</p>' });
702
+ chat.addMessage({ type: 'outgoing', html: '<p>Message 2</p>' });
703
+
704
+ await chat.updateComplete;
705
+
706
+ const messages = chat.shadowRoot?.querySelectorAll('.message');
707
+
708
+ if(!messages || messages.length !== 2){
709
+ cleanup(container);
710
+ fail(`Expected 2 rendered messages, got ${messages?.length || 0}`);
711
+ return;
712
+ }
713
+
714
+ cleanup(container);
715
+ pass('Messages render in window');
716
+ },
717
+
718
+ 'should render incoming message with correct class': async ({pass, fail}) => {
719
+ const { container, chat } = await createChat();
720
+
721
+ chat.addMessage({ type: 'incoming', html: '<p>Hello</p>' });
722
+
723
+ await chat.updateComplete;
724
+
725
+ const message = chat.shadowRoot?.querySelector('.message');
726
+
727
+ if(!message?.classList.contains('incoming')){
728
+ cleanup(container);
729
+ fail('Incoming message should have "incoming" class');
730
+ return;
731
+ }
732
+
733
+ cleanup(container);
734
+ pass('Incoming message has correct class');
735
+ },
736
+
737
+ 'should render outgoing message with correct class': async ({pass, fail}) => {
738
+ const { container, chat } = await createChat();
739
+
740
+ chat.addMessage({ type: 'outgoing', html: '<p>Hi</p>' });
741
+
742
+ await chat.updateComplete;
743
+
744
+ const message = chat.shadowRoot?.querySelector('.message');
745
+
746
+ if(!message?.classList.contains('outgoing')){
747
+ cleanup(container);
748
+ fail('Outgoing message should have "outgoing" class');
749
+ return;
750
+ }
751
+
752
+ cleanup(container);
753
+ pass('Outgoing message has correct class');
754
+ },
755
+
756
+ 'should render sender name when provided': async ({pass, fail}) => {
757
+ const { container, chat } = await createChat();
758
+
759
+ chat.addMessage({ type: 'incoming', html: '<p>Message</p>', sender: 'Alice' });
760
+
761
+ await chat.updateComplete;
762
+
763
+ const sender = chat.shadowRoot?.querySelector('.sender');
764
+
765
+ if(!sender || sender.textContent !== 'Alice'){
766
+ cleanup(container);
767
+ fail('Sender name not rendered correctly');
768
+ return;
769
+ }
770
+
771
+ cleanup(container);
772
+ pass('Sender name renders correctly');
773
+ },
774
+
775
+ 'should not render sender when empty': async ({pass, fail}) => {
776
+ const { container, chat } = await createChat();
777
+
778
+ chat.addMessage({ type: 'incoming', html: '<p>Message</p>', sender: '' });
779
+
780
+ await chat.updateComplete;
781
+
782
+ const sender = chat.shadowRoot?.querySelector('.sender');
783
+
784
+ if(sender){
785
+ cleanup(container);
786
+ fail('Sender element should not render when empty');
787
+ return;
788
+ }
789
+
790
+ cleanup(container);
791
+ pass('Sender not rendered when empty');
792
+ },
793
+
794
+ 'should have send button in controls': async ({pass, fail}) => {
795
+ const { container, chat } = await createChat();
796
+
797
+ const sendBtn = chat.shadowRoot?.querySelector('.send-btn');
798
+
799
+ if(!sendBtn){
800
+ cleanup(container);
801
+ fail('Send button not found in controls');
802
+ return;
803
+ }
804
+
805
+ cleanup(container);
806
+ pass('Send button present in controls');
807
+ },
808
+
809
+ /*
810
+ Chat Component - Sanitization Tests
811
+ */
812
+ 'should sanitize message HTML': async ({pass, fail}) => {
813
+ const { container, chat } = await createChat();
814
+
815
+ chat.addMessage({
816
+ type: 'incoming',
817
+ html: '<p>Hello</p><script>alert("xss")</script>'
818
+ });
819
+
820
+ if(chat.messages[0].html.includes('script')){
821
+ cleanup(container);
822
+ fail('Script tags should be sanitized');
823
+ return;
824
+ }
825
+
826
+ cleanup(container);
827
+ pass('HTML sanitization works');
828
+ }
829
+ };