@chat21/chat21-web-widget 5.1.34-rc1 → 5.1.34

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.
Files changed (193) hide show
  1. package/.github/workflows/docker-community-push-latest.yml +13 -23
  2. package/.github/workflows/docker-image-tag-community-tag-push.yml +12 -22
  3. package/CHANGELOG.md +8 -129
  4. package/Dockerfile +5 -4
  5. package/angular.json +3 -21
  6. package/docs/changelog/this-branch.md +0 -36
  7. package/env.sample +2 -3
  8. package/nginx.conf +2 -22
  9. package/package.json +3 -10
  10. package/src/app/app.component.html +2 -2
  11. package/src/app/app.component.scss +14 -25
  12. package/src/app/app.component.spec.ts +6 -21
  13. package/src/app/app.component.ts +9 -10
  14. package/src/app/app.module.ts +0 -13
  15. package/src/app/component/conversation-detail/conversation/conversation.component.html +11 -25
  16. package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -40
  17. package/src/app/component/conversation-detail/conversation/conversation.component.spec.ts +75 -644
  18. package/src/app/component/conversation-detail/conversation/conversation.component.ts +14 -100
  19. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html +13 -25
  20. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.spec.ts +5 -123
  21. package/src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts +0 -1
  22. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +10 -23
  23. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +1 -19
  24. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +149 -242
  25. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.ts +5 -8
  26. package/src/app/component/conversation-detail/conversation-emojii/conversation-emojii.component.spec.ts +3 -53
  27. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +96 -200
  28. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +6 -211
  29. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +78 -452
  30. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +76 -291
  31. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.html +53 -113
  32. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.scss +4 -12
  33. package/src/app/component/conversation-detail/conversation-header/conversation-header.component.spec.ts +29 -274
  34. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html +9 -23
  35. package/src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.spec.ts +8 -80
  36. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html +23 -29
  37. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.spec.ts +16 -185
  38. package/src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts +14 -34
  39. package/src/app/component/error-alert/error-alert.component.spec.ts +5 -65
  40. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html +7 -16
  41. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss +0 -21
  42. package/src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.spec.ts +7 -89
  43. package/src/app/component/form/form-builder/form-builder.component.html +1 -1
  44. package/src/app/component/form/form-builder/form-builder.component.spec.ts +21 -163
  45. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.html +4 -8
  46. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.scss +5 -10
  47. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.spec.ts +16 -90
  48. package/src/app/component/form/inputs/form-checkbox/form-checkbox.component.ts +0 -26
  49. package/src/app/component/form/inputs/form-label/form-label.component.spec.ts +11 -45
  50. package/src/app/component/form/inputs/form-radio-button/form-radio-button.component.spec.ts +6 -24
  51. package/src/app/component/form/inputs/form-select/form-select.component.spec.ts +5 -14
  52. package/src/app/component/form/inputs/form-text/form-text.component.html +12 -14
  53. package/src/app/component/form/inputs/form-text/form-text.component.scss +1 -11
  54. package/src/app/component/form/inputs/form-text/form-text.component.spec.ts +17 -113
  55. package/src/app/component/form/inputs/form-text/form-text.component.ts +3 -35
  56. package/src/app/component/form/inputs/form-textarea/form-textarea.component.html +11 -13
  57. package/src/app/component/form/inputs/form-textarea/form-textarea.component.scss +5 -6
  58. package/src/app/component/form/inputs/form-textarea/form-textarea.component.spec.ts +13 -149
  59. package/src/app/component/form/inputs/form-textarea/form-textarea.component.ts +0 -26
  60. package/src/app/component/form/prechat-form/prechat-form.component.html +11 -14
  61. package/src/app/component/form/prechat-form/prechat-form.component.spec.ts +10 -102
  62. package/src/app/component/form/prechat-form/prechat-form.component.ts +1 -8
  63. package/src/app/component/home/home.component.html +31 -38
  64. package/src/app/component/home/home.component.scss +2 -4
  65. package/src/app/component/home/home.component.spec.ts +11 -226
  66. package/src/app/component/home-conversations/home-conversations.component.html +26 -30
  67. package/src/app/component/home-conversations/home-conversations.component.scss +0 -3
  68. package/src/app/component/home-conversations/home-conversations.component.spec.ts +36 -212
  69. package/src/app/component/last-message/last-message.component.html +9 -15
  70. package/src/app/component/last-message/last-message.component.scss +2 -16
  71. package/src/app/component/last-message/last-message.component.spec.ts +23 -204
  72. package/src/app/component/last-message/last-message.component.ts +1 -4
  73. package/src/app/component/launcher-button/launcher-button.component.html +13 -8
  74. package/src/app/component/launcher-button/launcher-button.component.spec.ts +8 -104
  75. package/src/app/component/list-all-conversations/list-all-conversations.component.html +17 -12
  76. package/src/app/component/list-all-conversations/list-all-conversations.component.scss +0 -2
  77. package/src/app/component/list-conversations/list-conversations.component.html +22 -22
  78. package/src/app/component/menu-options/menu-options.component.html +20 -30
  79. package/src/app/component/menu-options/menu-options.component.spec.ts +9 -125
  80. package/src/app/component/message/audio/audio.component.html +15 -13
  81. package/src/app/component/message/audio/audio.component.spec.ts +5 -140
  82. package/src/app/component/message/audio/audio.component.ts +5 -1
  83. package/src/app/component/message/avatar/avatar.component.html +2 -2
  84. package/src/app/component/message/avatar/avatar.component.spec.ts +7 -99
  85. package/src/app/component/message/bubble-message/bubble-message.component.html +51 -38
  86. package/src/app/component/message/bubble-message/bubble-message.component.scss +1 -54
  87. package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +57 -154
  88. package/src/app/component/message/bubble-message/bubble-message.component.ts +11 -89
  89. package/src/app/component/message/buttons/action-button/action-button.component.html +4 -3
  90. package/src/app/component/message/buttons/action-button/action-button.component.spec.ts +5 -49
  91. package/src/app/component/message/buttons/link-button/link-button.component.scss +8 -5
  92. package/src/app/component/message/buttons/link-button/link-button.component.spec.ts +5 -50
  93. package/src/app/component/message/buttons/text-button/text-button.component.spec.ts +5 -44
  94. package/src/app/component/message/carousel/carousel.component.html +16 -29
  95. package/src/app/component/message/carousel/carousel.component.scss +8 -20
  96. package/src/app/component/message/carousel/carousel.component.spec.ts +3 -80
  97. package/src/app/component/message/carousel/carousel.component.ts +0 -16
  98. package/src/app/component/message/frame/frame.component.html +4 -9
  99. package/src/app/component/message/frame/frame.component.spec.ts +15 -34
  100. package/src/app/component/message/frame/frame.component.ts +2 -7
  101. package/src/app/component/message/html/html.component.html +1 -1
  102. package/src/app/component/message/html/html.component.scss +1 -1
  103. package/src/app/component/message/html/html.component.spec.ts +7 -24
  104. package/src/app/component/message/image/image.component.html +10 -12
  105. package/src/app/component/message/image/image.component.scss +0 -16
  106. package/src/app/component/message/image/image.component.spec.ts +15 -101
  107. package/src/app/component/message/image/image.component.ts +51 -90
  108. package/src/app/component/message/info-message/info-message.component.spec.ts +14 -26
  109. package/src/app/component/message/like-unlike/like-unlike.component.html +9 -7
  110. package/src/app/component/message/like-unlike/like-unlike.component.spec.ts +3 -31
  111. package/src/app/component/message/return-receipt/return-receipt.component.spec.ts +17 -38
  112. package/src/app/component/message/text/text.component.html +3 -3
  113. package/src/app/component/message/text/text.component.scss +86 -80
  114. package/src/app/component/message/text/text.component.spec.ts +13 -106
  115. package/src/app/component/message-attachment/message-attachment.component.spec.ts +13 -134
  116. package/src/app/component/selection-department/selection-department.component.html +23 -21
  117. package/src/app/component/selection-department/selection-department.component.spec.ts +14 -159
  118. package/src/app/component/selection-department/selection-department.component.ts +1 -8
  119. package/src/app/component/send-button/send-button.component.html +13 -5
  120. package/src/app/component/send-button/send-button.component.spec.ts +2 -2
  121. package/src/app/component/star-rating-widget/star-rating-widget.component.html +81 -51
  122. package/src/app/directives/tooltip.directive.spec.ts +4 -8
  123. package/src/app/modals/confirm-close/confirm-close.component.html +8 -20
  124. package/src/app/modals/confirm-close/confirm-close.component.scss +0 -3
  125. package/src/app/modals/confirm-close/confirm-close.component.spec.ts +4 -13
  126. package/src/app/modals/confirm-close/confirm-close.component.ts +1 -8
  127. package/src/app/pipe/html-entites-encode.pipe.spec.ts +2 -35
  128. package/src/app/pipe/marked.pipe.spec.ts +2 -38
  129. package/src/app/pipe/marked.pipe.ts +41 -51
  130. package/src/app/providers/app-config.service.ts +2 -4
  131. package/src/app/providers/brand.service.spec.ts +2 -23
  132. package/src/app/providers/brand.service.ts +1 -1
  133. package/src/app/providers/global-settings.service.spec.ts +14 -1009
  134. package/src/app/providers/global-settings.service.ts +2 -82
  135. package/src/app/providers/translator.service.ts +6 -26
  136. package/src/app/sass/_variables.scss +0 -3
  137. package/src/app/sass/animations.scss +1 -19
  138. package/src/app/utils/globals.ts +1 -21
  139. package/src/app/utils/utils-resources.ts +1 -1
  140. package/src/assets/i18n/en.json +99 -106
  141. package/src/assets/i18n/es.json +100 -107
  142. package/src/assets/i18n/fr.json +100 -107
  143. package/src/assets/i18n/it.json +98 -107
  144. package/src/assets/twp/index-dev.html +0 -18
  145. package/src/chat21-core/models/message.ts +1 -2
  146. package/src/chat21-core/providers/firebase/firebase-conversation-handler.ts +2 -3
  147. package/src/chat21-core/providers/mqtt/mqtt-conversation-handler.ts +0 -12
  148. package/src/chat21-core/providers/scripts/script.service.spec.ts +2 -12
  149. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
  150. package/src/chat21-core/utils/utils-message.ts +0 -7
  151. package/src/chat21-core/utils/utils.ts +2 -5
  152. package/src/widget-config-template.json +1 -4
  153. package/src/widget-config.json +1 -4
  154. package/tsconfig.json +0 -5
  155. package/.angular-mcp-cache/package.json +0 -1
  156. package/.cursor/angular18-accessibility-auditor-skill.md +0 -442
  157. package/.cursor/mcp.json +0 -15
  158. package/.github/workflows/build.yml +0 -22
  159. package/.github/workflows/playwright.yml +0 -27
  160. package/mocks/voice-websocket-mock/server.cjs +0 -245
  161. package/playwright.config.ts +0 -41
  162. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.html +0 -46
  163. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.scss +0 -83
  164. package/src/app/component/conversation-detail/stream-audio-spectrum/stream-audio-spectrum.component.ts +0 -192
  165. package/src/app/component/form/prechat-form-test-mock.ts +0 -35
  166. package/src/app/component/message/audio-sync/audio-sync.component.html +0 -18
  167. package/src/app/component/message/audio-sync/audio-sync.component.scss +0 -65
  168. package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +0 -103
  169. package/src/app/component/message/audio-sync/audio-sync.component.ts +0 -643
  170. package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +0 -117
  171. package/src/app/providers/tts-audio-playback-coordinator.service.ts +0 -109
  172. package/src/app/providers/voice/STT&TTS/openai-voice.config.ts +0 -12
  173. package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +0 -171
  174. package/src/app/providers/voice/STT&TTS/speech-provider.abstract.ts +0 -39
  175. package/src/app/providers/voice/audio.types.ts +0 -40
  176. package/src/app/providers/voice/vad.service.spec.ts +0 -28
  177. package/src/app/providers/voice/vad.service.ts +0 -70
  178. package/src/app/providers/voice/voice-streaming.service.spec.ts +0 -23
  179. package/src/app/providers/voice/voice-streaming.service.ts +0 -702
  180. package/src/app/providers/voice/voice-streaming.types.ts +0 -112
  181. package/src/app/providers/voice/voice.service.spec.ts +0 -227
  182. package/src/app/providers/voice/voice.service.ts +0 -973
  183. package/src/app/shims/onnxruntime-web-wasm.ts +0 -4
  184. package/src/assets/onnx/ort-wasm-simd-threaded.mjs +0 -59
  185. package/src/assets/onnx/ort-wasm-simd-threaded.wasm +0 -0
  186. package/src/assets/sounds/keyboard.mp3 +0 -0
  187. package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +0 -14
  188. package/src/assets/vad/silero_vad_legacy.onnx +0 -0
  189. package/src/assets/vad/vad.worklet.bundle.min.js +0 -1
  190. package/src/chat21-core/providers/chat-manager.spec.ts +0 -72
  191. package/tests/widget-form-rich.spec.ts +0 -67
  192. package/tests/widget-index-dev-settings.spec.ts +0 -52
  193. package/tests/widget-twp-iframe.spec.ts +0 -39
@@ -1,72 +1,53 @@
1
- import { SimpleChange } from '@angular/core';
2
- import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
3
- import { provideHttpClientTesting } from '@angular/common/http/testing';
4
- import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
5
- import { BehaviorSubject } from 'rxjs';
1
+ import { async, ComponentFixture, TestBed, waitForAsync, inject } from '@angular/core/testing';
6
2
 
7
- import { UploadService } from 'src/chat21-core/providers/abstract/upload.service';
8
- import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
9
- import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
10
- import { NGXLogger } from 'ngx-logger';
11
- import { MAX_WIDTH_IMAGES } from 'src/app/utils/constants';
12
3
  import { ConversationContentComponent } from './conversation-content.component';
4
+ import { MarkedPipe } from '../../../pipe/marked.pipe';
5
+ import { HtmlEntitiesEncodePipe } from '../../../pipe/html-entities-encode.pipe';
6
+ import { UploadService } from '../../../../chat21-core/providers/abstract/upload.service';
7
+ import { CustomLogger } from '../../../../chat21-core/providers/logger/customLogger';
8
+ import { LoggerInstance } from '../../../../chat21-core/providers/logger/loggerInstance';
9
+ import { NO_ERRORS_SCHEMA, Injectable } from '@angular/core';
10
+ import { By } from '@angular/platform-browser';
11
+ import { ImageRepoService } from '../../../../chat21-core/providers/abstract/image-repo.service';
13
12
 
14
13
  describe('ConversationContentComponent', () => {
15
14
  let component: ConversationContentComponent;
16
15
  let fixture: ComponentFixture<ConversationContentComponent>;
17
- let uploadState$: BehaviorSubject<any>;
18
16
 
19
- const ngxlogger = jasmine.createSpyObj('NGXLogger', ['log', 'trace', 'debug', 'warn', 'error', 'info']);
20
- const customLogger = new CustomLogger(ngxlogger);
21
-
22
- const uploadServiceStub = {
23
- BSStateUpload: new BehaviorSubject<any>(null),
24
- initialize: jasmine.createSpy('initialize'),
25
- upload: jasmine.createSpy('upload').and.resolveTo({ downloadURL: '', src: '' }),
26
- uploadFile: jasmine.createSpy('uploadFile').and.resolveTo({ downloadURL: '', src: '' }),
27
- uploadAsset: jasmine.createSpy('uploadAsset').and.resolveTo({ downloadURL: '', src: '' }),
28
- uploadProfile: jasmine.createSpy('uploadProfile').and.resolveTo({}),
29
- delete: jasmine.createSpy('delete').and.resolveTo({}),
30
- deleteFile: jasmine.createSpy('deleteFile').and.resolveTo({}),
31
- deleteAsset: jasmine.createSpy('deleteAsset').and.resolveTo({}),
32
- deleteProfile: jasmine.createSpy('deleteProfile').and.resolveTo({}),
33
- setBaseUrl: jasmine.createSpy('setBaseUrl'),
34
- getBaseUrl: jasmine.createSpy('getBaseUrl').and.returnValue(''),
35
- };
36
-
37
- beforeEach(async () => {
38
- LoggerInstance.setInstance(customLogger);
39
-
40
- await TestBed.configureTestingModule({
41
- declarations: [ConversationContentComponent],
42
- providers: [
43
- { provide: UploadService, useValue: uploadServiceStub },
44
- provideHttpClient(withInterceptorsFromDi()),
45
- provideHttpClientTesting(),
17
+ beforeEach(waitForAsync(() => {
18
+ TestBed.configureTestingModule({
19
+ declarations: [
20
+ ConversationContentComponent,
21
+ // BubbleMessageComponent,
22
+ // ReturnReceiptComponent,
23
+ // AvatarComponent,
24
+ // InfoMessageComponent,
25
+ // MessageAttachmentComponent,
26
+ // ImageComponent,
27
+ // FrameComponent,
28
+ // TextComponent,
29
+ // TextButtonComponent,
30
+ // LinkButtonComponent,
31
+ // ActionButtonComponent,
32
+
33
+ MarkedPipe,
34
+ HtmlEntitiesEncodePipe,
35
+ ],
36
+ imports: [
37
+ ],
38
+ providers: [
39
+ UploadService,
40
+ ImageRepoService
46
41
  ],
42
+ schemas: [NO_ERRORS_SCHEMA],
47
43
  })
48
- .overrideComponent(ConversationContentComponent, {
49
- set: {
50
- template: `
51
- <div class="c21-body">
52
- <div #scrollMe>
53
- <div id="c21-contentScroll" style="height:20px;">inner</div>
54
- </div>
55
- </div>
56
- `,
57
- },
58
- })
59
- .compileComponents();
44
+ .compileComponents();
45
+ }));
60
46
 
61
- uploadState$ = uploadServiceStub.BSStateUpload as BehaviorSubject<any>;
47
+ beforeEach(() => {
62
48
  fixture = TestBed.createComponent(ConversationContentComponent);
63
- component = fixture.componentInstance;
64
- component.messages = [];
65
- component.stylesMap = new Map([
66
- ['bubbleSentTextColor', '#111'],
67
- ['bubbleReceivedTextColor', '#222'],
68
- ]);
69
- component.translationMap = new Map([['LABEL_LOADING', 'Loading']]);
49
+ let upload = fixture.debugElement.injector.get(UploadService) as UploadService
50
+ upload.BSStateUpload.next({ upload: 100, type: 'image' })
70
51
  fixture.detectChanges();
71
52
  });
72
53
 
@@ -74,194 +55,120 @@ describe('ConversationContentComponent', () => {
74
55
  expect(component).toBeTruthy();
75
56
  });
76
57
 
77
- describe('ngOnInit', () => {
78
- it('should subscribe to upload progress', () => {
79
- spyOn(component, 'scrollToBottom');
80
- uploadState$.next({ upload: 50, type: 'image/png' });
81
- expect(component.showUploadProgress).toBe(true);
82
- expect(component.uploadProgress).toBe(50);
83
- expect(component.scrollToBottom).toHaveBeenCalled();
84
- });
85
-
86
- it('should hide progress when upload is complete (100 or NaN)', () => {
87
- uploadState$.next({ upload: 100, type: 'image/png' });
88
- expect(component.showUploadProgress).toBe(false);
89
- });
58
+ it('renders a div el with class ".c21-body"', () => {
59
+ const bubble_message = fixture.debugElement.query(By.css('.c21-body'));
60
+ expect(bubble_message).toBeTruthy();
61
+ })
62
+
63
+ it('renders a div el with class ".base_receive"', () => {
64
+ const messages: [any] = [{
65
+ attributes: {
66
+ projectId: "6013ec749b32000045be650e",
67
+ tiledesk_message_id: "611cbf8ffb379b00346660e7"
68
+ },
69
+ channel_type: "group",
70
+ recipient: "support-group-6013ec749b32000045be650e-4904aee91f8b487aad117bcda860549d",
71
+ recipient_fullname: "Guest ",
72
+ sender: "bot_602256f6c001b800342cb76f",
73
+ sender_fullname: "BOT2",
74
+ status: 150,
75
+ text: "Hello 👋. I'm a bot 🤖.\n\nChoose one of the options below or write a message to reach our staff.",
76
+ timestamp: 1629273999970,
77
+ type: "text",
78
+ uid: "-MhNI3eaIoLTOLoX3TAu",
79
+ isSender: false
80
+ }
81
+ ]
82
+ component.messages = messages
83
+ component.senderId = '9d3b6aa5-0aea-4b7e-935f-1c1c675cd8d4'
84
+ component.baseLocation = 'http://tiledesk-widget-pre.s3-eu-west-1.amazonaws.com'
85
+ component.translationMap = new Map();
86
+ component.stylesMap = new Map();
87
+ fixture.detectChanges()
88
+ const nativeEl: HTMLElement = fixture.nativeElement;
89
+ const baseReceiveEl = nativeEl.querySelector('.base_receive');
90
+ expect(baseReceiveEl).toBeTruthy();
90
91
  });
91
92
 
92
- describe('ngOnChanges', () => {
93
- it('should set CSS variables on .c21-body from stylesMap', () => {
94
- const body = (fixture.debugElement.nativeElement as HTMLElement).querySelector('.c21-body') as HTMLElement;
95
- spyOn(body.style, 'setProperty');
96
- component.ngOnChanges({
97
- stylesMap: new SimpleChange(null, component.stylesMap, true),
98
- });
99
- expect(body.style.setProperty).toHaveBeenCalledWith('--textColorSent', '#111');
100
- expect(body.style.setProperty).toHaveBeenCalledWith('--textColorReceive', '#222');
101
- });
93
+ it('renders the right textContext of div el with class ".message_sender_fullname"', () => {
94
+ const messages: [any] = [{
95
+ attributes: {
96
+ projectId: "6013ec749b32000045be650e",
97
+ tiledesk_message_id: "611cbf8ffb379b00346660e7"
98
+ },
99
+ channel_type: "group",
100
+ recipient: "support-group-6013ec749b32000045be650e-4904aee91f8b487aad117bcda860549d",
101
+ recipient_fullname: "Guest ",
102
+ sender: "bot_602256f6c001b800342cb76f",
103
+ sender_fullname: "BOT2",
104
+ status: 150,
105
+ text: "Hello 👋. I'm a bot 🤖.\n\nChoose one of the options below or write a message to reach our staff.",
106
+ timestamp: 1629273999970,
107
+ type: "text",
108
+ uid: "-MhNI3eaIoLTOLoX3TAu",
109
+ isSender: false
110
+ }
111
+ ]
112
+ component.messages = messages
113
+ component.senderId = '9d3b6aa5-0aea-4b7e-935f-1c1c675cd8d4'
114
+ component.baseLocation = 'http://tiledesk-widget-pre.s3-eu-west-1.amazonaws.com'
115
+ component.translationMap = new Map();
116
+ component.stylesMap = new Map();
117
+ const nativeEl: HTMLElement = fixture.nativeElement;
118
+ const baseReceiveEl = nativeEl.querySelector('.message_sender_fullname');
119
+ fixture.detectChanges()
120
+ expect(baseReceiveEl.textContent).toBe('BOT2');
102
121
  });
103
122
 
104
- describe('getMetadataSize', () => {
105
- it('should default width/height and scale wide images', () => {
106
- const meta: any = { width: MAX_WIDTH_IMAGES * 2, height: 100 };
107
- const s = component.getMetadataSize(meta);
108
- expect(s.width).toBe(MAX_WIDTH_IMAGES);
109
- const ratio = meta.width / meta.height;
110
- expect(s.height).toBeCloseTo(MAX_WIDTH_IMAGES / ratio, 5);
111
- });
112
-
113
- it('should fill missing dimensions', () => {
114
- const s = component.getMetadataSize({});
115
- expect(s.width).toBe('100%');
116
- expect(s.height).toBe(MAX_WIDTH_IMAGES);
117
- });
118
- });
119
-
120
- describe('scroll and badge', () => {
121
- beforeEach(() => {
122
- const scrollHost = document.createElement('div');
123
- scrollHost.style.height = '100px';
124
- scrollHost.style.overflow = 'auto';
125
- const inner = document.createElement('div');
126
- inner.style.height = '300px';
127
- scrollHost.appendChild(inner);
128
- component.scrollMe = { nativeElement: scrollHost } as any;
129
- });
130
-
131
- it('onScroll should emit true at bottom', () => {
132
- spyOn(component.onScrollContent, 'emit');
133
- const el = component.scrollMe.nativeElement;
134
- el.scrollTop = el.scrollHeight - el.clientHeight;
135
- component.onScroll({ target: el });
136
- expect(component.onScrollContent.emit).toHaveBeenCalledWith(true);
137
- });
138
-
139
- it('checkContentScrollPosition should return false when not at bottom', () => {
140
- spyOn(component, 'checkContentScrollPosition').and.callThrough();
141
- const el = component.scrollMe.nativeElement;
142
- Object.defineProperty(el, 'scrollHeight', { configurable: true, value: 500 });
143
- Object.defineProperty(el, 'clientHeight', { configurable: true, value: 100 });
144
- el.scrollTop = 0;
145
- expect(component.checkContentScrollPosition(el)).toBe(false);
146
- });
147
-
148
- it('scrollToBottom should emit onScrollContent true', fakeAsync(() => {
149
- const wrap = document.createElement('div');
150
- wrap.style.height = '40px';
151
- wrap.style.overflow = 'auto';
152
- const inner = document.createElement('div');
153
- inner.id = 'c21-contentScroll';
154
- inner.style.height = '200px';
155
- wrap.appendChild(inner);
156
- document.body.appendChild(wrap);
157
- spyOn(component.onScrollContent, 'emit');
158
- component.scrollToBottom(true);
159
- tick(0);
160
- expect(component.onScrollContent.emit).toHaveBeenCalledWith(true);
161
- document.body.removeChild(wrap);
162
- }));
163
- });
164
-
165
- describe('emitters', () => {
166
- it('hideOutsideElements should close menus', () => {
167
- spyOn(component.onMenuOptionShow, 'emit');
168
- spyOn(component.onEmojiiPickerShow, 'emit');
169
- component.hideOutsideElements();
170
- expect(component.onMenuOptionShow.emit).toHaveBeenCalledWith(false);
171
- expect(component.onEmojiiPickerShow.emit).toHaveBeenCalledWith(false);
172
- });
173
-
174
- it('onAttachmentButtonClickedFN should forward event', () => {
175
- spyOn(component.onAttachmentButtonClicked, 'emit');
176
- const ev = { a: 1 };
177
- component.onAttachmentButtonClickedFN(ev);
178
- expect(component.onAttachmentButtonClicked.emit).toHaveBeenCalledWith(ev);
179
- });
180
-
181
- it('onBeforeMessageRenderFN / onAfterMessageRenderFN should emit', () => {
182
- spyOn(component.onBeforeMessageRender, 'emit');
183
- spyOn(component.onAfterMessageRender, 'emit');
184
- const ev = { x: 'y' };
185
- component.onBeforeMessageRenderFN(ev);
186
- component.onAfterMessageRenderFN(ev);
187
- expect(component.onBeforeMessageRender.emit).toHaveBeenCalledWith(ev);
188
- expect(component.onAfterMessageRender.emit).toHaveBeenCalledWith(ev);
189
- });
190
-
191
- it('onElementRenderedFN with status should call scrollToBottom', () => {
192
- spyOn(component, 'scrollToBottom');
193
- component.scrollMe = { nativeElement: document.createElement('div') } as any;
194
- component.onElementRenderedFN({ status: true });
195
- expect(component.scrollToBottom).toHaveBeenCalled();
196
- });
197
-
198
- it('onElementRenderedFN with status false should not scroll', () => {
199
- spyOn(component, 'scrollToBottom');
200
- component.scrollMe = { nativeElement: document.createElement('div') } as any;
201
- component.onElementRenderedFN({ status: false });
202
- expect(component.scrollToBottom).not.toHaveBeenCalled();
203
- });
204
- });
205
-
206
- describe('upload observable edge cases', () => {
207
- it('should ignore null BSStateUpload payloads', () => {
208
- component.showUploadProgress = true;
209
- uploadState$.next(null);
210
- expect(component.showUploadProgress).toBe(true);
211
- });
212
-
213
- it('should treat NaN upload as complete for progress UI', () => {
214
- uploadState$.next({ upload: NaN, type: 'image/png' });
215
- expect(component.showUploadProgress).toBe(false);
216
- });
217
- });
218
-
219
- describe('onScroll without ViewChild', () => {
220
- it('should not emit when scrollMe is missing', () => {
221
- spyOn(component.onScrollContent, 'emit');
222
- component.scrollMe = undefined as any;
223
- component.onScroll({ target: document.createElement('div') });
224
- expect(component.onScrollContent.emit).not.toHaveBeenCalled();
225
- });
226
- });
227
-
228
- describe('scrollToBottom error path (sync)', () => {
229
- it('should log when getElementById throws synchronously', () => {
230
- spyOn(document, 'getElementById').and.throwError('no-dom');
231
- spyOn((component as any).logger, 'error');
232
- component.scrollToBottom();
233
- expect((component as any).logger.error).toHaveBeenCalled();
234
- });
235
- });
236
-
237
- describe('message helper delegates', () => {
238
- it('isLastMessage / isSameSender / isFirstMessage use messages array', () => {
239
- component.messages = [
240
- { uid: 'm1', sender: 'alice' },
241
- { uid: 'm2', sender: 'alice' },
242
- ] as any;
243
- expect(component.isLastMessage('m2')).toBe(true);
244
- expect(component.isSameSender('alice', 1)).toBe(true);
245
- expect(component.isFirstMessage('alice', 0)).toBe(true);
246
- });
247
- });
248
-
249
- describe('getMetadataSize string width', () => {
250
- it('should not scale when width is non-numeric string', () => {
251
- const s = component.getMetadataSize({ width: '100%', height: 200 });
252
- expect(s.width).toBe('100%');
253
- });
254
- });
255
-
256
- describe('ngOnChanges styles edge', () => {
257
- it('should not touch DOM when bubble color keys are absent', () => {
258
- const spy = spyOn((component as any).elementRef.nativeElement, 'querySelector');
259
- component.stylesMap = new Map();
260
- component.ngOnChanges({
261
- stylesMap: new SimpleChange(null, component.stylesMap, false),
262
- });
263
- expect(spy).not.toHaveBeenCalled();
264
- });
123
+ it('renders a chat-avatar-image & chat-bubble-message components in div with ".base_receive" class', () => {
124
+ const messages: Array<any> = [{
125
+ attributes: {
126
+ projectId: "6013ec749b32000045be650e",
127
+ tiledesk_message_id: "611cbf8ffb379b00346660e7"
128
+ },
129
+ channel_type: "group",
130
+ recipient: "support-group-6013ec749b32000045be650e-4904aee91f8b487aad117bcda860549d",
131
+ recipient_fullname: "Guest ",
132
+ sender: "bot_602256f6c001b800342cb76f",
133
+ sender_fullname: "BOT2",
134
+ status: 150,
135
+ text: "Hello 👋. I'm a bot 🤖.\n\nChoose one of the options below or write a message to reach our staff.",
136
+ timestamp: 1629273999970,
137
+ type: "text",
138
+ uid: "-MhNI3eaIoLTOLoX3TAu",
139
+ isSender: false
140
+ },
141
+ {
142
+ attributes: {
143
+ projectId: "6013ec749b32000045be650e",
144
+ tiledesk_message_id: "611cbf8ffb379b00346660e7"
145
+ },
146
+ channel_type: "group",
147
+ recipient: "support-group-6013ec749b32000045be650e-4904aee91f8b487aad117bcda860549d",
148
+ recipient_fullname: "Guest ",
149
+ sender: "bot_602256f6c001b800342cb76f",
150
+ sender_fullname: "BOT1",
151
+ status: 150,
152
+ text: "Hello 👋. I'm a bot 🤖.\n\nChoose one of the options below or write a message to reach our staff.",
153
+ timestamp: 1629273999970,
154
+ type: "text",
155
+ uid: "-MhNI3eaIoLTOLoX3TAu",
156
+ isSender: false
157
+ }
158
+ ]
159
+ component.messages = messages
160
+ component.senderId = '9d3b6aa5-0aea-4b7e-935f-1c1c675cd8d4'
161
+ component.baseLocation = 'http://tiledesk-widget-pre.s3-eu-west-1.amazonaws.com'
162
+ component.translationMap = new Map();
163
+ component.stylesMap = new Map();
164
+
165
+ const nativeEl: HTMLElement = fixture.nativeElement;
166
+ const baseReceiveEl = nativeEl.querySelectorAll('.base_receive')
167
+ const chatImageComponentChild = baseReceiveEl[0].querySelector('chat-avatar-image')
168
+ const bubbleMessageComponentChild = baseReceiveEl[0].querySelector('chat-bubble-message.msg_receive')
169
+ fixture.detectChanges()
170
+ expect(chatImageComponentChild).toBeTruthy();
171
+ expect(bubbleMessageComponentChild).toBeTruthy();
265
172
  });
266
173
 
267
174
  });
@@ -1,5 +1,4 @@
1
- import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, OnDestroy, Output, SimpleChanges, ViewChild } from '@angular/core';
2
- import { Subscription } from 'rxjs';
1
+ import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
3
2
  import { MAX_WIDTH_IMAGES, MSG_STATUS_RETURN_RECEIPT, MSG_STATUS_SENT, MSG_STATUS_SENT_SERVER } from 'src/app/utils/constants';
4
3
  import { MessageModel } from 'src/chat21-core/models/message';
5
4
  import { LoggerService } from 'src/chat21-core/providers/abstract/logger.service';
@@ -13,7 +12,7 @@ import { isCarousel, isEmojii, isFirstMessage, isFrame, isImage, isInfo, isLastM
13
12
  templateUrl: './conversation-content.component.html',
14
13
  styleUrls: ['./conversation-content.component.scss']
15
14
  })
16
- export class ConversationContentComponent implements OnInit, OnDestroy {
15
+ export class ConversationContentComponent implements OnInit {
17
16
  @ViewChild('scrollMe') public scrollMe: ElementRef;
18
17
 
19
18
  @Input() messages: MessageModel[]
@@ -27,7 +26,6 @@ export class ConversationContentComponent implements OnInit, OnDestroy {
27
26
  @Input() showThinkingMessage: boolean;
28
27
  @Input() lastServerSenderKind: 'bot' | 'human' | null;
29
28
  @Input() fullscreenMode: boolean;
30
- @Input() isStreamAudioActive: boolean;
31
29
  @Input() translationMap: Map< string, string>;
32
30
  @Input() stylesMap: Map<string, string>;
33
31
  @Output() onBeforeMessageRender = new EventEmitter();
@@ -74,7 +72,6 @@ export class ConversationContentComponent implements OnInit, OnDestroy {
74
72
  showUploadProgress: boolean = false;
75
73
  fileType: string;
76
74
  private logger: LoggerService = LoggerInstance.getInstance();
77
- private uploadSub?: Subscription;
78
75
 
79
76
  constructor(private cdref: ChangeDetectorRef,
80
77
  private elementRef: ElementRef,
@@ -84,8 +81,8 @@ export class ConversationContentComponent implements OnInit, OnDestroy {
84
81
  this.listenToUploadFileProgress();
85
82
  }
86
83
 
87
- ngOnDestroy() {
88
- this.uploadSub?.unsubscribe();
84
+ ngAfterContentChecked() {
85
+ this.cdref.detectChanges();
89
86
  }
90
87
 
91
88
  ngOnChanges(changes: SimpleChanges){
@@ -123,7 +120,7 @@ export class ConversationContentComponent implements OnInit, OnDestroy {
123
120
 
124
121
  // ENABLE HTML SECTION 'FILE PENDING UPLOAD'
125
122
  listenToUploadFileProgress() {
126
- this.uploadSub = this.uploadService.BSStateUpload.subscribe((data: any) => {
123
+ this.uploadService.BSStateUpload.subscribe((data: any) => {
127
124
  this.logger.debug('[CONV-CONTENT] BSStateUpload', data);
128
125
  // && data.type.startsWith("application")
129
126
  if (data) {
@@ -1,73 +1,23 @@
1
1
  import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { provideHttpClientTesting } from '@angular/common/http/testing';
3
- import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
4
2
 
5
3
  import { ConversationEmojiiComponent } from './conversation-emojii.component';
6
4
 
7
- /** Template references `stylesMap`, which is not declared on the component; use a shallow template for unit tests. */
8
5
  describe('ConversationEmojiiComponent', () => {
9
6
  let component: ConversationEmojiiComponent;
10
7
  let fixture: ComponentFixture<ConversationEmojiiComponent>;
11
8
 
12
9
  beforeEach(async () => {
13
10
  await TestBed.configureTestingModule({
14
- declarations: [ConversationEmojiiComponent],
15
- providers: [
16
- provideHttpClient(withInterceptorsFromDi()),
17
- provideHttpClientTesting(),
18
- ],
11
+ declarations: [ ConversationEmojiiComponent ]
19
12
  })
20
- .overrideComponent(ConversationEmojiiComponent, {
21
- set: { template: '<span></span>' },
22
- })
23
- .compileComponents();
13
+ .compileComponents();
24
14
 
25
15
  fixture = TestBed.createComponent(ConversationEmojiiComponent);
26
16
  component = fixture.componentInstance;
17
+ fixture.detectChanges();
27
18
  });
28
19
 
29
20
  it('should create', () => {
30
- fixture.detectChanges();
31
21
  expect(component).toBeTruthy();
32
22
  });
33
-
34
- describe('ngOnInit', () => {
35
- it('should initialise default emoji-mart options', () => {
36
- component.ngOnInit();
37
- expect(component.emojiiOptions.emojiPerLine).toBe(9);
38
- expect(component.emojiiOptions.include).toContain('people');
39
- });
40
- });
41
-
42
- describe('addEmojiFN', () => {
43
- it('should emit addEmoji when picker selects an emoji', () => {
44
- let emitted: unknown;
45
- component.addEmoji.subscribe((e) => (emitted = e));
46
- const payload = { emoji: { native: '🙂' } };
47
- component.addEmojiFN(payload);
48
- expect(emitted).toEqual(payload);
49
- });
50
-
51
- it('should emit multiple selections to all subscribers', () => {
52
- const out: unknown[] = [];
53
- component.addEmoji.subscribe((e) => out.push(e));
54
- component.addEmoji.subscribe((e) => out.push(e));
55
- const p = { emoji: { native: '🎯' } };
56
- component.addEmojiFN(p);
57
- expect(out).toEqual([p, p]);
58
- });
59
-
60
- it('should forward undefined-like payloads without throwing', () => {
61
- spyOn(component.addEmoji, 'emit');
62
- component.addEmojiFN(undefined);
63
- expect(component.addEmoji.emit).toHaveBeenCalledWith(undefined);
64
- });
65
- });
66
-
67
- describe('@Input var', () => {
68
- it('should accept custom picker label', () => {
69
- component.var = 'picker-a';
70
- expect(component.var).toBe('picker-a');
71
- });
72
- });
73
23
  });