cap-creatives-ui 8.0.280 → 8.0.321

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 (247) hide show
  1. package/.github/workflows/pr-title-check.yml +88 -0
  2. package/app/constants/unified.js +21 -1
  3. package/app/containers/App/constants.js +0 -1
  4. package/app/containers/Login/test/index.test.js +123 -0
  5. package/app/containers/Login/test/selectors.test.js +165 -0
  6. package/app/initialState.js +0 -2
  7. package/app/services/api.js +6 -0
  8. package/app/services/tests/api.test.js +7 -0
  9. package/app/services/tests/getSchema.test.js +95 -0
  10. package/app/utils/common.js +23 -9
  11. package/app/utils/commonUtils.js +64 -93
  12. package/app/utils/tagValidations.js +83 -219
  13. package/app/utils/templateVarUtils.js +172 -0
  14. package/app/utils/tests/common.test.js +265 -323
  15. package/app/utils/tests/commonUtil.test.js +461 -118
  16. package/app/utils/tests/commonUtils.test.js +581 -0
  17. package/app/utils/tests/messageUtils.test.js +95 -0
  18. package/app/utils/tests/smsCharCount.test.js +304 -0
  19. package/app/utils/tests/smsCharCountV2.test.js +213 -10
  20. package/app/utils/tests/tagValidations.test.js +474 -357
  21. package/app/utils/tests/templateVarUtils.test.js +160 -0
  22. package/app/v2Components/CapDeviceContent/index.js +10 -7
  23. package/app/v2Components/CapTagList/index.js +32 -24
  24. package/app/v2Components/CapTagList/style.scss +48 -0
  25. package/app/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
  26. package/app/v2Components/CapTagListWithInput/index.js +8 -0
  27. package/app/v2Components/CapWhatsappCTA/index.js +2 -0
  28. package/app/v2Components/CapWhatsappCarouselButton/index.js +32 -14
  29. package/app/v2Components/CapWhatsappCarouselButton/tests/index.test.js +120 -2
  30. package/app/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
  31. package/app/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +39 -0
  32. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +606 -0
  33. package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +36 -0
  34. package/app/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +79 -0
  35. package/app/v2Components/CommonTestAndPreview/DeliverySettings/index.js +314 -0
  36. package/app/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +141 -0
  37. package/app/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +156 -0
  38. package/app/v2Components/CommonTestAndPreview/SendTestMessage.js +57 -1
  39. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
  40. package/app/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
  41. package/app/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +210 -4
  42. package/app/v2Components/CommonTestAndPreview/actions.js +20 -0
  43. package/app/v2Components/CommonTestAndPreview/constants.js +57 -1
  44. package/app/v2Components/CommonTestAndPreview/index.js +878 -156
  45. package/app/v2Components/CommonTestAndPreview/messages.js +41 -3
  46. package/app/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
  47. package/app/v2Components/CommonTestAndPreview/reducer.js +47 -0
  48. package/app/v2Components/CommonTestAndPreview/sagas.js +75 -5
  49. package/app/v2Components/CommonTestAndPreview/selectors.js +51 -0
  50. package/app/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
  51. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +1156 -0
  52. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +334 -0
  53. package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +576 -0
  54. package/app/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +156 -0
  55. package/app/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
  56. package/app/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
  57. package/app/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -7
  58. package/app/v2Components/CommonTestAndPreview/tests/index.test.js +914 -5
  59. package/app/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
  60. package/app/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
  61. package/app/v2Components/CommonTestAndPreview/tests/sagas.test.js +146 -378
  62. package/app/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
  63. package/app/v2Components/ErrorInfoNote/index.js +24 -26
  64. package/app/v2Components/FormBuilder/index.js +182 -204
  65. package/app/v2Components/FormBuilder/messages.js +4 -8
  66. package/app/v2Components/HtmlEditor/HTMLEditor.js +7 -6
  67. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -1
  68. package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +928 -17
  69. package/app/v2Components/HtmlEditor/components/CodeEditorPane/index.js +4 -2
  70. package/app/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +452 -3
  71. package/app/v2Components/HtmlEditor/hooks/useValidation.js +12 -9
  72. package/app/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +132 -0
  73. package/app/v2Components/HtmlEditor/utils/htmlValidator.js +4 -2
  74. package/app/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
  75. package/app/v2Components/SmsFallback/constants.js +73 -0
  76. package/app/v2Components/SmsFallback/index.js +956 -0
  77. package/app/v2Components/SmsFallback/index.scss +265 -0
  78. package/app/v2Components/SmsFallback/messages.js +78 -0
  79. package/app/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
  80. package/app/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
  81. package/app/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
  82. package/app/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
  83. package/app/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
  84. package/app/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
  85. package/app/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
  86. package/app/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
  87. package/app/v2Components/TestAndPreviewSlidebox/index.js +22 -1
  88. package/app/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
  89. package/app/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
  90. package/app/v2Components/VarSegmentMessageEditor/constants.js +2 -0
  91. package/app/v2Components/VarSegmentMessageEditor/index.js +125 -0
  92. package/app/v2Components/VarSegmentMessageEditor/index.scss +46 -0
  93. package/app/v2Containers/BeeEditor/index.js +3 -0
  94. package/app/v2Containers/BeePopupEditor/index.js +9 -2
  95. package/app/v2Containers/Cap/mockData.js +0 -14
  96. package/app/v2Containers/Cap/reducer.js +3 -55
  97. package/app/v2Containers/Cap/tests/reducer.test.js +0 -102
  98. package/app/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
  99. package/app/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
  100. package/app/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
  101. package/app/v2Containers/CommunicationFlow/constants.js +200 -0
  102. package/app/v2Containers/CommunicationFlow/index.js +102 -0
  103. package/app/v2Containers/CommunicationFlow/messages.js +346 -0
  104. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
  105. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
  106. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
  107. package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
  108. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
  109. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
  110. package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
  111. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
  112. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
  113. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
  114. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
  115. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
  116. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
  117. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
  118. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
  119. package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
  120. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
  121. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
  122. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
  123. package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
  124. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
  125. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
  126. package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
  127. package/app/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
  128. package/app/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
  129. package/app/v2Containers/CreativesContainer/SlideBoxContent.js +127 -11
  130. package/app/v2Containers/CreativesContainer/SlideBoxFooter.js +62 -9
  131. package/app/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
  132. package/app/v2Containers/CreativesContainer/constants.js +24 -0
  133. package/app/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
  134. package/app/v2Containers/CreativesContainer/index.js +346 -71
  135. package/app/v2Containers/CreativesContainer/index.scss +51 -1
  136. package/app/v2Containers/CreativesContainer/messages.js +12 -0
  137. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
  138. package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
  139. package/app/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +443 -0
  140. package/app/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
  141. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +147 -4
  142. package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
  143. package/app/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +57 -10
  144. package/app/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
  145. package/app/v2Containers/CreativesContainer/tests/index.test.js +71 -9
  146. package/app/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
  147. package/app/v2Containers/Email/index.js +2 -5
  148. package/app/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +58 -77
  149. package/app/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
  150. package/app/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +158 -89
  151. package/app/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
  152. package/app/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +17 -12
  153. package/app/v2Containers/EmailWrapper/index.js +4 -0
  154. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
  155. package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +133 -0
  156. package/app/v2Containers/FTP/index.js +2 -51
  157. package/app/v2Containers/FTP/messages.js +0 -4
  158. package/app/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
  159. package/app/v2Containers/InApp/index.js +297 -118
  160. package/app/v2Containers/InApp/tests/index.test.js +17 -6
  161. package/app/v2Containers/InApp/tests/mockData.js +1 -1
  162. package/app/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
  163. package/app/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
  164. package/app/v2Containers/InAppWrapper/index.js +3 -0
  165. package/app/v2Containers/InappAdvance/index.js +5 -104
  166. package/app/v2Containers/InappAdvance/tests/index.test.js +2 -0
  167. package/app/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +24 -3
  168. package/app/v2Containers/Line/Container/Text/index.js +0 -1
  169. package/app/v2Containers/MobilePush/Create/index.js +105 -28
  170. package/app/v2Containers/MobilePush/Create/messages.js +4 -0
  171. package/app/v2Containers/MobilePush/Edit/index.js +250 -68
  172. package/app/v2Containers/MobilePush/Edit/messages.js +4 -0
  173. package/app/v2Containers/MobilePushNew/components/PlatformContentFields.js +36 -12
  174. package/app/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +68 -27
  175. package/app/v2Containers/MobilePushNew/index.js +78 -35
  176. package/app/v2Containers/MobilePushNew/messages.js +8 -0
  177. package/app/v2Containers/MobilepushWrapper/index.js +11 -1
  178. package/app/v2Containers/Rcs/constants.js +32 -1
  179. package/app/v2Containers/Rcs/index.js +963 -916
  180. package/app/v2Containers/Rcs/index.scss +85 -6
  181. package/app/v2Containers/Rcs/messages.js +10 -1
  182. package/app/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
  183. package/app/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +41136 -1566
  184. package/app/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
  185. package/app/v2Containers/Rcs/tests/index.test.js +41 -38
  186. package/app/v2Containers/Rcs/tests/mockData.js +38 -0
  187. package/app/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
  188. package/app/v2Containers/Rcs/tests/utils.test.js +379 -1
  189. package/app/v2Containers/Rcs/utils.js +358 -10
  190. package/app/v2Containers/Sms/Create/index.js +122 -39
  191. package/app/v2Containers/Sms/Create/messages.js +4 -0
  192. package/app/v2Containers/Sms/Edit/index.js +37 -3
  193. package/app/v2Containers/Sms/commonMethods.js +3 -6
  194. package/app/v2Containers/Sms/smsFormDataHelpers.js +67 -0
  195. package/app/v2Containers/Sms/tests/commonMethods.test.js +122 -0
  196. package/app/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
  197. package/app/v2Containers/SmsTrai/Create/index.js +9 -4
  198. package/app/v2Containers/SmsTrai/Create/index.scss +1 -1
  199. package/app/v2Containers/SmsTrai/Edit/constants.js +2 -0
  200. package/app/v2Containers/SmsTrai/Edit/index.js +667 -160
  201. package/app/v2Containers/SmsTrai/Edit/index.scss +121 -0
  202. package/app/v2Containers/SmsTrai/Edit/messages.js +9 -4
  203. package/app/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4590 -2436
  204. package/app/v2Containers/SmsWrapper/index.js +41 -8
  205. package/app/v2Containers/TagList/index.js +63 -2
  206. package/app/v2Containers/TagList/messages.js +8 -0
  207. package/app/v2Containers/TagList/tests/TagList.test.js +122 -20
  208. package/app/v2Containers/TagList/tests/mockdata.js +17 -0
  209. package/app/v2Containers/Templates/TemplatesActionBar.js +101 -0
  210. package/app/v2Containers/Templates/_templates.scss +61 -2
  211. package/app/v2Containers/Templates/actions.js +11 -0
  212. package/app/v2Containers/Templates/constants.js +2 -0
  213. package/app/v2Containers/Templates/index.js +90 -40
  214. package/app/v2Containers/Templates/reducer.js +3 -1
  215. package/app/v2Containers/Templates/sagas.js +57 -12
  216. package/app/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
  217. package/app/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
  218. package/app/v2Containers/Templates/tests/reducer.test.js +12 -0
  219. package/app/v2Containers/Templates/tests/sagas.test.js +193 -12
  220. package/app/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
  221. package/app/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
  222. package/app/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
  223. package/app/v2Containers/TemplatesV2/index.js +147 -49
  224. package/app/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
  225. package/app/v2Containers/Viber/index.js +9 -10
  226. package/app/v2Containers/Viber/index.scss +1 -1
  227. package/app/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
  228. package/app/v2Containers/WebPush/Create/components/MessageSection.js +78 -19
  229. package/app/v2Containers/WebPush/Create/components/MessageSection.test.js +82 -0
  230. package/app/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
  231. package/app/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +25 -17
  232. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.js +80 -0
  233. package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.test.js +210 -0
  234. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -5
  235. package/app/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
  236. package/app/v2Containers/WebPush/Create/index.js +36 -6
  237. package/app/v2Containers/WebPush/Create/index.scss +5 -0
  238. package/app/v2Containers/WebPush/Create/messages.js +8 -1
  239. package/app/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +269 -0
  240. package/app/v2Containers/WebPush/Create/utils/validation.js +31 -15
  241. package/app/v2Containers/WebPush/Create/utils/validation.test.js +72 -24
  242. package/app/v2Containers/Whatsapp/index.js +28 -53
  243. package/app/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +26939 -3982
  244. package/app/v2Containers/Whatsapp/tests/index.test.js +172 -0
  245. package/app/v2Containers/Zalo/index.js +5 -11
  246. package/package.json +2 -2
  247. package/version +9 -0
@@ -0,0 +1,1156 @@
1
+ /**
2
+ * Tests for ModifyDeliverySettings component (delivery settings form in slidebox)
3
+ * @jest-environment jsdom
4
+ */
5
+
6
+ /* eslint-disable react/prop-types */
7
+
8
+ import React from 'react';
9
+ import { fireEvent, render, screen } from '@testing-library/react';
10
+ import ModifyDeliverySettings from '../../DeliverySettings/ModifyDeliverySettings';
11
+ import { CHANNELS } from '../../constants';
12
+
13
+ jest.mock('../../DeliverySettings/ModifyDeliverySettings.scss', () => ({}));
14
+
15
+ jest.mock('@capillarytech/cap-ui-library/CapRow', () => ({ children, className }) => <div className={className} data-testid="cap-row">{children}</div>);
16
+ jest.mock('@capillarytech/cap-ui-library/CapHeader', () => ({ title, className }) => <div className={className} data-testid="cap-header">{title}</div>);
17
+ jest.mock('@capillarytech/cap-ui-library/CapSelect', () => ({
18
+ options = [],
19
+ value,
20
+ onChange,
21
+ disabled,
22
+ placeholder,
23
+ componentClassName,
24
+ }) => (
25
+ <div>
26
+ <select
27
+ data-testid="cap-select"
28
+ data-placeholder={placeholder}
29
+ data-component-classname={componentClassName}
30
+ value={value ?? ''}
31
+ onChange={(e) => {
32
+ const matched = options.find((opt) => String(opt.value) === e.target.value);
33
+ onChange(matched ? matched.value : e.target.value);
34
+ }}
35
+ disabled={disabled}
36
+ aria-label={placeholder}
37
+ >
38
+ <option value="">--empty--</option>
39
+ {(options || []).map((opt) => (
40
+ <option key={`${opt.label}-${opt.value}`} value={String(opt.value)}>
41
+ {opt.label}
42
+ </option>
43
+ ))}
44
+ </select>
45
+ <input
46
+ data-testid="cap-select-raw-input"
47
+ onChange={(e) => onChange(e.target.value)}
48
+ />
49
+ </div>
50
+ ));
51
+ jest.mock('@capillarytech/cap-ui-library/CapButton', () => ({ children, onClick, type }) => (
52
+ <button type="button" onClick={onClick} data-testid="cap-button" data-buttontype={type}>
53
+ {children}
54
+ </button>
55
+ ));
56
+ jest.mock('@capillarytech/cap-ui-library/CapSpin', () => ({ spinning }) => (spinning ? <div data-testid="cap-spin">Loading...</div> : null));
57
+ jest.mock('@capillarytech/cap-ui-library/CapLabel', () => ({ children, type }) => <span data-testid="cap-label" data-type={type}>{children}</span>);
58
+
59
+ jest.mock('react-intl', () => ({
60
+ FormattedMessage: ({ defaultMessage }) => <span>{defaultMessage}</span>,
61
+ defineMessages: (obj) => obj,
62
+ }));
63
+
64
+ describe('ModifyDeliverySettings', () => {
65
+ const defaultProps = {
66
+ channel: CHANNELS.SMS,
67
+ deliverySettings: {},
68
+ senderDetailsOptions: [],
69
+ wecrmAccounts: [],
70
+ onSaveDeliverySettings: jest.fn(),
71
+ onClose: jest.fn(),
72
+ isLoading: false,
73
+ formatMessage: (msg) => msg?.defaultMessage || msg?.id,
74
+ whatsappAccountFromForm: undefined,
75
+ };
76
+
77
+ const smsDomains = [
78
+ {
79
+ domainId: 11,
80
+ domainName: 'SMS Domain',
81
+ dgmId: 111,
82
+ gsmSenders: [{ value: 'GSM_A' }, { value: 'GSM_DEFAULT', default: true }],
83
+ cdmaSenders: [{ value: 'CDMA_A' }, { value: 'CDMA_DEFAULT', default: true }],
84
+ },
85
+ {
86
+ domainId: 22,
87
+ dgmId: 222,
88
+ gsmSenders: [],
89
+ cdmaSenders: [],
90
+ },
91
+ ];
92
+
93
+ const emailDomains = [
94
+ {
95
+ domainId: 31,
96
+ domainName: 'Email Domain',
97
+ dgmId: 331,
98
+ emailSenders: [
99
+ { value: 'noreply@test.com', label: 'No Reply' },
100
+ { value: 'default@test.com', label: 'Default Label', default: true },
101
+ ],
102
+ emailRepliers: [
103
+ { value: 'reply@test.com' },
104
+ { value: 'default-reply@test.com', default: true },
105
+ ],
106
+ },
107
+ ];
108
+
109
+ const whatsappDomains = [
110
+ {
111
+ domainId: 41,
112
+ sourceAccountIdentifier: 'waba-1',
113
+ gsmSenders: [{ value: '+111' }, { value: '+112' }],
114
+ },
115
+ {
116
+ domainId: 42,
117
+ sourceAccountIdentifier: 'waba-2',
118
+ gsmSenders: [{ value: '+221' }],
119
+ },
120
+ ];
121
+
122
+ const wecrmAccounts = [
123
+ { name: 'Account One', sourceAccountIdentifier: 'waba-1' },
124
+ { name: 'Account Two', sourceAccountIdentifier: 'waba-2' },
125
+ ];
126
+
127
+ const renderComponent = (props = {}) => render(
128
+ <ModifyDeliverySettings
129
+ {...defaultProps}
130
+ {...props}
131
+ />
132
+ );
133
+
134
+ const getSelects = () => screen.getAllByTestId('cap-select');
135
+ const getRawInputs = () => screen.getAllByTestId('cap-select-raw-input');
136
+
137
+ beforeEach(() => {
138
+ jest.clearAllMocks();
139
+ });
140
+
141
+ describe('loading state', () => {
142
+ it('should render CapSpin when isLoading is true', () => {
143
+ renderComponent({ isLoading: true });
144
+ expect(screen.getByTestId('cap-spin')).toBeTruthy();
145
+ expect(screen.getByText('Loading...')).toBeTruthy();
146
+ expect(screen.queryByTestId('cap-button')).toBeNull();
147
+ });
148
+ });
149
+
150
+ describe('fallbacks and unsupported channel', () => {
151
+ it('should use destructured default values when optional props are undefined', () => {
152
+ const onSave = jest.fn();
153
+
154
+ render(
155
+ <ModifyDeliverySettings
156
+ channel={CHANNELS.SMS}
157
+ deliverySettings={undefined}
158
+ senderDetailsOptions={undefined}
159
+ wecrmAccounts={undefined}
160
+ onSaveDeliverySettings={onSave}
161
+ onClose={jest.fn()}
162
+ isLoading={false}
163
+ formatMessage={(msg) => msg?.defaultMessage || msg?.id}
164
+ />
165
+ );
166
+
167
+ expect(getSelects()).toHaveLength(2);
168
+ fireEvent.click(screen.getByText('Done'));
169
+ expect(onSave).toHaveBeenCalledWith({
170
+ domainId: null,
171
+ domainGatewayMapId: null,
172
+ gsmSenderId: '',
173
+ cdmaSenderId: '',
174
+ });
175
+ });
176
+
177
+ it('should use fallback placeholder when formatMessage is not provided', () => {
178
+ renderComponent({
179
+ channel: CHANNELS.SMS,
180
+ senderDetailsOptions: smsDomains,
181
+ });
182
+ expect(getSelects()[0].getAttribute('data-placeholder')).toBe('No options');
183
+ });
184
+
185
+ it('should render no select rows for unsupported channel and still save empty object', () => {
186
+ const onSave = jest.fn();
187
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
188
+
189
+ renderComponent({
190
+ channel: 'UNSUPPORTED_CHANNEL',
191
+ onSaveDeliverySettings: onSave,
192
+ });
193
+
194
+ expect(screen.queryAllByTestId('cap-select')).toHaveLength(0);
195
+ fireEvent.click(screen.getByText('Done'));
196
+ expect(onSave).toHaveBeenCalledWith({});
197
+
198
+ consoleErrorSpy.mockRestore();
199
+ });
200
+
201
+ it('should handle locked WhatsApp lookup when wecrmAccounts is null', () => {
202
+ const onSave = jest.fn();
203
+
204
+ renderComponent({
205
+ channel: CHANNELS.WHATSAPP,
206
+ wecrmAccounts: null,
207
+ senderDetailsOptions: whatsappDomains,
208
+ whatsappAccountFromForm: { accountName: 'Account One' },
209
+ onSaveDeliverySettings: onSave,
210
+ });
211
+
212
+ fireEvent.click(screen.getByText('Done'));
213
+ expect(onSave).toHaveBeenCalledWith({
214
+ domainId: null,
215
+ senderMobNum: '',
216
+ sourceAccountIdentifier: '',
217
+ });
218
+ });
219
+
220
+ it('should evaluate account name fallback when locked WhatsApp account entry has no name', () => {
221
+ const onSave = jest.fn();
222
+
223
+ renderComponent({
224
+ channel: CHANNELS.WHATSAPP,
225
+ wecrmAccounts: [{ sourceAccountIdentifier: 'waba-1' }],
226
+ senderDetailsOptions: whatsappDomains,
227
+ whatsappAccountFromForm: { accountName: 'Account One' },
228
+ onSaveDeliverySettings: onSave,
229
+ });
230
+
231
+ fireEvent.click(screen.getByText('Done'));
232
+ expect(onSave).toHaveBeenCalledWith({
233
+ domainId: null,
234
+ senderMobNum: '',
235
+ sourceAccountIdentifier: '',
236
+ });
237
+ });
238
+ });
239
+
240
+ describe('SMS flow', () => {
241
+ it('should render SMS fields and button type', () => {
242
+ renderComponent({
243
+ channel: CHANNELS.SMS,
244
+ senderDetailsOptions: smsDomains,
245
+ });
246
+
247
+ expect(getSelects()).toHaveLength(2);
248
+ expect(screen.getByTestId('cap-button').getAttribute('data-buttontype')).toBe('primary');
249
+ });
250
+
251
+ it('should update gateway map, gsm sender, and cdma sender on SMS domain change using defaults', () => {
252
+ const onSave = jest.fn();
253
+
254
+ renderComponent({
255
+ channel: CHANNELS.SMS,
256
+ senderDetailsOptions: smsDomains,
257
+ onSaveDeliverySettings: onSave,
258
+ });
259
+
260
+ fireEvent.change(getSelects()[0], { target: { value: '11' } });
261
+ fireEvent.click(screen.getByText('Done'));
262
+
263
+ expect(onSave).toHaveBeenCalledWith({
264
+ domainId: 11,
265
+ domainGatewayMapId: 111,
266
+ gsmSenderId: 'GSM_DEFAULT',
267
+ cdmaSenderId: 'CDMA_DEFAULT',
268
+ });
269
+ });
270
+
271
+ it('should set empty sender values when SMS domain has no senders', () => {
272
+ const onSave = jest.fn();
273
+
274
+ renderComponent({
275
+ channel: CHANNELS.SMS,
276
+ senderDetailsOptions: smsDomains,
277
+ deliverySettings: {
278
+ domainId: null,
279
+ domainGatewayMapId: null,
280
+ gsmSenderId: 'OLD_GSM',
281
+ cdmaSenderId: 'OLD_CDMA',
282
+ },
283
+ onSaveDeliverySettings: onSave,
284
+ });
285
+
286
+ fireEvent.change(getSelects()[0], { target: { value: '22' } });
287
+ fireEvent.click(screen.getByText('Done'));
288
+
289
+ expect(onSave).toHaveBeenCalledWith({
290
+ domainId: 22,
291
+ domainGatewayMapId: 222,
292
+ gsmSenderId: '',
293
+ cdmaSenderId: '',
294
+ });
295
+ });
296
+
297
+ it('should keep previous gateway values when SMS domain is not found and still update domainId', () => {
298
+ const onSave = jest.fn();
299
+
300
+ renderComponent({
301
+ channel: CHANNELS.SMS,
302
+ senderDetailsOptions: smsDomains,
303
+ deliverySettings: {
304
+ domainId: 11,
305
+ domainGatewayMapId: 999,
306
+ gsmSenderId: 'KEEP_GSM',
307
+ cdmaSenderId: 'KEEP_CDMA',
308
+ },
309
+ onSaveDeliverySettings: onSave,
310
+ });
311
+
312
+ fireEvent.change(getRawInputs()[0], { target: { value: '9999' } });
313
+ fireEvent.click(screen.getByText('Done'));
314
+
315
+ expect(onSave).toHaveBeenCalledWith({
316
+ domainId: '9999',
317
+ domainGatewayMapId: 999,
318
+ gsmSenderId: 'KEEP_GSM',
319
+ cdmaSenderId: 'KEEP_CDMA',
320
+ });
321
+ });
322
+
323
+ it('should update SMS sender field directly', () => {
324
+ const onSave = jest.fn();
325
+
326
+ renderComponent({
327
+ channel: CHANNELS.SMS,
328
+ senderDetailsOptions: smsDomains,
329
+ deliverySettings: {
330
+ domainId: 11,
331
+ domainGatewayMapId: 111,
332
+ gsmSenderId: 'GSM_A',
333
+ cdmaSenderId: 'CDMA_A',
334
+ },
335
+ onSaveDeliverySettings: onSave,
336
+ });
337
+
338
+ fireEvent.change(getSelects()[1], { target: { value: 'GSM_DEFAULT' } });
339
+ fireEvent.click(screen.getByText('Done'));
340
+
341
+ expect(onSave).toHaveBeenCalledWith({
342
+ domainId: 11,
343
+ domainGatewayMapId: 111,
344
+ gsmSenderId: 'GSM_DEFAULT',
345
+ cdmaSenderId: 'CDMA_A',
346
+ });
347
+ });
348
+
349
+ it('should use sender label when gsm sender value is missing', () => {
350
+ renderComponent({
351
+ channel: CHANNELS.SMS,
352
+ senderDetailsOptions: [{
353
+ domainId: 55,
354
+ domainName: 'Label Domain',
355
+ gsmSenders: [{ label: 'Label GSM', value: '' }],
356
+ cdmaSenders: [],
357
+ }],
358
+ deliverySettings: { domainId: 55 },
359
+ });
360
+
361
+ const senderOptions = Array.from(getSelects()[1].querySelectorAll('option')).map((opt) => opt.textContent);
362
+ expect(senderOptions).toEqual(expect.arrayContaining(['Label GSM']));
363
+ });
364
+
365
+ it('should keep existing SMS domain options when TRAI DLT is not enabled', () => {
366
+ renderComponent({
367
+ channel: CHANNELS.SMS,
368
+ senderDetailsOptions: [
369
+ {
370
+ domainId: 11,
371
+ domainName: 'Domain One',
372
+ gsmSenders: [{ value: 'ID1' }],
373
+ cdmaSenders: [],
374
+ },
375
+ {
376
+ domainId: 22,
377
+ domainName: 'Domain Two',
378
+ gsmSenders: [{ value: 'ID2' }],
379
+ cdmaSenders: [],
380
+ },
381
+ ],
382
+ smsTraiDltEnabled: false,
383
+ registeredSenderIds: ['ID2'],
384
+ });
385
+
386
+ const domainOptions = Array.from(getSelects()[0].querySelectorAll('option')).map((opt) => opt.textContent);
387
+ expect(domainOptions).toEqual(expect.arrayContaining(['Domain One', 'Domain Two']));
388
+ });
389
+
390
+ it('should filter SMS domains and sender IDs when TRAI DLT is enabled', () => {
391
+ renderComponent({
392
+ channel: CHANNELS.SMS,
393
+ senderDetailsOptions: [
394
+ {
395
+ domainId: 11,
396
+ domainName: 'Domain One',
397
+ dgmId: 111,
398
+ gsmSenders: [{ value: 'ID1' }, { value: 'ID2' }],
399
+ cdmaSenders: [],
400
+ },
401
+ {
402
+ domainId: 22,
403
+ domainName: 'Domain Two',
404
+ dgmId: 222,
405
+ gsmSenders: [{ value: 'ID3' }],
406
+ cdmaSenders: [],
407
+ },
408
+ ],
409
+ smsTraiDltEnabled: true,
410
+ registeredSenderIds: ['ID2'],
411
+ });
412
+
413
+ const domainOptions = Array.from(getSelects()[0].querySelectorAll('option')).map((opt) => opt.textContent);
414
+ expect(domainOptions).toEqual(expect.arrayContaining(['Domain One']));
415
+ expect(domainOptions).not.toEqual(expect.arrayContaining(['Domain Two']));
416
+
417
+ fireEvent.change(getSelects()[0], { target: { value: '11' } });
418
+ const senderOptions = Array.from(getSelects()[1].querySelectorAll('option')).map((opt) => opt.textContent);
419
+ expect(senderOptions).toEqual(expect.arrayContaining(['ID2']));
420
+ expect(senderOptions).not.toEqual(expect.arrayContaining(['ID1']));
421
+ });
422
+
423
+ it('should default to the first allowed GSM sender when SMS domain changes in TRAI DLT mode', () => {
424
+ const onSave = jest.fn();
425
+
426
+ renderComponent({
427
+ channel: CHANNELS.SMS,
428
+ senderDetailsOptions: [
429
+ {
430
+ domainId: 11,
431
+ domainName: 'Domain One',
432
+ dgmId: 111,
433
+ gsmSenders: [{ value: 'ID1' }, { value: 'ID2' }],
434
+ cdmaSenders: [{ value: 'CDMA1' }],
435
+ },
436
+ ],
437
+ smsTraiDltEnabled: true,
438
+ registeredSenderIds: ['ID2'],
439
+ onSaveDeliverySettings: onSave,
440
+ });
441
+
442
+ fireEvent.change(getSelects()[0], { target: { value: '11' } });
443
+ fireEvent.click(screen.getByText('Done'));
444
+
445
+ expect(onSave).toHaveBeenCalledWith({
446
+ domainId: 11,
447
+ domainGatewayMapId: 111,
448
+ gsmSenderId: 'ID2',
449
+ cdmaSenderId: 'CDMA1',
450
+ });
451
+ });
452
+
453
+ it('should show no SMS domain options when TRAI DLT is enabled and no registered sender ID matches', () => {
454
+ renderComponent({
455
+ channel: CHANNELS.SMS,
456
+ senderDetailsOptions: [
457
+ {
458
+ domainId: 11,
459
+ domainName: 'Domain One',
460
+ gsmSenders: [{ value: 'ID1' }],
461
+ cdmaSenders: [],
462
+ },
463
+ ],
464
+ smsTraiDltEnabled: true,
465
+ registeredSenderIds: ['UNMATCHED'],
466
+ });
467
+
468
+ const domainOptions = Array.from(getSelects()[0].querySelectorAll('option')).map((opt) => opt.textContent);
469
+ expect(domainOptions).not.toEqual(expect.arrayContaining(['Domain One']));
470
+ });
471
+ });
472
+
473
+ describe('EMAIL flow', () => {
474
+ it('should render EMAIL fields', () => {
475
+ renderComponent({
476
+ channel: CHANNELS.EMAIL,
477
+ senderDetailsOptions: emailDomains,
478
+ });
479
+
480
+ expect(getSelects()).toHaveLength(4);
481
+ });
482
+
483
+ it('should update email defaults on domain change', () => {
484
+ const onSave = jest.fn();
485
+
486
+ renderComponent({
487
+ channel: CHANNELS.EMAIL,
488
+ senderDetailsOptions: emailDomains,
489
+ onSaveDeliverySettings: onSave,
490
+ });
491
+
492
+ fireEvent.change(getSelects()[0], { target: { value: '31' } });
493
+ fireEvent.click(screen.getByText('Done'));
494
+
495
+ expect(onSave).toHaveBeenCalledWith({
496
+ domainId: 31,
497
+ domainGatewayMapId: 331,
498
+ senderEmail: 'default@test.com',
499
+ senderLabel: 'Default Label',
500
+ senderReplyTo: 'default-reply@test.com',
501
+ });
502
+ });
503
+
504
+ it('should update sender email and fallback label from sender id selection', () => {
505
+ const onSave = jest.fn();
506
+
507
+ renderComponent({
508
+ channel: CHANNELS.EMAIL,
509
+ senderDetailsOptions: [{
510
+ ...emailDomains[0],
511
+ emailSenders: [{ value: 'solo@test.com' }],
512
+ }],
513
+ deliverySettings: { domainId: 31, domainGatewayMapId: 331 },
514
+ onSaveDeliverySettings: onSave,
515
+ });
516
+
517
+ fireEvent.change(getSelects()[1], { target: { value: 'solo@test.com' } });
518
+ fireEvent.click(screen.getByText('Done'));
519
+
520
+ expect(onSave).toHaveBeenCalledWith({
521
+ domainId: 31,
522
+ domainGatewayMapId: 331,
523
+ senderEmail: 'solo@test.com',
524
+ senderLabel: 'solo@test.com',
525
+ senderReplyTo: '',
526
+ });
527
+ });
528
+
529
+ it('should update sender email and sender label from sender name selection when match exists', () => {
530
+ const onSave = jest.fn();
531
+
532
+ renderComponent({
533
+ channel: CHANNELS.EMAIL,
534
+ senderDetailsOptions: emailDomains,
535
+ deliverySettings: { domainId: 31, domainGatewayMapId: 331 },
536
+ onSaveDeliverySettings: onSave,
537
+ });
538
+
539
+ fireEvent.change(getSelects()[2], { target: { value: 'No Reply' } });
540
+ fireEvent.click(screen.getByText('Done'));
541
+
542
+ expect(onSave).toHaveBeenCalledWith({
543
+ domainId: 31,
544
+ domainGatewayMapId: 331,
545
+ senderEmail: 'noreply@test.com',
546
+ senderLabel: 'No Reply',
547
+ senderReplyTo: '',
548
+ });
549
+ });
550
+
551
+ it('should update only sender label when sender name selection has no match', () => {
552
+ const onSave = jest.fn();
553
+
554
+ renderComponent({
555
+ channel: CHANNELS.EMAIL,
556
+ senderDetailsOptions: emailDomains,
557
+ deliverySettings: {
558
+ domainId: 31,
559
+ domainGatewayMapId: 331,
560
+ senderEmail: 'keep@test.com',
561
+ },
562
+ onSaveDeliverySettings: onSave,
563
+ });
564
+
565
+ fireEvent.change(getRawInputs()[2], { target: { value: 'Custom Label' } });
566
+ fireEvent.click(screen.getByText('Done'));
567
+
568
+ expect(onSave).toHaveBeenCalledWith({
569
+ domainId: 31,
570
+ domainGatewayMapId: 331,
571
+ senderEmail: 'keep@test.com',
572
+ senderLabel: 'Custom Label',
573
+ senderReplyTo: '',
574
+ });
575
+ });
576
+
577
+ it('should update reply-to selection directly', () => {
578
+ const onSave = jest.fn();
579
+
580
+ renderComponent({
581
+ channel: CHANNELS.EMAIL,
582
+ senderDetailsOptions: emailDomains,
583
+ deliverySettings: { domainId: 31, domainGatewayMapId: 331 },
584
+ onSaveDeliverySettings: onSave,
585
+ });
586
+
587
+ fireEvent.change(getSelects()[3], { target: { value: 'reply@test.com' } });
588
+ fireEvent.click(screen.getByText('Done'));
589
+
590
+ expect(onSave).toHaveBeenCalledWith({
591
+ domainId: 31,
592
+ domainGatewayMapId: 331,
593
+ senderEmail: '',
594
+ senderLabel: '',
595
+ senderReplyTo: 'reply@test.com',
596
+ });
597
+ });
598
+
599
+ it('should use fallback labels for email sender name and reply-to options', () => {
600
+ renderComponent({
601
+ channel: CHANNELS.EMAIL,
602
+ senderDetailsOptions: [{
603
+ domainId: 88,
604
+ domainName: 'Fallback Email Domain',
605
+ emailSenders: [{ value: 'fallback@test.com' }],
606
+ emailRepliers: [{ label: 'Reply Label', value: '' }],
607
+ }],
608
+ deliverySettings: { domainId: 88 },
609
+ });
610
+
611
+ const senderNameOptions = Array.from(getSelects()[2].querySelectorAll('option')).map((opt) => opt.textContent);
612
+ const replyToOptions = Array.from(getSelects()[3].querySelectorAll('option')).map((opt) => opt.textContent);
613
+
614
+ expect(senderNameOptions).toEqual(expect.arrayContaining(['fallback@test.com']));
615
+ expect(replyToOptions).toEqual(expect.arrayContaining(['Reply Label']));
616
+ });
617
+
618
+ it('should use reply-to label when value is empty in reply-to options', () => {
619
+ renderComponent({
620
+ channel: CHANNELS.EMAIL,
621
+ senderDetailsOptions: [{
622
+ domainId: 90,
623
+ domainName: 'Reply Label Domain',
624
+ emailSenders: [{ value: 'a@test.com' }],
625
+ emailRepliers: [{ label: 'OnlyLabel', value: '' }],
626
+ }],
627
+ deliverySettings: { domainId: 90, domainGatewayMapId: 1 },
628
+ });
629
+
630
+ const replyToOptions = Array.from(getSelects()[3].querySelectorAll('option')).map((opt) => opt.textContent);
631
+ expect(replyToOptions).toEqual(expect.arrayContaining(['OnlyLabel']));
632
+ });
633
+ });
634
+
635
+ describe('WHATSAPP flow', () => {
636
+ it('should render WhatsApp fields', () => {
637
+ renderComponent({
638
+ channel: CHANNELS.WHATSAPP,
639
+ senderDetailsOptions: whatsappDomains,
640
+ wecrmAccounts,
641
+ });
642
+
643
+ expect(getSelects()).toHaveLength(2);
644
+ });
645
+
646
+ it('should show all sender numbers when no account is selected', () => {
647
+ renderComponent({
648
+ channel: CHANNELS.WHATSAPP,
649
+ senderDetailsOptions: whatsappDomains,
650
+ wecrmAccounts,
651
+ });
652
+
653
+ const senderSelect = getSelects()[1];
654
+ const optionTexts = Array.from(senderSelect.querySelectorAll('option')).map((opt) => opt.textContent);
655
+
656
+ expect(optionTexts).toEqual(expect.arrayContaining(['+111', '+112', '+221']));
657
+ });
658
+
659
+ it('should show sender numbers only for selected account', () => {
660
+ renderComponent({
661
+ channel: CHANNELS.WHATSAPP,
662
+ senderDetailsOptions: whatsappDomains,
663
+ wecrmAccounts,
664
+ deliverySettings: { sourceAccountIdentifier: 'waba-2', senderMobNum: '' },
665
+ });
666
+
667
+ const senderSelect = getSelects()[1];
668
+ const optionTexts = Array.from(senderSelect.querySelectorAll('option')).map((opt) => opt.textContent);
669
+
670
+ expect(optionTexts).toEqual(expect.arrayContaining(['+221']));
671
+ expect(optionTexts).not.toEqual(expect.arrayContaining(['+111']));
672
+ });
673
+
674
+ it('should update account and sender number when account selection changes', () => {
675
+ const onSave = jest.fn();
676
+
677
+ renderComponent({
678
+ channel: CHANNELS.WHATSAPP,
679
+ senderDetailsOptions: whatsappDomains,
680
+ wecrmAccounts,
681
+ onSaveDeliverySettings: onSave,
682
+ });
683
+
684
+ fireEvent.change(getSelects()[0], { target: { value: 'waba-2' } });
685
+ fireEvent.click(screen.getByText('Done'));
686
+
687
+ expect(onSave).toHaveBeenCalledWith({
688
+ domainId: null,
689
+ senderMobNum: '+221',
690
+ sourceAccountIdentifier: 'waba-2',
691
+ });
692
+ });
693
+
694
+ it('should set empty sender number when selected WhatsApp account has no matching domain', () => {
695
+ const onSave = jest.fn();
696
+
697
+ renderComponent({
698
+ channel: CHANNELS.WHATSAPP,
699
+ senderDetailsOptions: whatsappDomains,
700
+ wecrmAccounts,
701
+ onSaveDeliverySettings: onSave,
702
+ });
703
+
704
+ fireEvent.change(getRawInputs()[0], { target: { value: 'missing-account' } });
705
+ fireEvent.click(screen.getByText('Done'));
706
+
707
+ expect(onSave).toHaveBeenCalledWith({
708
+ domainId: null,
709
+ senderMobNum: '',
710
+ sourceAccountIdentifier: 'missing-account',
711
+ });
712
+ });
713
+
714
+ it('should update sender number directly', () => {
715
+ const onSave = jest.fn();
716
+
717
+ renderComponent({
718
+ channel: CHANNELS.WHATSAPP,
719
+ senderDetailsOptions: whatsappDomains,
720
+ wecrmAccounts,
721
+ deliverySettings: { sourceAccountIdentifier: 'waba-1', senderMobNum: '+111' },
722
+ onSaveDeliverySettings: onSave,
723
+ });
724
+
725
+ fireEvent.change(getSelects()[1], { target: { value: '+112' } });
726
+ fireEvent.click(screen.getByText('Done'));
727
+
728
+ expect(onSave).toHaveBeenCalledWith({
729
+ domainId: null,
730
+ senderMobNum: '+112',
731
+ sourceAccountIdentifier: 'waba-1',
732
+ });
733
+ });
734
+
735
+ it('should lock account select, show disabled message, and seed settings from matching form account', () => {
736
+ const onSave = jest.fn();
737
+
738
+ renderComponent({
739
+ channel: CHANNELS.WHATSAPP,
740
+ senderDetailsOptions: whatsappDomains,
741
+ wecrmAccounts,
742
+ whatsappAccountFromForm: { accountName: 'Account One' },
743
+ onSaveDeliverySettings: onSave,
744
+ });
745
+
746
+ const [accountSelect] = getSelects();
747
+ expect(accountSelect.disabled).toBe(true);
748
+ expect(accountSelect.getAttribute('data-component-classname')).toContain('modify-delivery-settings__select--disabled');
749
+ expect(screen.getByText('Message template selected belongs to this account')).toBeTruthy();
750
+
751
+ fireEvent.click(screen.getByText('Done'));
752
+
753
+ expect(onSave).toHaveBeenCalledWith({
754
+ domainId: null,
755
+ senderMobNum: '+111',
756
+ sourceAccountIdentifier: 'waba-1',
757
+ });
758
+ });
759
+
760
+ it('should keep previous state when locked account already matches source account identifier', () => {
761
+ const onSave = jest.fn();
762
+
763
+ renderComponent({
764
+ channel: CHANNELS.WHATSAPP,
765
+ senderDetailsOptions: whatsappDomains,
766
+ wecrmAccounts,
767
+ whatsappAccountFromForm: { accountName: 'Account One' },
768
+ deliverySettings: {
769
+ domainId: null,
770
+ senderMobNum: 'KEEP_ME',
771
+ sourceAccountIdentifier: 'waba-1',
772
+ },
773
+ onSaveDeliverySettings: onSave,
774
+ });
775
+
776
+ fireEvent.click(screen.getByText('Done'));
777
+
778
+ expect(onSave).toHaveBeenCalledWith({
779
+ domainId: null,
780
+ senderMobNum: 'KEEP_ME',
781
+ sourceAccountIdentifier: 'waba-1',
782
+ });
783
+ });
784
+
785
+ it('should show fallback account option when locked account name is not found in wecrm accounts', () => {
786
+ renderComponent({
787
+ channel: CHANNELS.WHATSAPP,
788
+ senderDetailsOptions: whatsappDomains,
789
+ wecrmAccounts,
790
+ whatsappAccountFromForm: { accountName: 'Unknown Account' },
791
+ });
792
+
793
+ const accountSelect = getSelects()[0];
794
+ const optionTexts = Array.from(accountSelect.querySelectorAll('option')).map((opt) => opt.textContent);
795
+
796
+ expect(optionTexts).toEqual(expect.arrayContaining(['Unknown Account']));
797
+ });
798
+
799
+ it('should not update source account when locked account has no matching source identifier', () => {
800
+ const onSave = jest.fn();
801
+
802
+ renderComponent({
803
+ channel: CHANNELS.WHATSAPP,
804
+ senderDetailsOptions: whatsappDomains,
805
+ wecrmAccounts: [{ name: 'Broken Account' }],
806
+ whatsappAccountFromForm: { accountName: 'Broken Account' },
807
+ onSaveDeliverySettings: onSave,
808
+ });
809
+
810
+ fireEvent.click(screen.getByText('Done'));
811
+
812
+ expect(onSave).toHaveBeenCalledWith({
813
+ domainId: null,
814
+ senderMobNum: '',
815
+ sourceAccountIdentifier: '',
816
+ });
817
+ });
818
+
819
+ it('should use sourceAccountIdentifier as account label when account name is missing', () => {
820
+ renderComponent({
821
+ channel: CHANNELS.WHATSAPP,
822
+ senderDetailsOptions: whatsappDomains,
823
+ wecrmAccounts: [{ sourceAccountIdentifier: 'fallback-waba' }],
824
+ });
825
+
826
+ const accountOptions = Array.from(getSelects()[0].querySelectorAll('option')).map((opt) => opt.textContent);
827
+ expect(accountOptions).toEqual(expect.arrayContaining(['fallback-waba']));
828
+ });
829
+
830
+ it('should keep sender number unchanged when locked account maps to domain without first sender value', () => {
831
+ const onSave = jest.fn();
832
+
833
+ renderComponent({
834
+ channel: CHANNELS.WHATSAPP,
835
+ senderDetailsOptions: [{
836
+ sourceAccountIdentifier: 'waba-1',
837
+ gsmSenders: [{}],
838
+ }],
839
+ wecrmAccounts: [{ name: 'Account One', sourceAccountIdentifier: 'waba-1' }],
840
+ whatsappAccountFromForm: { accountName: 'Account One' },
841
+ deliverySettings: {
842
+ domainId: null,
843
+ senderMobNum: 'KEEP_NUMBER',
844
+ sourceAccountIdentifier: '',
845
+ },
846
+ onSaveDeliverySettings: onSave,
847
+ });
848
+
849
+ fireEvent.click(screen.getByText('Done'));
850
+
851
+ expect(onSave).toHaveBeenCalledWith({
852
+ domainId: null,
853
+ senderMobNum: 'KEEP_NUMBER',
854
+ sourceAccountIdentifier: 'waba-1',
855
+ });
856
+ });
857
+
858
+ it('should render disabled CapLabel with label3 for locked WhatsApp account row', () => {
859
+ renderComponent({
860
+ channel: CHANNELS.WHATSAPP,
861
+ senderDetailsOptions: whatsappDomains,
862
+ wecrmAccounts,
863
+ whatsappAccountFromForm: { accountName: 'Account One' },
864
+ });
865
+
866
+ const labels = screen.getAllByTestId('cap-label');
867
+ const disabledHint = labels.find((el) => el.getAttribute('data-type') === 'label3');
868
+ expect(disabledHint).toBeTruthy();
869
+ });
870
+ });
871
+
872
+ describe('Done and close handling', () => {
873
+ it('should call onSaveDeliverySettings with channel defaults when nothing changes', () => {
874
+ const onSave = jest.fn();
875
+
876
+ renderComponent({
877
+ channel: CHANNELS.SMS,
878
+ onSaveDeliverySettings: onSave,
879
+ onClose: jest.fn(),
880
+ });
881
+
882
+ fireEvent.click(screen.getByText('Done'));
883
+
884
+ expect(onSave).toHaveBeenCalledTimes(1);
885
+ expect(onSave).toHaveBeenCalledWith({
886
+ domainId: null,
887
+ domainGatewayMapId: null,
888
+ gsmSenderId: '',
889
+ cdmaSenderId: '',
890
+ });
891
+ });
892
+
893
+ it('should call onClose when Done is clicked if onClose is provided', () => {
894
+ const onClose = jest.fn();
895
+
896
+ renderComponent({
897
+ onSaveDeliverySettings: jest.fn(),
898
+ onClose,
899
+ });
900
+
901
+ fireEvent.click(screen.getByText('Done'));
902
+ expect(onClose).toHaveBeenCalled();
903
+ });
904
+
905
+ it('should save successfully when onClose is not provided', () => {
906
+ const onSave = jest.fn();
907
+
908
+ renderComponent({
909
+ onSaveDeliverySettings: onSave,
910
+ onClose: undefined,
911
+ });
912
+
913
+ fireEvent.click(screen.getByText('Done'));
914
+ expect(onSave).toHaveBeenCalled();
915
+ });
916
+ });
917
+
918
+ describe('RCS flow', () => {
919
+ const rcsDomains = [
920
+ {
921
+ domainId: 51,
922
+ domainName: 'RCS Domain A',
923
+ dgmId: 511,
924
+ gsmSenders: [{ value: 'RCS_GSM_1' }, { value: 'RCS_GSM_2' }],
925
+ },
926
+ {
927
+ domainId: 52,
928
+ domainName: 'RCS Domain B',
929
+ dgmId: 522,
930
+ gsmSenders: [{ value: 'RCS_OTHER' }],
931
+ },
932
+ ];
933
+
934
+ const smsFallbackDomains = [
935
+ {
936
+ domainId: 61,
937
+ domainName: 'SMS Fallback',
938
+ gsmSenders: [{ value: 'SMS_FB_1' }],
939
+ },
940
+ ];
941
+
942
+ it('should render RCS domain and sender selects', () => {
943
+ renderComponent({
944
+ channel: CHANNELS.RCS,
945
+ senderDetailsOptions: rcsDomains,
946
+ });
947
+ expect(getSelects()).toHaveLength(2);
948
+ });
949
+
950
+ it('should set composite gsm sender id when RCS domain changes', () => {
951
+ const onSave = jest.fn();
952
+
953
+ renderComponent({
954
+ channel: CHANNELS.RCS,
955
+ senderDetailsOptions: rcsDomains,
956
+ onSaveDeliverySettings: onSave,
957
+ });
958
+
959
+ fireEvent.change(getSelects()[0], { target: { value: '51' } });
960
+ fireEvent.click(screen.getByText('Done'));
961
+
962
+ expect(onSave).toHaveBeenCalledWith(
963
+ expect.objectContaining({
964
+ domainId: 51,
965
+ gsmSenderId: '51|RCS_GSM_1',
966
+ }),
967
+ );
968
+ });
969
+
970
+ it('should render SMS fallback domain and sender when fallback options exist', () => {
971
+ renderComponent({
972
+ channel: CHANNELS.RCS,
973
+ senderDetailsOptions: rcsDomains,
974
+ smsFallbackSenderDetailsOptions: smsFallbackDomains,
975
+ });
976
+ expect(getSelects()).toHaveLength(4);
977
+ });
978
+
979
+ it('should strip gsm and fallback sender ids that are not in current options on Done', () => {
980
+ const onSave = jest.fn();
981
+
982
+ renderComponent({
983
+ channel: CHANNELS.RCS,
984
+ senderDetailsOptions: rcsDomains,
985
+ smsFallbackSenderDetailsOptions: smsFallbackDomains,
986
+ deliverySettings: {
987
+ domainId: 51,
988
+ gsmSenderId: 'not-a-valid-composite',
989
+ smsFallbackDomainId: 61,
990
+ cdmaSenderId: '61|SMS_FB_1',
991
+ },
992
+ onSaveDeliverySettings: onSave,
993
+ });
994
+
995
+ fireEvent.click(screen.getByText('Done'));
996
+
997
+ expect(onSave).toHaveBeenCalledWith(
998
+ expect.objectContaining({
999
+ domainId: 51,
1000
+ gsmSenderId: '',
1001
+ smsFallbackDomainId: 61,
1002
+ cdmaSenderId: '61|SMS_FB_1',
1003
+ }),
1004
+ );
1005
+ });
1006
+
1007
+ it('should strip invalid SMS fallback composite sender on Done', () => {
1008
+ const onSave = jest.fn();
1009
+
1010
+ renderComponent({
1011
+ channel: CHANNELS.RCS,
1012
+ senderDetailsOptions: rcsDomains,
1013
+ smsFallbackSenderDetailsOptions: smsFallbackDomains,
1014
+ deliverySettings: {
1015
+ domainId: 51,
1016
+ gsmSenderId: '51|RCS_GSM_1',
1017
+ smsFallbackDomainId: 61,
1018
+ cdmaSenderId: 'bogus|value',
1019
+ },
1020
+ onSaveDeliverySettings: onSave,
1021
+ });
1022
+
1023
+ fireEvent.click(screen.getByText('Done'));
1024
+
1025
+ expect(onSave).toHaveBeenCalledWith(
1026
+ expect.objectContaining({
1027
+ gsmSenderId: '51|RCS_GSM_1',
1028
+ cdmaSenderId: '',
1029
+ }),
1030
+ );
1031
+ });
1032
+
1033
+ it('should filter SMS fallback domains and GSM senders when TRAI DLT and registered sender IDs apply', () => {
1034
+ const fallbackDlt = [
1035
+ {
1036
+ domainId: 71,
1037
+ domainName: 'Has Registered GSM',
1038
+ gsmSenders: [{ value: 'REG_OK' }, { value: 'NOT_ON_DLT' }],
1039
+ },
1040
+ {
1041
+ domainId: 72,
1042
+ domainName: 'No Match',
1043
+ gsmSenders: [{ value: 'UNMATCHED' }],
1044
+ },
1045
+ ];
1046
+ const onSave = jest.fn();
1047
+
1048
+ renderComponent({
1049
+ channel: CHANNELS.RCS,
1050
+ senderDetailsOptions: rcsDomains,
1051
+ smsFallbackSenderDetailsOptions: fallbackDlt,
1052
+ smsTraiDltEnabled: true,
1053
+ registeredSenderIds: ['REG_OK'],
1054
+ deliverySettings: {
1055
+ domainId: 51,
1056
+ gsmSenderId: '51|RCS_GSM_1',
1057
+ smsFallbackDomainId: 71,
1058
+ cdmaSenderId: '71|REG_OK',
1059
+ },
1060
+ onSaveDeliverySettings: onSave,
1061
+ });
1062
+
1063
+ fireEvent.click(screen.getByText('Done'));
1064
+
1065
+ expect(onSave).toHaveBeenCalledWith(
1066
+ expect.objectContaining({
1067
+ smsFallbackDomainId: 71,
1068
+ cdmaSenderId: '71|REG_OK',
1069
+ }),
1070
+ );
1071
+ });
1072
+
1073
+ it('should set SMS fallback composite to first GSM after skipping domain-name echo row', () => {
1074
+ const fallbackEcho = [
1075
+ {
1076
+ domainId: 80,
1077
+ domainName: 'Echo',
1078
+ gsmSenders: [{ value: 'Echo' }, { value: 'REAL_SENDER' }],
1079
+ },
1080
+ ];
1081
+ const onSave = jest.fn();
1082
+
1083
+ renderComponent({
1084
+ channel: CHANNELS.RCS,
1085
+ senderDetailsOptions: rcsDomains,
1086
+ smsFallbackSenderDetailsOptions: fallbackEcho,
1087
+ deliverySettings: {
1088
+ domainId: 51,
1089
+ gsmSenderId: '51|RCS_GSM_1',
1090
+ },
1091
+ onSaveDeliverySettings: onSave,
1092
+ });
1093
+
1094
+ fireEvent.change(getSelects()[2], { target: { value: '80' } });
1095
+ fireEvent.click(screen.getByText('Done'));
1096
+
1097
+ expect(onSave).toHaveBeenCalledWith(
1098
+ expect.objectContaining({
1099
+ smsFallbackDomainId: 80,
1100
+ cdmaSenderId: '80|REAL_SENDER',
1101
+ }),
1102
+ );
1103
+ });
1104
+
1105
+ it('should only list RCS sender options for the selected RCS domain', () => {
1106
+ const onSave = jest.fn();
1107
+
1108
+ renderComponent({
1109
+ channel: CHANNELS.RCS,
1110
+ senderDetailsOptions: rcsDomains,
1111
+ onSaveDeliverySettings: onSave,
1112
+ deliverySettings: {
1113
+ domainId: 52,
1114
+ gsmSenderId: '52|RCS_OTHER',
1115
+ },
1116
+ });
1117
+
1118
+ fireEvent.click(screen.getByText('Done'));
1119
+
1120
+ expect(onSave).toHaveBeenCalledWith(
1121
+ expect.objectContaining({
1122
+ domainId: 52,
1123
+ gsmSenderId: '52|RCS_OTHER',
1124
+ }),
1125
+ );
1126
+ });
1127
+
1128
+ it('should set composite RCS gsm when domain id is numeric zero', () => {
1129
+ const rcsWithZero = [
1130
+ {
1131
+ domainId: 0,
1132
+ domainName: 'Zero Id',
1133
+ dgmId: 1,
1134
+ gsmSenders: [{ value: 'Z_GSM' }],
1135
+ },
1136
+ ];
1137
+ const onSave = jest.fn();
1138
+
1139
+ renderComponent({
1140
+ channel: CHANNELS.RCS,
1141
+ senderDetailsOptions: rcsWithZero,
1142
+ onSaveDeliverySettings: onSave,
1143
+ });
1144
+
1145
+ fireEvent.change(getSelects()[0], { target: { value: '0' } });
1146
+ fireEvent.click(screen.getByText('Done'));
1147
+
1148
+ expect(onSave).toHaveBeenCalledWith(
1149
+ expect.objectContaining({
1150
+ domainId: 0,
1151
+ gsmSenderId: '0|Z_GSM',
1152
+ }),
1153
+ );
1154
+ });
1155
+ });
1156
+ });