@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,290 +1,45 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { provideHttpClientTesting } from '@angular/common/http/testing';
3
1
  import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
4
- import { HttpTestingController } from '@angular/common/http/testing';
2
+ import { AppConfigService } from './../../../providers/app-config.service';
3
+ import { Globals } from './../../../utils/globals';
4
+ import { NO_ERRORS_SCHEMA } from '@angular/core';
5
+ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
5
6
 
6
- import { AppConfigService } from '../../../providers/app-config.service';
7
- import { HEADER_MENU_OPTION } from '../../../utils/constants';
7
+ import { ConversationHeaderComponent } from './conversation-header.component';
8
+ import { TypingService } from '../../../../chat21-core/providers/abstract/typing.service';
8
9
  import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
9
- import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
10
10
  import { NGXLogger } from 'ngx-logger';
11
- import { ConversationHeaderComponent } from './conversation-header.component';
11
+ import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
12
12
 
13
13
  describe('ConversationHeaderComponent', () => {
14
14
  let component: ConversationHeaderComponent;
15
15
  let fixture: ComponentFixture<ConversationHeaderComponent>;
16
- let httpMock: HttpTestingController;
17
-
18
- const ngxlogger = jasmine.createSpyObj('NGXLogger', ['log', 'trace', 'debug', 'warn', 'error', 'info']);
19
- const customLogger = new CustomLogger(ngxlogger);
20
-
21
- const apiUrl = 'https://api.example.com/';
22
- const appConfigStub = {
23
- getConfig: jasmine.createSpy('getConfig').and.returnValue({ apiUrl }),
24
- };
25
-
26
- beforeEach(async () => {
27
- LoggerInstance.setInstance(customLogger);
28
-
29
- await TestBed.configureTestingModule({
30
- declarations: [ConversationHeaderComponent],
31
- providers: [
32
- { provide: AppConfigService, useValue: appConfigStub },
33
- provideHttpClient(withInterceptorsFromDi()),
34
- provideHttpClientTesting(),
35
- ],
36
- })
37
- .overrideComponent(ConversationHeaderComponent, {
38
- set: {
39
- template: `
40
- <button class="back" (click)="returnHome()">back</button>
41
- <button class="close-widget" (click)="closeWidget()">closeW</button>
42
- <button class="toggle-menu" (click)="toggleMenu()">menu</button>
43
- <button class="close-chat" (click)="closeChat()">closeChat</button>
44
- <button class="restart" (click)="restartChat()">restart</button>
45
- <button class="detail" (click)="openDetail()">detail</button>
46
- <button class="signout" (click)="signOut()">signout</button>
47
- <button class="sound" (click)="toggleSound()">sound</button>
48
- <button class="max" (click)="onChangeSize('max')">max</button>
49
- <button class="min" (click)="onChangeSize('min')">min</button>
50
- <button class="top" (click)="onChangeSize('top')">top</button>
51
- <button class="transcript" (click)="dowloadTranscript()">transcript</button>
52
- `,
53
- },
54
- })
55
- .compileComponents();
56
-
16
+ let ngxlogger: NGXLogger;
17
+ let customLogger = new CustomLogger(ngxlogger)
18
+
19
+ beforeEach(waitForAsync(() => {
20
+ TestBed.configureTestingModule({
21
+ declarations: [ConversationHeaderComponent],
22
+ schemas: [NO_ERRORS_SCHEMA],
23
+ imports: [],
24
+ providers: [
25
+ Globals,
26
+ TypingService,
27
+ AppConfigService,
28
+ provideHttpClient(withInterceptorsFromDi())
29
+ ]
30
+ })
31
+ .compileComponents();
32
+ }));
33
+
34
+ beforeEach(() => {
57
35
  fixture = TestBed.createComponent(ConversationHeaderComponent);
58
36
  component = fixture.componentInstance;
59
- httpMock = TestBed.inject(HttpTestingController);
60
- component.translationMap = new Map([['OPTIONS', 'Opt']]);
61
- component.senderId = 'sender-1';
62
- component.idConversation = 'conv-1';
63
- component.windowContext = window;
64
- fixture.detectChanges();
65
- });
66
-
67
- afterEach(() => {
68
- httpMock.verify();
37
+ LoggerInstance.setInstance(customLogger)
38
+ let logger = LoggerInstance.getInstance()
39
+ component['logger']= logger
69
40
  });
70
41
 
71
42
  it('should create', () => {
72
43
  expect(component).toBeTruthy();
73
44
  });
74
-
75
- describe('constructor / ngOnInit', () => {
76
- it('should read apiUrl from AppConfigService', () => {
77
- expect((component as any).API_URL).toBe(apiUrl);
78
- });
79
-
80
- it('ngOnInit should add senderId to membersConversation', () => {
81
- const m = new ConversationHeaderComponent(appConfigStub as unknown as AppConfigService);
82
- m.senderId = 'u1';
83
- m.translationMap = new Map();
84
- m.ngOnInit();
85
- expect(m.membersConversation).toContain('u1');
86
- });
87
- });
88
-
89
- describe('ngOnChanges', () => {
90
- it('should run when idConversation is set', () => {
91
- component.idConversation = 'new-id';
92
- component.ngOnChanges({
93
- idConversation: {
94
- previousValue: undefined,
95
- currentValue: 'new-id',
96
- firstChange: true,
97
- isFirstChange: () => true,
98
- },
99
- });
100
- expect(component).toBeTruthy();
101
- });
102
- });
103
-
104
- describe('ngAfterViewInit', () => {
105
- it('should enable header buttons', () => {
106
- component.isButtonsDisabled = true;
107
- component.ngAfterViewInit();
108
- expect(component.isButtonsDisabled).toBe(false);
109
- });
110
- });
111
-
112
- describe('click handlers / outputs', () => {
113
- it('returnHome should emit onBack', () => {
114
- spyOn(component.onBack, 'emit');
115
- component.returnHome();
116
- expect(component.onBack.emit).toHaveBeenCalled();
117
- });
118
-
119
- it('closeWidget should emit onCloseWidget', () => {
120
- spyOn(component.onCloseWidget, 'emit');
121
- component.closeWidget();
122
- expect(component.onCloseWidget.emit).toHaveBeenCalled();
123
- });
124
-
125
- it('toggleMenu should flip menu visibility', () => {
126
- spyOn(component.onMenuOptionShow, 'emit');
127
- component.isMenuShow = false;
128
- component.toggleMenu();
129
- expect(component.onMenuOptionShow.emit).toHaveBeenCalledWith(true);
130
- });
131
-
132
- it('closeChat should emit CLOSE option', () => {
133
- spyOn(component.onMenuOptionClick, 'emit');
134
- component.closeChat();
135
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.CLOSE);
136
- });
137
-
138
- it('restartChat should emit RESTART and close menu', () => {
139
- spyOn(component.onMenuOptionClick, 'emit');
140
- spyOn(component.onMenuOptionShow, 'emit');
141
- component.restartChat();
142
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.RESTART);
143
- expect(component.onMenuOptionShow.emit).toHaveBeenCalledWith(false);
144
- });
145
-
146
- it('openDetail should emit DETAIL', () => {
147
- spyOn(component.onMenuOptionClick, 'emit');
148
- component.openDetail();
149
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.DETAIL);
150
- });
151
-
152
- it('signOut should emit LOGOUT', () => {
153
- spyOn(component.onMenuOptionClick, 'emit');
154
- component.signOut();
155
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.LOGOUT);
156
- });
157
-
158
- it('onChangeSize max should emit MAXIMIZE and close menu', () => {
159
- spyOn(component.onMenuOptionClick, 'emit');
160
- spyOn(component.onMenuOptionShow, 'emit');
161
- component.onChangeSize('max');
162
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.MAXIMIZE);
163
- expect(component.onMenuOptionShow.emit).toHaveBeenCalledWith(false);
164
- });
165
-
166
- it('toggleSound should emit volume option and flip soundEnabled', () => {
167
- spyOn(component.onMenuOptionClick, 'emit');
168
- spyOn(component.onMenuOptionShow, 'emit');
169
- component.soundEnabled = false;
170
- component.toggleSound();
171
- expect(component.soundEnabled).toBe(true);
172
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.VOLUME_ON);
173
- component.toggleSound();
174
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.VOLUME_OFF);
175
- });
176
- });
177
-
178
- describe('dowloadTranscript', () => {
179
- it('should open transcript URL in windowContext', () => {
180
- const openSpy = jasmine.createSpy('open');
181
- component.windowContext = { open: openSpy };
182
- component.idConversation = 'req-99';
183
- component.dowloadTranscript();
184
- expect(openSpy).toHaveBeenCalledWith(apiUrl + 'public/requests/req-99/messages-user.html', '_blank');
185
- });
186
- });
187
-
188
- describe('DOM integration', () => {
189
- it('should wire back button', () => {
190
- spyOn(component, 'returnHome');
191
- const btn = fixture.nativeElement.querySelector('.back');
192
- btn.click();
193
- expect(component.returnHome).toHaveBeenCalled();
194
- });
195
- });
196
-
197
- describe('ngOnDestroy', () => {
198
- it('should clear subscription entries when subscriptions use value.unsubscribe', () => {
199
- const unsub = jasmine.createSpy('unsubscribe');
200
- (component as any).subscriptions = [{ value: { unsubscribe: unsub } }];
201
- component.ngOnDestroy();
202
- expect(unsub).toHaveBeenCalled();
203
- expect((component as any).subscriptions.length).toBe(0);
204
- });
205
-
206
- it('should not throw when subscriptions is empty', () => {
207
- (component as any).subscriptions = [];
208
- expect(() => component.ngOnDestroy()).not.toThrow();
209
- });
210
- });
211
-
212
- describe('onChangeSize edge cases', () => {
213
- beforeEach(() => {
214
- spyOn(component.onMenuOptionClick, 'emit');
215
- spyOn(component.onMenuOptionShow, 'emit');
216
- });
217
-
218
- it('should emit MINIMIZE for min', () => {
219
- component.onChangeSize('min');
220
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.MINIMIZE);
221
- });
222
-
223
- it('should emit TOP for top', () => {
224
- component.onChangeSize('top');
225
- expect(component.onMenuOptionClick.emit).toHaveBeenCalledWith(HEADER_MENU_OPTION.TOP);
226
- });
227
-
228
- it('should emit nothing for unknown status (only menu close)', () => {
229
- (component.onMenuOptionClick.emit as jasmine.Spy).calls.reset();
230
- component.onChangeSize('unknown' as any);
231
- expect(component.onMenuOptionClick.emit).not.toHaveBeenCalled();
232
- expect(component.onMenuOptionShow.emit).toHaveBeenCalledWith(false);
233
- });
234
- });
235
-
236
- describe('toggleMenu observable-style flips', () => {
237
- it('should emit negation when menu already open', () => {
238
- spyOn(component.onMenuOptionShow, 'emit');
239
- component.isMenuShow = true;
240
- component.toggleMenu();
241
- expect(component.onMenuOptionShow.emit).toHaveBeenCalledWith(false);
242
- });
243
- });
244
-
245
- describe('dowloadTranscript error handling', () => {
246
- it('should propagate open failure and not emit menu close after throw', () => {
247
- spyOn(component.onMenuOptionShow, 'emit');
248
- component.windowContext = {
249
- open: jasmine.createSpy('open').and.throwError('blocked'),
250
- };
251
- expect(() => component.dowloadTranscript()).toThrow();
252
- expect(component.onMenuOptionShow.emit).not.toHaveBeenCalled();
253
- });
254
- });
255
-
256
- describe('DOM click integration (handlers)', () => {
257
- const clickClass = (cls: string) => (fixture.nativeElement as HTMLElement).querySelector(cls)?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
258
-
259
- it('wires close-widget, toggle-menu, transcript', () => {
260
- spyOn(component, 'closeWidget');
261
- spyOn(component, 'toggleMenu');
262
- spyOn(component, 'dowloadTranscript');
263
- clickClass('.close-widget');
264
- clickClass('.toggle-menu');
265
- clickClass('.transcript');
266
- expect(component.closeWidget).toHaveBeenCalled();
267
- expect(component.toggleMenu).toHaveBeenCalled();
268
- expect(component.dowloadTranscript).toHaveBeenCalled();
269
- });
270
-
271
- it('wires sound and resize buttons', () => {
272
- spyOn(component, 'toggleSound');
273
- spyOn(component, 'onChangeSize');
274
- clickClass('.sound');
275
- clickClass('.max');
276
- expect(component.toggleSound).toHaveBeenCalled();
277
- expect(component.onChangeSize).toHaveBeenCalledWith('max');
278
- });
279
- });
280
-
281
- describe('ngOnInit edge', () => {
282
- it('should push undefined senderId when missing', () => {
283
- const h = new ConversationHeaderComponent(appConfigStub as unknown as AppConfigService);
284
- h.senderId = undefined as any;
285
- h.translationMap = new Map();
286
- h.ngOnInit();
287
- expect(h.membersConversation.length).toBeGreaterThan(1);
288
- });
289
- });
290
45
  });
@@ -1,25 +1,20 @@
1
1
  <div class="c21-iframe-container" [@enterAnimation]>
2
+ <!-- 'background-color': stylesMap?.get('backgroundColor'), 'color': styleMap?.get('foregroundColor'), 'background-image': 'linear-gradient( 180grad,' + stylesMap?.get('themeColor')+','+ convertColorToRGBA(stylesMap?.get('themeColor'), 50)+')' -->
2
3
  <div class="c21-iframe-header c21-header" [ngStyle]="{ 'background-image': stylesMap?.get('colorGradient')}">
3
-
4
+
4
5
  <!-- ICON BACK CHAT -->
5
- <button type="button"
6
- class="c21-header-button c21-left c21-button-clean"
7
- [attr.aria-label]="translationMap?.get('CLOSE') || 'Close'"
8
- (click)="returnClose()">
9
- <svg aria-hidden="true" focusable="false" [ngStyle]="{'fill': stylesMap?.get('foregroundColor') }" xmlns="http://www.w3.org/2000/svg"
6
+ <button tabindex="1530" class="c21-header-button c21-left c21-button-clean" (click)="returnClose()">
7
+ <svg role="img" aria-labelledby="altIconTitle" [ngStyle]="{'fill': stylesMap?.get('foregroundColor') }" xmlns="http://www.w3.org/2000/svg"
10
8
  width="24px" height="24px" viewBox="0 0 24 24">
11
9
  <path fill="none" d="M0 0h24v24H0V0z" />
12
10
  <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12l4.58-4.59z" />
11
+ <title id="altIconTitle">{{ translationMap?.get('CLOSE') }}</title>
13
12
  </svg>
14
13
  </button>
15
14
 
16
15
  <!-- ICON OPEN EXTERNAL -->
17
- <button *ngIf="openExternalLinkButton"
18
- type="button"
19
- class="c21-header-button c21-right c21-button-clean"
20
- aria-label="Open in a new window"
21
- (click)="returnOpenExternal()">
22
- <svg aria-hidden="true" focusable="false" [ngStyle]="{'fill': stylesMap?.get('foregroundColor') }" xmlns="http://www.w3.org/2000/svg"
16
+ <button *ngIf="openExternalLinkButton" tabindex="1530" class="c21-header-button c21-right c21-button-clean" (click)="returnOpenExternal()">
17
+ <svg role="img" aria-labelledby="altIconTitle" [ngStyle]="{'fill': stylesMap?.get('foregroundColor') }" xmlns="http://www.w3.org/2000/svg"
23
18
  width="24px" height="24px" viewBox="0 0 24 24">
24
19
  <path d="M0 0h24v24H0z" fill="none"/>
25
20
  <path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/>
@@ -32,19 +27,10 @@
32
27
  </div>
33
28
  </div>
34
29
  <div class="c21-iframe">
35
- <div *ngIf="!hideSpinner" class="lds-roller" aria-hidden="true">
30
+ <div *ngIf="!hideSpinner" class="lds-roller">
36
31
  <div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
37
32
  </div>
38
33
 
39
- <iframe id="contentFrame" #iframe
40
- allowfullscreen
41
- [title]="button?.value || 'Embedded conversation panel'"
42
- sandbox="allow-scripts allow-same-origin allow-popups allow-forms allow-popups-to-escape-sandbox"
43
- referrerpolicy="strict-origin-when-cross-origin"
44
- loading="lazy"
45
- width="100%" height="100%" style="border:none;"
46
- [src]="url"
47
- (load)="onIframeLoaded($event)"
48
- (error)="onError($event)"></iframe>
34
+ <iframe id="contentFrame" #iframe allowfullscreen title="" width="100%" height="100%" style= "border:none;" [src] = "url" (load)="onIframeLoaded($event)" (error)="onError($event)"></iframe>
49
35
  </div>
50
36
  </div>
@@ -1,101 +1,29 @@
1
1
  import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
2
- import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+ import { async, ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
3
3
 
4
4
  import { ConversationInternalFrameComponent } from './conversation-internal-frame.component';
5
- import { CustomLogger } from 'src/chat21-core/providers/logger/customLogger';
6
- import { LoggerInstance } from 'src/chat21-core/providers/logger/loggerInstance';
7
- describe('ConversationInternalFrameComponent', () => {
5
+
6
+ describe('InterlalFrameComponent', () => {
8
7
  let component: ConversationInternalFrameComponent;
9
8
  let fixture: ComponentFixture<ConversationInternalFrameComponent>;
10
- const ngxlogger = jasmine.createSpyObj('NGXLogger', ['log', 'trace', 'debug', 'warn', 'error', 'info']);
11
- const customLogger = new CustomLogger(ngxlogger);
12
9
 
13
10
  beforeEach(waitForAsync(() => {
14
- LoggerInstance.setInstance(customLogger);
15
11
  TestBed.configureTestingModule({
16
- declarations: [ConversationInternalFrameComponent],
17
- imports: [BrowserAnimationsModule],
12
+ declarations: [ ConversationInternalFrameComponent ],
13
+ imports: [
14
+ BrowserAnimationsModule
15
+ ]
18
16
  })
19
- .overrideComponent(ConversationInternalFrameComponent, {
20
- set: {
21
- template: `
22
- <iframe #iframe title="inner" src="about:blank"></iframe>
23
- <button type="button" (click)="returnClose()">close</button>
24
- <button type="button" (click)="returnOpenExternal()">ext</button>
25
- `,
26
- },
27
- })
28
- .compileComponents();
17
+ .compileComponents();
29
18
  }));
30
19
 
31
20
  beforeEach(() => {
32
21
  fixture = TestBed.createComponent(ConversationInternalFrameComponent);
33
22
  component = fixture.componentInstance;
34
- component.button = { link: 'https://example.com/path', value: 'Test' };
35
- component.translationMap = new Map();
36
- component.stylesMap = new Map();
37
23
  fixture.detectChanges();
38
24
  });
39
25
 
40
26
  it('should create', () => {
41
27
  expect(component).toBeTruthy();
42
28
  });
43
-
44
- describe('ngOnInit', () => {
45
- it('should trust resource URL from button.link', () => {
46
- expect(component.url).toBeDefined();
47
- });
48
-
49
- it('should leave url unset when button has no link', () => {
50
- const f = TestBed.createComponent(ConversationInternalFrameComponent);
51
- const c = f.componentInstance;
52
- c.button = { value: 'x' };
53
- c.ngOnInit();
54
- expect(c.url).toBeUndefined();
55
- f.destroy();
56
- });
57
- });
58
-
59
- describe('ngAfterViewInit', () => {
60
- it('should run without throwing when iframe is present', () => {
61
- expect(() => component.ngAfterViewInit()).not.toThrow();
62
- });
63
- });
64
-
65
- describe('events', () => {
66
- it('returnClose should emit onClose and set closed state', () => {
67
- spyOn(component.onClose, 'emit');
68
- component.returnClose();
69
- expect(component.isOpen).toBe('closed');
70
- expect(component.onClose.emit).toHaveBeenCalled();
71
- });
72
-
73
- it('returnOpenExternal should emit button payload', () => {
74
- spyOn(component.onOpenExternal, 'emit');
75
- component.returnOpenExternal();
76
- expect(component.onOpenExternal.emit).toHaveBeenCalledWith(component.button);
77
- });
78
-
79
- it('onIframeLoaded should hide spinner', () => {
80
- component.hideSpinner = false;
81
- component.onIframeLoaded({});
82
- expect(component.hideSpinner).toBe(true);
83
- });
84
-
85
- it('onError should log', () => {
86
- spyOn((component as any).logger, 'error');
87
- component.onError({ message: 'x' });
88
- expect((component as any).logger.error).toHaveBeenCalled();
89
- });
90
- });
91
-
92
- describe('ngOnDestroy', () => {
93
- it('should reset url and spinner', () => {
94
- component.url = {} as any;
95
- component.hideSpinner = true;
96
- component.ngOnDestroy();
97
- expect(component.url).toBeNull();
98
- expect(component.hideSpinner).toBe(false);
99
- });
100
- });
101
29
  });
@@ -1,27 +1,23 @@
1
- <div id='c21-preview'
2
- role="dialog"
3
- aria-modal="true"
4
- cdkTrapFocus
5
- [cdkTrapFocusAutoCapture]="true"
6
- [attr.aria-label]="translationMap?.get('LABEL_PREVIEW')">
1
+ <div id='c21-preview' >
7
2
  <div class="modal-preview-image" #divPreview id="divPreview">
8
3
 
9
4
  <div class="c21-header">
10
5
  <div class="c21-header-container">
11
6
 
12
7
  <div class="c21-header-content">
13
- <!-- ICON CLOSE PREVIEW -->
14
- <button type="button"
15
- class="c21-header-button c21-right c21-close c21-button-clean"
16
- [attr.aria-label]="translationMap?.get('BUTTON_CLOSE_PREVIEW') || translationMap?.get('CLOSE')"
17
- [ngStyle]="{'display': (hideHeaderCloseButton)?'none':'flex'}"
18
- (click)="onClickClose()">
19
- <svg aria-hidden="true" focusable="false" [ngStyle]="{'fill': stylesMap?.get('themeColor') }" xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
8
+ <!-- ICON CLOSE CHAT -->
9
+ <button tabindex="-1" class="c21-header-button c21-right c21-close c21-button-clean" [ngStyle]="{'display': (hideHeaderCloseButton)?'none':'flex'}" (click)="onClickClose()">
10
+ <svg role="img" aria-labelledby="altIconTitle" [ngStyle]="{'fill': stylesMap?.get('themeColor') }" xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
20
11
  <path fill="none" d="M0 0h24v24H0V0z" />
21
12
  <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
13
+ <title id="altIconTitle">{{ translationMap?.get('CLOSE') }}</title>
22
14
  </svg>
23
15
  </button>
24
16
 
17
+ <!-- TITLE -->
18
+ <!-- <div class="c21-title" [ngStyle]="{'color': stylesMap?.get('themeColor') }">
19
+ <div class="titleText">{{ translationMap?.get('LABEL_PREVIEW') }}</div>
20
+ </div> -->
25
21
  </div>
26
22
 
27
23
  </div>
@@ -41,38 +37,36 @@
41
37
 
42
38
  <div id="c21-footer">
43
39
  <div class="visible-text-area">
44
- <textarea
40
+ <textarea
45
41
  start-focus-chat21-conversation-preview-component
46
- inputTextArea
47
- #textbox
48
- [attr.aria-label]="translationMap?.get('LABEL_PLACEHOLDER')"
49
- [attr.aria-multiline]="true"
50
- rows="1"
51
- id="chat21-main-message-context-preview"
42
+ inputTextArea
43
+ #textbox
44
+ tabindex="1502"
45
+ aria-labelledby="altTextArea"
46
+ rows="1"
47
+ id="chat21-main-message-context-preview"
52
48
  class='f21textarea c21-button-clean'
53
49
  [(ngModel)]="textInputTextArea"
54
- (ngModelChange)="onTextAreaChange()"
50
+ (ngModelChange)="onTextAreaChange()"
51
+ (keypress)="onkeypress($event)"
55
52
  (keydown)="onkeydown($event)"
56
53
  (paste)="onPaste($event)"
57
54
  placeholder="{{ translationMap?.get('LABEL_PLACEHOLDER') }}">
58
55
  </textarea>
59
56
 
60
57
  </div>
61
- <button type="button"
62
- class="chat21-textarea-button c21-button-clean"
63
- id="chat21-button-send"
64
- [class.active]="!isFilePendingToLoad"
65
- [attr.aria-label]="translationMap?.get('BUTTON_SEND_MESSAGE')"
66
- (click)="onSendPressed($event)">
58
+ <div tabindex="-1" class="chat21-textarea-button" id="chat21-button-send" [class.active]="!isFilePendingToLoad " (click)="onSendPressed($event)">
67
59
  <span class="v-align-center">
68
- <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
60
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
69
61
  <path d="M1.8,18.9V1.7L22,10.3L1.8,18.9z M3.9,15.6l12.6-5.4L3.9,4.9v3.7l6.4,1.6l-6.4,1.6V15.6z M3.9,15.6V4.9v7V15.6z"/>
70
62
  </svg>
71
63
  </span>
72
- </button>
64
+ </div>
73
65
 
74
66
  </div>
75
67
 
76
68
  </div>
77
69
 
78
70
  </div>
71
+ <span id="testFocus" tabindex="1599" onFocus="document.querySelector('[start-focus-chat21-conversation-preview-component]').focus()"></span>
72
+ <span id="altTextArea" style="display: none">scrivi la tua domanda</span>