@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,107 +1,113 @@
1
+ // Shadow DOM styles - isolati dal resto dell'applicazione
1
2
  :host {
2
3
  display: block;
4
+ // Le variabili CSS custom properties possono attraversare il boundary del Shadow DOM
3
5
  font-size: var(--font-size-bubble-message, 14px);
4
6
  }
5
7
 
6
8
  .message_innerhtml {
7
9
  margin: 0px;
8
-
9
- &.marked {
10
+ // padding: 0px 14px;
11
+ &.marked{
10
12
  padding: 12px 16px;
11
- margin-block-start: 0em !important;
12
- margin-block-end: 0em !important;
13
+ margin-block-start: 0em!important;
14
+ margin-block-end: 0em!important;
13
15
  }
14
-
16
+
15
17
  .text-message {
16
18
  padding-top: 14px;
17
19
  }
20
+ }
18
21
 
19
- p {
20
- font-size: inherit;
21
- margin: 0;
22
- line-height: 1.4em;
23
- font-style: normal;
24
- letter-spacing: normal;
25
- font-stretch: normal;
26
- font-variant: normal;
27
- font-weight: 300;
28
- overflow: hidden;
29
- }
30
-
31
- a {
32
- word-break: break-word;
33
- color: inherit;
34
- text-decoration: underline;
35
- }
22
+ p {
23
+ font-size: inherit;
24
+ margin: 0;
25
+ // padding: 14px;
26
+ line-height: 1.4em;
27
+ font-style: normal;
28
+ letter-spacing: normal;
29
+ font-stretch: normal;
30
+ font-variant: normal;
31
+ font-weight: 300;
32
+ overflow: hidden;
33
+ }
36
34
 
37
- a:hover {
38
- margin-block-end: 0em;
39
- margin-block-start: 0em;
40
- }
35
+ p ::ng-deep a,
36
+ p a {
37
+ word-break: break-word;
38
+ color: inherit; // Eredita il colore dal parent
39
+ text-decoration: underline;
40
+ }
41
41
 
42
- ol {
43
- margin-block-end: 0em;
44
- margin-block-start: 0em;
45
- padding-inline-start: 15px;
42
+ p ::ng-deep p,
43
+ p a:hover{
44
+ margin-block-end: 0em;
45
+ margin-block-start: 0em;
46
+ }
46
47
 
47
- li::before {
48
- content: "";
49
- font-size: 1.4em;
50
- vertical-align: middle;
51
- }
48
+ p ol {
49
+ margin-block-end: 0em;
50
+ margin-block-start: 0em;
51
+ padding-inline-start: 15px;
52
+
53
+ li::before {
54
+ content: "";
55
+ font-size: 1.4em;
56
+ vertical-align: middle;
52
57
  }
58
+ }
53
59
 
54
- h1, h2, h3, h4, h5, h6 {
55
- margin-block-start: 0.5em;
56
- margin-block-end: 0.5em;
57
- font-weight: bold;
58
- color: inherit;
59
- }
60
+ // Stili aggiuntivi per elementi markdown comuni
61
+ p h1, p h2, p h3, p h4, p h5, p h6 {
62
+ margin-block-start: 0.5em;
63
+ margin-block-end: 0.5em;
64
+ font-weight: bold;
65
+ color: inherit; // Eredita il colore dal parent
66
+ }
60
67
 
61
- &.marked h1 {
62
- line-height: normal;
63
- }
68
+ .message_innerhtml.marked h1 {
69
+ line-height: normal;
70
+ }
64
71
 
65
- ul {
66
- margin-block-end: 0em;
67
- margin-block-start: 0em;
68
- padding-inline-start: 15px;
69
- }
72
+ p ul {
73
+ margin-block-end: 0em;
74
+ margin-block-start: 0em;
75
+ padding-inline-start: 15px;
76
+ }
70
77
 
71
- code {
72
- background-color: rgba(0, 0, 0, 0.05);
73
- padding: 2px 4px;
74
- border-radius: 3px;
75
- font-family: monospace;
76
- }
78
+ p code {
79
+ background-color: rgba(0, 0, 0, 0.05);
80
+ padding: 2px 4px;
81
+ border-radius: 3px;
82
+ font-family: monospace;
83
+ }
77
84
 
78
- pre {
79
- background-color: rgba(0, 0, 0, 0.05);
80
- padding: 10px;
81
- border-radius: 3px;
82
- overflow-x: auto;
83
- }
85
+ p pre {
86
+ background-color: rgba(0, 0, 0, 0.05);
87
+ padding: 10px;
88
+ border-radius: 3px;
89
+ overflow-x: auto;
90
+ }
84
91
 
85
- blockquote {
86
- border-left: 3px solid rgba(0, 0, 0, 0.1);
87
- padding-left: 10px;
88
- margin-left: 0;
89
- font-style: italic;
90
- }
92
+ p blockquote {
93
+ border-left: 3px solid rgba(0, 0, 0, 0.1);
94
+ padding-left: 10px;
95
+ margin-left: 0;
96
+ font-style: italic;
97
+ }
91
98
 
92
- table {
93
- border-collapse: collapse;
94
- width: 100%;
95
- margin: 10px 0;
96
- }
99
+ p table {
100
+ border-collapse: collapse;
101
+ width: 100%;
102
+ margin: 10px 0;
103
+ }
97
104
 
98
- table td, table th {
99
- border: 1px solid rgba(0, 0, 0, 0.1);
100
- padding: 8px;
101
- }
105
+ p table td, p table th {
106
+ border: 1px solid rgba(0, 0, 0, 0.1);
107
+ padding: 8px;
108
+ }
102
109
 
103
- table th {
104
- background-color: rgba(0, 0, 0, 0.05);
105
- font-weight: bold;
106
- }
110
+ p table th {
111
+ background-color: rgba(0, 0, 0, 0.05);
112
+ font-weight: bold;
107
113
  }
@@ -1,126 +1,33 @@
1
- import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
1
+ import { HtmlEntitiesEncodePipe } from './../../../pipe/html-entities-encode.pipe';
2
+ import { MarkedPipe } from './../../../pipe/marked.pipe';
3
+ import { async, ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
4
 
3
- import { MarkedPipe } from '../../../pipe/marked.pipe';
4
5
  import { TextComponent } from './text.component';
5
6
 
6
- /**
7
- * Il template usa solo `| marked` su ciò che ritorna `printMessage(...)`:
8
- * `[innerHTML]="printMessage(text, messageEl, this) | marked"`
9
- * I test qui riproducono gli stessi use case (XSS / HTML grezzo / markdown) sulla pipeline reale del componente.
10
- */
11
- describe('TextComponent (render via MarkedPipe)', () => {
7
+ describe('TextComponent', () => {
12
8
  let component: TextComponent;
13
9
  let fixture: ComponentFixture<TextComponent>;
14
- let markedPipe: MarkedPipe;
15
-
16
- function shadowInnerHtml(): string {
17
- const host = fixture.nativeElement as HTMLElement;
18
- const root = host.shadowRoot;
19
- if (!root) {
20
- throw new Error('Expected ShadowDom root');
21
- }
22
- return (root.querySelector('.message_innerhtml') as HTMLElement).innerHTML;
23
- }
24
10
 
25
11
  beforeEach(waitForAsync(() => {
26
12
  TestBed.configureTestingModule({
27
- declarations: [TextComponent, MarkedPipe],
28
- }).compileComponents();
13
+ declarations: [
14
+ TextComponent,
15
+ MarkedPipe,
16
+ HtmlEntitiesEncodePipe
17
+ ]
18
+ })
19
+ .compileComponents();
29
20
  }));
30
21
 
31
22
  beforeEach(() => {
32
23
  fixture = TestBed.createComponent(TextComponent);
33
24
  component = fixture.componentInstance;
34
- markedPipe = new MarkedPipe();
35
- component.text = 'Msg text';
36
- component.color = 'black';
25
+ component.text = 'Msg text'
26
+ component.color= 'black'
37
27
  fixture.detectChanges();
38
28
  });
39
29
 
40
30
  it('should create', () => {
41
31
  expect(component).toBeTruthy();
42
32
  });
43
-
44
- it('printMessage should emit before and after and return text (input grezzo per la pipe)', () => {
45
- spyOn(component.onBeforeMessageRender, 'emit');
46
- spyOn(component.onAfterMessageRender, 'emit');
47
- const out = component.printMessage('Hello', {} as any, component);
48
- expect(out).toBe('Hello');
49
- expect(component.onBeforeMessageRender.emit).toHaveBeenCalled();
50
- expect(component.onAfterMessageRender.emit).toHaveBeenCalled();
51
- });
52
-
53
- it('should render message container with color style', () => {
54
- const host = fixture.nativeElement as HTMLElement;
55
- const el = host.shadowRoot!.querySelector('div.message_innerhtml') as HTMLElement;
56
- expect(el).toBeTruthy();
57
- expect(el.style.color).toBe('black');
58
- });
59
-
60
- describe('markdown (use case contenuto legittimo)', () => {
61
- it('should render heading and bold from markdown', () => {
62
- component.text = '# Title\n\n**Bold**';
63
- fixture.detectChanges();
64
- const html = shadowInnerHtml();
65
- expect(html).toMatch(/<h1|<h2/i);
66
- expect(html).toMatch(/<strong>|<b>/i);
67
- });
68
-
69
- it('should render unordered list from markdown', () => {
70
- component.text = '- uno\n- due';
71
- fixture.detectChanges();
72
- const html = shadowInnerHtml();
73
- expect(html).toMatch(/<ul/i);
74
- expect(html).toMatch(/<li/i);
75
- });
76
-
77
- it('should render safe https link with rel=noopener (MarkedPipe renderer)', () => {
78
- component.text = '[label](https://example.com/path)';
79
- fixture.detectChanges();
80
- const html = shadowInnerHtml();
81
- expect(html).toContain('https://example.com/path');
82
- expect(html).toContain('rel="noopener noreferrer"');
83
- expect(html).toContain('target="_blank"');
84
- });
85
- });
86
-
87
- describe('sicurezza / anti code-injection (stesso use case, solo marked)', () => {
88
- it('should not leave raw script tags in DOM innerHTML', () => {
89
- component.text = '<script>alert(1)</script>testo';
90
- fixture.detectChanges();
91
- const html = shadowInnerHtml();
92
- expect(html.toLowerCase()).not.toContain('<script');
93
- expect(html).toContain('&lt;');
94
- });
95
-
96
- it('should escape inline HTML img/onerror so it is not a live <img> node', () => {
97
- component.text = 'Hi <img src=x onerror="alert(1)">';
98
- fixture.detectChanges();
99
- const html = shadowInnerHtml();
100
- expect(html.toLowerCase()).not.toContain('<img');
101
- expect(html).toContain('&lt;');
102
- });
103
-
104
- it('should not emit javascript: href for markdown link', () => {
105
- component.text = '[bad](javascript:void(0))';
106
- fixture.detectChanges();
107
- const html = shadowInnerHtml();
108
- expect(html).not.toMatch(/href=["']javascript:/i);
109
- });
110
-
111
- it('should not emit data: href for markdown link', () => {
112
- component.text = '[bad](data:text/html,<script>alert(1)</script>)';
113
- fixture.detectChanges();
114
- const html = shadowInnerHtml();
115
- expect(html).not.toMatch(/href=["']data:/i);
116
- });
117
-
118
- it('DOM innerHTML should match MarkedPipe applied to printMessage output (stessa pipeline del template)', () => {
119
- component.text = 'Line1\n\n**x**';
120
- fixture.detectChanges();
121
- const raw = component.printMessage(component.text, {} as any, component);
122
- const viaPipe = markedPipe.transform(raw) as string;
123
- expect(shadowInnerHtml()).toBe(viaPipe);
124
- });
125
- });
126
33
  });
@@ -1,154 +1,33 @@
1
- import { CommonModule } from '@angular/common';
2
- import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
3
- import { By } from '@angular/platform-browser';
4
-
5
- import { MessageModel } from 'src/chat21-core/models/message';
6
- import { ActionButtonComponent } from '../message/buttons/action-button/action-button.component';
7
- import { LinkButtonComponent } from '../message/buttons/link-button/link-button.component';
8
- import { TextButtonComponent } from '../message/buttons/text-button/text-button.component';
1
+ import { ActionButtonComponent } from './../message/buttons/action-button/action-button.component';
2
+ import { TextButtonComponent } from './../message/buttons/text-button/text-button.component';
3
+ import { LinkButtonComponent } from './../message/buttons/link-button/link-button.component';
4
+ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
9
5
 
10
6
  import { MessageAttachmentComponent } from './message-attachment.component';
11
7
 
12
- function makeMessageWithAttachment(buttons: any[]): MessageModel {
13
- return new MessageModel(
14
- 'm1',
15
- 'it',
16
- 'r1',
17
- 'Recipient',
18
- 's1',
19
- 'Sender',
20
- 200,
21
- {},
22
- 'testo',
23
- Date.now(),
24
- 'text',
25
- {
26
- attachment: {
27
- type: 'template',
28
- buttons,
29
- },
30
- },
31
- 'chat',
32
- true,
33
- false,
34
- );
35
- }
36
-
37
8
  describe('MessageAttachmentComponent', () => {
38
9
  let component: MessageAttachmentComponent;
39
10
  let fixture: ComponentFixture<MessageAttachmentComponent>;
40
11
 
41
12
  beforeEach(waitForAsync(() => {
42
13
  TestBed.configureTestingModule({
43
- imports: [CommonModule],
44
- declarations: [
45
- MessageAttachmentComponent,
46
- LinkButtonComponent,
47
- TextButtonComponent,
48
- ActionButtonComponent,
49
- ],
50
- }).compileComponents();
14
+ declarations: [
15
+ MessageAttachmentComponent,
16
+ LinkButtonComponent,
17
+ TextButtonComponent,
18
+ ActionButtonComponent
19
+ ]
20
+ })
21
+ .compileComponents();
51
22
  }));
52
23
 
53
24
  beforeEach(() => {
54
25
  fixture = TestBed.createComponent(MessageAttachmentComponent);
55
26
  component = fixture.componentInstance;
56
- component.isConversationArchived = false;
57
- component.isLastMessage = true;
58
- component.fullscreenMode = false;
59
- component.limit = 5;
60
- component.stylesMap = new Map<string, string>([
61
- ['buttonFontSize', '14px'],
62
- ['buttonBackgroundColor', '#eee'],
63
- ['buttonTextColor', '#111'],
64
- ['buttonHoverBackgroundColor', '#ddd'],
65
- ['buttonHoverTextColor', '#000'],
66
- ]);
27
+ fixture.detectChanges();
67
28
  });
68
29
 
69
30
  it('should create', () => {
70
- component.message = makeMessageWithAttachment([]);
71
- fixture.detectChanges();
72
31
  expect(component).toBeTruthy();
73
32
  });
74
-
75
- it('getAttachmentButton should read type and buttons from message.attributes.attachment', () => {
76
- component.message = makeMessageWithAttachment([
77
- { type: 'text', value: 'OK' },
78
- { type: 'url', value: 'Apri', link: 'https://example.com' },
79
- ]);
80
- component.ngOnInit();
81
- expect(component.type).toBe('template');
82
- expect((component.buttons as unknown as any[]).length).toBe(2);
83
- });
84
-
85
- it('should render text and url attachment buttons in DOM when isLastMessage is true for text', () => {
86
- component.message = makeMessageWithAttachment([
87
- { type: 'text', value: 'Conferma' },
88
- { type: 'url', value: 'Vai', link: 'https://example.com', target: '_blank' },
89
- ]);
90
- fixture.detectChanges();
91
- const root = fixture.nativeElement.querySelector('#buttons-in-message') as HTMLElement;
92
- expect(root).toBeTruthy();
93
- expect(fixture.nativeElement.querySelector('chat-text-button-attachment')).toBeTruthy();
94
- expect(fixture.nativeElement.querySelector('chat-link-button-attachment')).toBeTruthy();
95
- const textBtn = fixture.nativeElement.querySelector('chat-text-button-attachment .text') as HTMLElement;
96
- expect(textBtn.textContent?.trim()).toContain('Conferma');
97
- });
98
-
99
- it('should respect limit via slice (only first button when limit is 1)', () => {
100
- component.limit = 1;
101
- component.message = makeMessageWithAttachment([
102
- { type: 'text', value: 'A' },
103
- { type: 'text', value: 'B' },
104
- ]);
105
- fixture.detectChanges();
106
- const textButtons = fixture.nativeElement.querySelectorAll('chat-text-button-attachment');
107
- expect(textButtons.length).toBe(1);
108
- });
109
-
110
- it('should hide text and action buttons when isLastMessage is false', () => {
111
- component.isLastMessage = false;
112
- component.message = makeMessageWithAttachment([
113
- { type: 'text', value: 'Nascosto' },
114
- { type: 'url', value: 'Visibile', link: 'https://x.com' },
115
- ]);
116
- fixture.detectChanges();
117
- expect(fixture.nativeElement.querySelector('chat-text-button-attachment')).toBeNull();
118
- expect(fixture.nativeElement.querySelector('chat-link-button-attachment')).toBeTruthy();
119
- });
120
-
121
- it('returnOnAttachmentButtonClicked should emit structured event when target exists', () => {
122
- component.message = makeMessageWithAttachment([{ type: 'text', value: 'X' }]);
123
- fixture.detectChanges();
124
- spyOn(component.onAttachmentButtonClicked, 'emit');
125
- const inner = fixture.debugElement.query(By.css('chat-text-button-attachment .text'));
126
- inner.triggerEventHandler('click', {});
127
- expect(component.onAttachmentButtonClicked.emit).toHaveBeenCalled();
128
- const arg = (component.onAttachmentButtonClicked.emit as jasmine.Spy).calls.mostRecent().args[0];
129
- expect(arg.message).toBe(component.message);
130
- expect(arg.target).toBeTruthy();
131
- });
132
-
133
- it('returnOnAttachmentButtonClicked should not emit when event has no target', () => {
134
- component.message = makeMessageWithAttachment([{ type: 'text', value: 'X' }]);
135
- fixture.detectChanges();
136
- spyOn(component.onAttachmentButtonClicked, 'emit');
137
- component.returnOnAttachmentButtonClicked({} as any);
138
- expect(component.onAttachmentButtonClicked.emit).not.toHaveBeenCalled();
139
- });
140
-
141
- it('ngAfterViewInit should emit onElementRendered for attachment', () => {
142
- spyOn(component.onElementRendered, 'emit');
143
- component.message = makeMessageWithAttachment([{ type: 'url', value: 'L', link: 'https://a' }]);
144
- fixture.detectChanges();
145
- component.ngAfterViewInit();
146
- expect(component.onElementRendered.emit).toHaveBeenCalledWith({ element: 'attachment', status: true });
147
- });
148
-
149
- it('should render action button when type is action and isLastMessage', () => {
150
- component.message = makeMessageWithAttachment([{ type: 'action', value: 'Azione', action: 'reply' }]);
151
- fixture.detectChanges();
152
- expect(fixture.nativeElement.querySelector('chat-action-button-attachment')).toBeTruthy();
153
- });
154
33
  });
@@ -1,25 +1,24 @@
1
- <div id="chat21-selection-department"
1
+ <!-- <div id="chat21-modal-content modal-body"></div> -->
2
+ <!-- tabindex="1200"-->
3
+
4
+ <div id="chat21-selection-department"
2
5
  #afSelectionDepartment
3
- role="dialog"
6
+ tabindex="1200"
4
7
  aria-modal="true"
5
- cdkTrapFocus
6
- [cdkTrapFocusAutoCapture]="true"
7
- [attr.aria-label]="g.LABEL_SELECT_TOPIC">
8
+ onFocus="document.querySelector('[start-focus-chat21-selection-department]').focus()">
8
9
 
9
10
  <!-- HEADER -->
10
- <div class="c21-header" [ngStyle]="{'color': g.themeForegroundColor, 'background-image': g.colorGradient180 }">
11
+ <div class="c21-header" [ngStyle]="{'color': g.themeForegroundColor, 'background-image': g.colorGradient180 }" >
11
12
  <div class="c21-header-container">
12
13
 
13
- <!-- ICON CLOSE PANEL -->
14
+ <!-- ICON CLOSE CHAT -->
14
15
  <div class="c21-header-button">
15
- <div class="c21-close-button c21-small">
16
- <button type="button"
17
- [attr.aria-label]="g.BUTTON_CLOSE_TO_ICON || 'Close panel'"
18
- class="c21-close-button-body"
19
- (click)="closePage()">
20
- <svg aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24">
16
+ <div class="c21-close-button c21-small" role="button">
17
+ <button tabindex="1210" aria-label=" chiudi pannello" class="c21-close-button-body" (click)="closePage()">
18
+ <svg role="img" aria-labelledby="altIconTitle" xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewBox="0 0 24 24">
21
19
  <path fill="none" d="M0 0h24v24H0V0z"/>
22
20
  <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"/>
21
+ <!-- <title id="altIconTitle">{{BUTTON_CLOSE_CHAT}}</title> -->
23
22
  </svg>
24
23
  </button>
25
24
  </div>
@@ -27,7 +26,9 @@
27
26
 
28
27
  <!-- CONTENT HEADER -->
29
28
  <div class="c21-header-content">
29
+ <!-- TITLE HEADER -->
30
30
  <div class="c21-title" [ngStyle]="{'color': g.themeForegroundColor}">
31
+ <!-- <span>{{g.LABEL_SELECT_TOPIC}}</span> -->
31
32
  </div>
32
33
  </div>
33
34
 
@@ -39,17 +40,15 @@
39
40
  <div class="c21-body-container">
40
41
 
41
42
  <div class="c21-body-header">
42
- <h2 start-focus-chat21-selection-department class="c21-message-field">{{g.LABEL_SELECT_TOPIC}}</h2>
43
+ <div start-focus-chat21-selection-department tabindex="1201" class="c21-message-field">{{g.LABEL_SELECT_TOPIC}}</div>
43
44
  </div>
44
45
 
45
- <div class="c21-body-content">
46
+ <div class="c21-body-content" tabindex="211" onFocus="document.querySelector('[start-focus]').focus()">
46
47
  <ul class="chat21-content-modal-select">
47
48
  <li *ngFor="let department of departments; let i = index">
48
- <button type="button"
49
- class="c21-button-department c21-button-clean"
50
- (click)="onSelectDepartment(department)">
51
- <span class="chat21-badge" aria-hidden="true">{{i+1}}</span>
52
- <span class="chat21-name-list">{{department?.name}}</span>
49
+ <button tabindex="1202" class="c21-button-department c21-button-clean" (click)="onSelectDepartment(department)">
50
+ <span class="chat21-badge">{{i+1}}</span>
51
+ <span class="chat21-name-list" >{{department?.name}}</span>
53
52
  </button>
54
53
  </li>
55
54
  </ul>
@@ -57,4 +56,7 @@
57
56
 
58
57
  </div>
59
58
  </div>
60
- </div>
59
+
60
+ <span style="height:0px" tabindex="1299" onFocus="document.querySelector('[start-focus-chat21-selection-department]').focus()"></span>
61
+ </div>
62
+