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.
- package/.github/workflows/pr-title-check.yml +88 -0
- package/app/constants/unified.js +21 -1
- package/app/containers/App/constants.js +0 -1
- package/app/containers/Login/test/index.test.js +123 -0
- package/app/containers/Login/test/selectors.test.js +165 -0
- package/app/initialState.js +0 -2
- package/app/services/api.js +6 -0
- package/app/services/tests/api.test.js +7 -0
- package/app/services/tests/getSchema.test.js +95 -0
- package/app/utils/common.js +23 -9
- package/app/utils/commonUtils.js +64 -93
- package/app/utils/tagValidations.js +83 -219
- package/app/utils/templateVarUtils.js +172 -0
- package/app/utils/tests/common.test.js +265 -323
- package/app/utils/tests/commonUtil.test.js +461 -118
- package/app/utils/tests/commonUtils.test.js +581 -0
- package/app/utils/tests/messageUtils.test.js +95 -0
- package/app/utils/tests/smsCharCount.test.js +304 -0
- package/app/utils/tests/smsCharCountV2.test.js +213 -10
- package/app/utils/tests/tagValidations.test.js +474 -357
- package/app/utils/tests/templateVarUtils.test.js +160 -0
- package/app/v2Components/CapDeviceContent/index.js +10 -7
- package/app/v2Components/CapTagList/index.js +32 -24
- package/app/v2Components/CapTagList/style.scss +48 -0
- package/app/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +63 -0
- package/app/v2Components/CapTagListWithInput/index.js +8 -0
- package/app/v2Components/CapWhatsappCTA/index.js +2 -0
- package/app/v2Components/CapWhatsappCarouselButton/index.js +32 -14
- package/app/v2Components/CapWhatsappCarouselButton/tests/index.test.js +120 -2
- package/app/v2Components/CommonTestAndPreview/CustomValuesEditor.js +70 -49
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/DeliverySettings.scss +39 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.js +606 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/ModifyDeliverySettings.scss +36 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/constants.js +79 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/index.js +314 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/messages.js +141 -0
- package/app/v2Components/CommonTestAndPreview/DeliverySettings/utils/parseSenderDetailsResponse.js +156 -0
- package/app/v2Components/CommonTestAndPreview/SendTestMessage.js +57 -1
- package/app/v2Components/CommonTestAndPreview/UnifiedPreview/_unifiedPreview.scss +20 -1
- package/app/v2Components/CommonTestAndPreview/UnifiedPreview/index.js +133 -4
- package/app/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +210 -4
- package/app/v2Components/CommonTestAndPreview/actions.js +20 -0
- package/app/v2Components/CommonTestAndPreview/constants.js +57 -1
- package/app/v2Components/CommonTestAndPreview/index.js +878 -156
- package/app/v2Components/CommonTestAndPreview/messages.js +41 -3
- package/app/v2Components/CommonTestAndPreview/previewApiUtils.js +59 -0
- package/app/v2Components/CommonTestAndPreview/reducer.js +47 -0
- package/app/v2Components/CommonTestAndPreview/sagas.js +75 -5
- package/app/v2Components/CommonTestAndPreview/selectors.js +51 -0
- package/app/v2Components/CommonTestAndPreview/tests/CustomValuesEditor.test.js +352 -0
- package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/ModifyDeliverySettings.test.js +1156 -0
- package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/index.test.js +334 -0
- package/app/v2Components/CommonTestAndPreview/tests/DeliverySettings/utils/parseSenderDetailsResponse.test.js +576 -0
- package/app/v2Components/CommonTestAndPreview/tests/SendTestMessage.test.js +156 -0
- package/app/v2Components/CommonTestAndPreview/tests/UnifiedPreview/index.test.js +199 -1
- package/app/v2Components/CommonTestAndPreview/tests/actions.test.js +50 -0
- package/app/v2Components/CommonTestAndPreview/tests/constants.test.js +18 -7
- package/app/v2Components/CommonTestAndPreview/tests/index.test.js +914 -5
- package/app/v2Components/CommonTestAndPreview/tests/previewApiUtils.test.js +67 -0
- package/app/v2Components/CommonTestAndPreview/tests/reducer.test.js +118 -0
- package/app/v2Components/CommonTestAndPreview/tests/sagas.test.js +146 -378
- package/app/v2Components/CommonTestAndPreview/tests/selectors.test.js +146 -0
- package/app/v2Components/ErrorInfoNote/index.js +24 -26
- package/app/v2Components/FormBuilder/index.js +182 -204
- package/app/v2Components/FormBuilder/messages.js +4 -8
- package/app/v2Components/HtmlEditor/HTMLEditor.js +7 -6
- package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +1 -1
- package/app/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +928 -17
- package/app/v2Components/HtmlEditor/components/CodeEditorPane/index.js +4 -2
- package/app/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +452 -3
- package/app/v2Components/HtmlEditor/hooks/useValidation.js +12 -9
- package/app/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +132 -0
- package/app/v2Components/HtmlEditor/utils/htmlValidator.js +4 -2
- package/app/v2Components/SmsFallback/SmsFallbackLocalSelector.js +87 -0
- package/app/v2Components/SmsFallback/constants.js +73 -0
- package/app/v2Components/SmsFallback/index.js +956 -0
- package/app/v2Components/SmsFallback/index.scss +265 -0
- package/app/v2Components/SmsFallback/messages.js +78 -0
- package/app/v2Components/SmsFallback/smsFallbackUtils.js +107 -0
- package/app/v2Components/SmsFallback/tests/SmsFallbackLocalSelector.test.js +50 -0
- package/app/v2Components/SmsFallback/tests/rcsSmsFallback.acceptance.test.js +147 -0
- package/app/v2Components/SmsFallback/tests/smsFallbackHandlers.test.js +304 -0
- package/app/v2Components/SmsFallback/tests/smsFallbackUi.test.js +197 -0
- package/app/v2Components/SmsFallback/tests/smsFallbackUtils.test.js +261 -0
- package/app/v2Components/SmsFallback/tests/useLocalTemplateList.test.js +422 -0
- package/app/v2Components/SmsFallback/useLocalTemplateList.js +92 -0
- package/app/v2Components/TestAndPreviewSlidebox/index.js +22 -1
- package/app/v2Components/TestAndPreviewSlidebox/sagas.js +11 -4
- package/app/v2Components/TestAndPreviewSlidebox/tests/saga.test.js +3 -1
- package/app/v2Components/VarSegmentMessageEditor/constants.js +2 -0
- package/app/v2Components/VarSegmentMessageEditor/index.js +125 -0
- package/app/v2Components/VarSegmentMessageEditor/index.scss +46 -0
- package/app/v2Containers/BeeEditor/index.js +3 -0
- package/app/v2Containers/BeePopupEditor/index.js +9 -2
- package/app/v2Containers/Cap/mockData.js +0 -14
- package/app/v2Containers/Cap/reducer.js +3 -55
- package/app/v2Containers/Cap/tests/reducer.test.js +0 -102
- package/app/v2Containers/CommunicationFlow/CommunicationFlow.js +291 -0
- package/app/v2Containers/CommunicationFlow/CommunicationFlow.scss +25 -0
- package/app/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +255 -0
- package/app/v2Containers/CommunicationFlow/constants.js +200 -0
- package/app/v2Containers/CommunicationFlow/index.js +102 -0
- package/app/v2Containers/CommunicationFlow/messages.js +346 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +522 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +170 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +796 -0
- package/app/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +95 -0
- package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/Tests/CommunicationStrategyStep.test.js +133 -0
- package/app/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +289 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.scss +70 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.js +319 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/SenderDetails.scss +69 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +616 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/SenderDetails.test.js +577 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/deliverySettingsConfig.test.js +1111 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/deliverySettingsConfig.js +696 -0
- package/app/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/index.js +7 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.js +102 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/DynamicControlsStep.scss +36 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/Tests/DynamicControlsStep.test.js +91 -0
- package/app/v2Containers/CommunicationFlow/steps/DynamicControlsStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/MessageTypeStep.js +86 -0
- package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/Tests/MessageTypeStep.test.js +100 -0
- package/app/v2Containers/CommunicationFlow/steps/MessageTypeStep/index.js +5 -0
- package/app/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +30 -0
- package/app/v2Containers/CreativesContainer/CreativesSlideBoxWrapper.js +43 -0
- package/app/v2Containers/CreativesContainer/SlideBoxContent.js +127 -11
- package/app/v2Containers/CreativesContainer/SlideBoxFooter.js +62 -9
- package/app/v2Containers/CreativesContainer/SlideBoxHeader.js +29 -4
- package/app/v2Containers/CreativesContainer/constants.js +24 -0
- package/app/v2Containers/CreativesContainer/embeddedSlideboxUtils.js +67 -0
- package/app/v2Containers/CreativesContainer/index.js +346 -71
- package/app/v2Containers/CreativesContainer/index.scss +51 -1
- package/app/v2Containers/CreativesContainer/messages.js +12 -0
- package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.localTemplates.test.js +90 -0
- package/app/v2Containers/CreativesContainer/tests/SlideBoxContent.test.js +69 -1
- package/app/v2Containers/CreativesContainer/tests/SlideBoxFooter.test.js +443 -0
- package/app/v2Containers/CreativesContainer/tests/SlideBoxHeader.test.js +110 -0
- package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +147 -4
- package/app/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxHeader.test.js.snap +363 -0
- package/app/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +57 -10
- package/app/v2Containers/CreativesContainer/tests/embeddedSlideboxUtils.test.js +258 -0
- package/app/v2Containers/CreativesContainer/tests/index.test.js +71 -9
- package/app/v2Containers/CreativesContainer/tests/useLocalTemplatesProp.test.js +125 -0
- package/app/v2Containers/Email/index.js +2 -5
- package/app/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +58 -77
- package/app/v2Containers/EmailWrapper/components/EmailWrapperView.js +3 -0
- package/app/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +158 -89
- package/app/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +16 -1
- package/app/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +17 -12
- package/app/v2Containers/EmailWrapper/index.js +4 -0
- package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +1 -0
- package/app/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +133 -0
- package/app/v2Containers/FTP/index.js +2 -51
- package/app/v2Containers/FTP/messages.js +0 -4
- package/app/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +110 -155
- package/app/v2Containers/InApp/index.js +297 -118
- package/app/v2Containers/InApp/tests/index.test.js +17 -6
- package/app/v2Containers/InApp/tests/mockData.js +1 -1
- package/app/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +19 -0
- package/app/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +3 -0
- package/app/v2Containers/InAppWrapper/index.js +3 -0
- package/app/v2Containers/InappAdvance/index.js +5 -104
- package/app/v2Containers/InappAdvance/tests/index.test.js +2 -0
- package/app/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +24 -3
- package/app/v2Containers/Line/Container/Text/index.js +0 -1
- package/app/v2Containers/MobilePush/Create/index.js +105 -28
- package/app/v2Containers/MobilePush/Create/messages.js +4 -0
- package/app/v2Containers/MobilePush/Edit/index.js +250 -68
- package/app/v2Containers/MobilePush/Edit/messages.js +4 -0
- package/app/v2Containers/MobilePushNew/components/PlatformContentFields.js +36 -12
- package/app/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +68 -27
- package/app/v2Containers/MobilePushNew/index.js +78 -35
- package/app/v2Containers/MobilePushNew/messages.js +8 -0
- package/app/v2Containers/MobilepushWrapper/index.js +11 -1
- package/app/v2Containers/Rcs/constants.js +32 -1
- package/app/v2Containers/Rcs/index.js +963 -916
- package/app/v2Containers/Rcs/index.scss +85 -6
- package/app/v2Containers/Rcs/messages.js +10 -1
- package/app/v2Containers/Rcs/rcsLibraryHydrationUtils.js +205 -0
- package/app/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +41136 -1566
- package/app/v2Containers/Rcs/tests/__snapshots__/utils.test.js.snap +0 -5
- package/app/v2Containers/Rcs/tests/index.test.js +41 -38
- package/app/v2Containers/Rcs/tests/mockData.js +38 -0
- package/app/v2Containers/Rcs/tests/rcsLibraryHydrationUtils.test.js +251 -0
- package/app/v2Containers/Rcs/tests/utils.test.js +379 -1
- package/app/v2Containers/Rcs/utils.js +358 -10
- package/app/v2Containers/Sms/Create/index.js +122 -39
- package/app/v2Containers/Sms/Create/messages.js +4 -0
- package/app/v2Containers/Sms/Edit/index.js +37 -3
- package/app/v2Containers/Sms/commonMethods.js +3 -6
- package/app/v2Containers/Sms/smsFormDataHelpers.js +67 -0
- package/app/v2Containers/Sms/tests/commonMethods.test.js +122 -0
- package/app/v2Containers/Sms/tests/smsFormDataHelpers.test.js +253 -0
- package/app/v2Containers/SmsTrai/Create/index.js +9 -4
- package/app/v2Containers/SmsTrai/Create/index.scss +1 -1
- package/app/v2Containers/SmsTrai/Edit/constants.js +2 -0
- package/app/v2Containers/SmsTrai/Edit/index.js +667 -160
- package/app/v2Containers/SmsTrai/Edit/index.scss +121 -0
- package/app/v2Containers/SmsTrai/Edit/messages.js +9 -4
- package/app/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4590 -2436
- package/app/v2Containers/SmsWrapper/index.js +41 -8
- package/app/v2Containers/TagList/index.js +63 -2
- package/app/v2Containers/TagList/messages.js +8 -0
- package/app/v2Containers/TagList/tests/TagList.test.js +122 -20
- package/app/v2Containers/TagList/tests/mockdata.js +17 -0
- package/app/v2Containers/Templates/TemplatesActionBar.js +101 -0
- package/app/v2Containers/Templates/_templates.scss +61 -2
- package/app/v2Containers/Templates/actions.js +11 -0
- package/app/v2Containers/Templates/constants.js +2 -0
- package/app/v2Containers/Templates/index.js +90 -40
- package/app/v2Containers/Templates/reducer.js +3 -1
- package/app/v2Containers/Templates/sagas.js +57 -12
- package/app/v2Containers/Templates/tests/TemplatesActionBar.test.js +120 -0
- package/app/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +1043 -1079
- package/app/v2Containers/Templates/tests/reducer.test.js +12 -0
- package/app/v2Containers/Templates/tests/sagas.test.js +193 -12
- package/app/v2Containers/Templates/tests/smsTemplatesListApi.test.js +180 -0
- package/app/v2Containers/Templates/utils/smsTemplatesListApi.js +79 -0
- package/app/v2Containers/TemplatesV2/TemplatesV2.style.js +72 -1
- package/app/v2Containers/TemplatesV2/index.js +147 -49
- package/app/v2Containers/TemplatesV2/tests/TemplatesV2.localTemplates.test.js +131 -0
- package/app/v2Containers/Viber/index.js +9 -10
- package/app/v2Containers/Viber/index.scss +1 -1
- package/app/v2Containers/WebPush/Create/components/BrandIconSection.test.js +264 -0
- package/app/v2Containers/WebPush/Create/components/MessageSection.js +78 -19
- package/app/v2Containers/WebPush/Create/components/MessageSection.test.js +82 -0
- package/app/v2Containers/WebPush/Create/components/__snapshots__/BrandIconSection.test.js.snap +187 -0
- package/app/v2Containers/WebPush/Create/components/__snapshots__/MessageSection.test.js.snap +25 -17
- package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.js +80 -0
- package/app/v2Containers/WebPush/Create/hooks/useAiraTriggerPosition.test.js +210 -0
- package/app/v2Containers/WebPush/Create/hooks/useTagManagement.js +1 -5
- package/app/v2Containers/WebPush/Create/hooks/useTagManagement.test.js +0 -7
- package/app/v2Containers/WebPush/Create/index.js +36 -6
- package/app/v2Containers/WebPush/Create/index.scss +5 -0
- package/app/v2Containers/WebPush/Create/messages.js +8 -1
- package/app/v2Containers/WebPush/Create/preview/tests/NotificationContainer.test.js +269 -0
- package/app/v2Containers/WebPush/Create/utils/validation.js +31 -15
- package/app/v2Containers/WebPush/Create/utils/validation.test.js +72 -24
- package/app/v2Containers/Whatsapp/index.js +28 -53
- package/app/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +26939 -3982
- package/app/v2Containers/Whatsapp/tests/index.test.js +172 -0
- package/app/v2Containers/Zalo/index.js +5 -11
- package/package.json +2 -2
- package/version +9 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '@testing-library/jest-dom';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
hasUnsubscribeTag,
|
|
4
|
+
validateTagsCore,
|
|
5
5
|
getTagMapValue,
|
|
6
6
|
getLoyaltyTagsMapValue,
|
|
7
7
|
getForwardedMapValues,
|
|
@@ -9,10 +9,160 @@ import {
|
|
|
9
9
|
validateIfTagClosed,
|
|
10
10
|
validateTags,
|
|
11
11
|
skipTags,
|
|
12
|
-
|
|
12
|
+
checkIfSupportedTag,
|
|
13
|
+
transformInjectedTags,
|
|
13
14
|
} from '../tagValidations';
|
|
14
15
|
import { containsBase64Images } from '../content';
|
|
15
|
-
|
|
16
|
+
|
|
17
|
+
describe('hasUnsubscribeTag', () => {
|
|
18
|
+
it('should return false when content is not a string', () => {
|
|
19
|
+
expect(hasUnsubscribeTag(null)).toBe(false);
|
|
20
|
+
expect(hasUnsubscribeTag(undefined)).toBe(false);
|
|
21
|
+
expect(hasUnsubscribeTag(123)).toBe(false);
|
|
22
|
+
expect(hasUnsubscribeTag({})).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should return false when content has no unsubscribe tag', () => {
|
|
26
|
+
expect(hasUnsubscribeTag('Hello world')).toBe(false);
|
|
27
|
+
expect(hasUnsubscribeTag('{{ name }}')).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return true when content has {{ unsubscribe }}', () => {
|
|
31
|
+
expect(hasUnsubscribeTag('Click {{ unsubscribe }} here')).toBe(true);
|
|
32
|
+
expect(hasUnsubscribeTag('{{ unsubscribe }}')).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('validateTagsCore', () => {
|
|
37
|
+
it('should include isContentEmpty: false when includeIsContentEmpty is true', () => {
|
|
38
|
+
const result = validateTagsCore({
|
|
39
|
+
contentForBraceCheck: '{{a}}',
|
|
40
|
+
contentForUnsubscribeScan: '{{a}}',
|
|
41
|
+
tags: null,
|
|
42
|
+
currentModule: 'default',
|
|
43
|
+
isFullMode: true,
|
|
44
|
+
includeIsContentEmpty: true,
|
|
45
|
+
});
|
|
46
|
+
expect(result.isContentEmpty).toBe(false);
|
|
47
|
+
expect(result.valid).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should use initialMissingTags when provided', () => {
|
|
51
|
+
const result = validateTagsCore({
|
|
52
|
+
contentForBraceCheck: '{{a}}',
|
|
53
|
+
contentForUnsubscribeScan: '{{a}}',
|
|
54
|
+
tags: [{ definition: { supportedModules: [], value: 'x' } }],
|
|
55
|
+
currentModule: 'default',
|
|
56
|
+
isFullMode: false,
|
|
57
|
+
initialMissingTags: ['requiredTag'],
|
|
58
|
+
});
|
|
59
|
+
expect(result.missingTags).toEqual(['requiredTag']);
|
|
60
|
+
expect(result.valid).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should use custom skipTagsFn when provided', () => {
|
|
64
|
+
const customSkip = jest.fn(() => true);
|
|
65
|
+
const result = validateTagsCore({
|
|
66
|
+
contentForBraceCheck: '{{ unsubscribe }}',
|
|
67
|
+
contentForUnsubscribeScan: '{{ unsubscribe }}',
|
|
68
|
+
tags: [
|
|
69
|
+
{
|
|
70
|
+
definition: {
|
|
71
|
+
supportedModules: [{ context: 'DEFAULT', mandatory: true }],
|
|
72
|
+
value: 'unsubscribe',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
currentModule: 'DEFAULT',
|
|
77
|
+
isFullMode: false,
|
|
78
|
+
skipTagsFn: customSkip,
|
|
79
|
+
});
|
|
80
|
+
expect(customSkip).toHaveBeenCalled();
|
|
81
|
+
expect(result.valid).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('checkIfSupportedTag', () => {
|
|
86
|
+
it('should return false for empty or no injected tags', () => {
|
|
87
|
+
expect(checkIfSupportedTag('someTag', {})).toBe(false);
|
|
88
|
+
expect(checkIfSupportedTag('someTag', undefined)).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should return true when tag matches a top-level key', () => {
|
|
92
|
+
const injectedTags = { name: { name: 'Name' }, unsubscribe: { name: 'Unsubscribe' } };
|
|
93
|
+
expect(checkIfSupportedTag('name', injectedTags)).toBe(true);
|
|
94
|
+
expect(checkIfSupportedTag('unsubscribe', injectedTags)).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should return false when tag does not match any key', () => {
|
|
98
|
+
const injectedTags = { name: { name: 'Name' } };
|
|
99
|
+
expect(checkIfSupportedTag('other', injectedTags)).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return true when tag matches a nested subtag', () => {
|
|
103
|
+
const injectedTags = {
|
|
104
|
+
customer: {
|
|
105
|
+
name: 'Customer',
|
|
106
|
+
subtags: {
|
|
107
|
+
first_name: { name: 'First Name' },
|
|
108
|
+
last_name: { name: 'Last Name' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
expect(checkIfSupportedTag('first_name', injectedTags)).toBe(true);
|
|
113
|
+
expect(checkIfSupportedTag('last_name', injectedTags)).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should return false when tag is in neither top-level nor subtags', () => {
|
|
117
|
+
const injectedTags = {
|
|
118
|
+
customer: {
|
|
119
|
+
name: 'Customer',
|
|
120
|
+
subtags: { first_name: { name: 'First Name' } },
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
expect(checkIfSupportedTag('unknown', injectedTags)).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('transformInjectedTags', () => {
|
|
128
|
+
it('should add tag-header and normalize subtags when key contains "subtags"', () => {
|
|
129
|
+
const tags = [
|
|
130
|
+
{
|
|
131
|
+
name: 'Customer',
|
|
132
|
+
subtags: {
|
|
133
|
+
first_name: { name: 'First Name' },
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
const result = transformInjectedTags(tags);
|
|
138
|
+
expect(result).toBe(tags);
|
|
139
|
+
expect(tags[0]['tag-header']).toBe(true);
|
|
140
|
+
expect(tags[0].subtags).toEqual({ first_name: { name: 'First Name' } });
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should recursively transform nested subtags', () => {
|
|
144
|
+
const tags = [
|
|
145
|
+
{
|
|
146
|
+
name: 'Parent',
|
|
147
|
+
subtags: {
|
|
148
|
+
child: {
|
|
149
|
+
name: 'Child',
|
|
150
|
+
subtags: { grandchild: { name: 'Grandchild' } },
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
];
|
|
155
|
+
transformInjectedTags(tags);
|
|
156
|
+
expect(tags[0].subtags.child.subtags).toEqual({ grandchild: { name: 'Grandchild' } });
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should return tags unchanged when no subtag keys exist', () => {
|
|
160
|
+
const tags = [{ name: 'Simple', desc: 'No subtags' }];
|
|
161
|
+
const result = transformInjectedTags(tags);
|
|
162
|
+
expect(result).toBe(tags);
|
|
163
|
+
expect(tags[0]).toEqual({ name: 'Simple', desc: 'No subtags' });
|
|
164
|
+
});
|
|
165
|
+
});
|
|
16
166
|
|
|
17
167
|
describe("check if curly brackets are balanced", () => {
|
|
18
168
|
it("test for balanced curly brackets", () => {
|
|
@@ -22,6 +172,9 @@ describe("check if curly brackets are balanced", () => {
|
|
|
22
172
|
value += "}}"
|
|
23
173
|
let result = validateIfTagClosed(value);
|
|
24
174
|
expect(result).toEqual(true);
|
|
175
|
+
// no braces or empty string: match returns null, l1/l2/l3 undefined -> true
|
|
176
|
+
expect(validateIfTagClosed("")).toEqual(true);
|
|
177
|
+
expect(validateIfTagClosed("plain text no braces")).toEqual(true);
|
|
25
178
|
//valid cases
|
|
26
179
|
expect(validateIfTagClosed("{{{Hello}}}")).toEqual(true);
|
|
27
180
|
expect(validateIfTagClosed("{{{Hello}}")).toEqual(true);
|
|
@@ -64,53 +217,42 @@ describe("validateTags", () => {
|
|
|
64
217
|
it("should return valid response when all tags are present", () => {
|
|
65
218
|
const content = "Hello {{tag1}}, {{tag2}}, {{tag3}} {{entryTrigger.lifetimePurchases}}";
|
|
66
219
|
|
|
67
|
-
const injectedTagsParams = [];
|
|
68
220
|
const location = { query: { module: "DEFAULT" } };
|
|
69
221
|
const tagModule = null;
|
|
70
222
|
|
|
71
223
|
const result = validateTags({
|
|
72
224
|
content,
|
|
73
225
|
tagsParam,
|
|
74
|
-
injectedTagsParams,
|
|
75
226
|
location,
|
|
76
227
|
tagModule,
|
|
77
|
-
eventContextTags,
|
|
78
228
|
});
|
|
79
229
|
|
|
80
230
|
expect(result.valid).toEqual(true);
|
|
81
231
|
expect(result.missingTags).toEqual([]);
|
|
82
|
-
expect(result.unsupportedTags).toEqual([]);
|
|
83
232
|
expect(result.isBraceError).toEqual(false);
|
|
84
233
|
});
|
|
85
234
|
|
|
86
|
-
it("should return
|
|
235
|
+
it("should return valid response when content has balanced braces and all tags present", () => {
|
|
87
236
|
const content = "Hello {{tag1}}, {{tag2}}, {{tag3}}";
|
|
88
|
-
const updatedTagsParam = [...tagsParam, {
|
|
89
|
-
definition: {
|
|
90
|
-
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
91
|
-
value: "tag4",
|
|
92
|
-
},
|
|
93
|
-
},];
|
|
94
|
-
const injectedTagsParams = [];
|
|
95
237
|
const location = { query: { module: "DEFAULT" } };
|
|
96
238
|
const tagModule = null;
|
|
97
239
|
|
|
98
240
|
const result = validateTags({
|
|
99
241
|
content,
|
|
100
|
-
|
|
101
|
-
injectedTagsParams,
|
|
242
|
+
tagsParam,
|
|
102
243
|
location,
|
|
103
244
|
tagModule,
|
|
245
|
+
waitEventContextTags: {},
|
|
104
246
|
});
|
|
105
247
|
|
|
106
248
|
expect(result.valid).toEqual(true);
|
|
107
|
-
expect(result.
|
|
249
|
+
expect(result.missingTags).toEqual([]);
|
|
108
250
|
expect(result.isBraceError).toEqual(false);
|
|
109
251
|
});
|
|
110
252
|
|
|
111
|
-
it("should return
|
|
253
|
+
it("should return valid response when content has balanced braces with missingEventContextTags and partial tagsParam", () => {
|
|
112
254
|
const content = "Hello {{tag1}}, {{tag2}}, {{tag3}} {{missingEventContextTags}}";
|
|
113
|
-
const
|
|
255
|
+
const tagsParamLocal = [
|
|
114
256
|
{
|
|
115
257
|
definition: {
|
|
116
258
|
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
@@ -124,27 +266,25 @@ describe("validateTags", () => {
|
|
|
124
266
|
},
|
|
125
267
|
},
|
|
126
268
|
];
|
|
127
|
-
const injectedTagsParams = [];
|
|
128
269
|
const location = { query: { module: "DEFAULT" } };
|
|
129
270
|
const tagModule = null;
|
|
130
271
|
|
|
131
272
|
const result = validateTags({
|
|
132
273
|
content,
|
|
133
|
-
tagsParam,
|
|
134
|
-
injectedTagsParams,
|
|
274
|
+
tagsParam: tagsParamLocal,
|
|
135
275
|
location,
|
|
136
276
|
tagModule,
|
|
277
|
+
waitEventContextTags: {},
|
|
137
278
|
});
|
|
138
279
|
|
|
139
280
|
expect(result.valid).toEqual(true);
|
|
140
281
|
expect(result.missingTags).toEqual([]);
|
|
141
|
-
expect(result.unsupportedTags).toEqual(["tag3", "missingEventContextTags"]);
|
|
142
282
|
expect(result.isBraceError).toEqual(false);
|
|
143
283
|
});
|
|
144
284
|
|
|
145
285
|
it("should return invalid response when there is an unbalanced bracket error", () => {
|
|
146
286
|
const content = "Hello {{tag1}, {{tag2}}, {{tag3}}";
|
|
147
|
-
const
|
|
287
|
+
const tagsParamLocal = [
|
|
148
288
|
{
|
|
149
289
|
definition: {
|
|
150
290
|
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
@@ -164,26 +304,24 @@ describe("validateTags", () => {
|
|
|
164
304
|
},
|
|
165
305
|
},
|
|
166
306
|
];
|
|
167
|
-
const injectedTagsParams = [];
|
|
168
307
|
const location = { query: { module: "DEFAULT" } };
|
|
169
308
|
const tagModule = null;
|
|
170
309
|
|
|
171
310
|
const result = validateTags({
|
|
172
311
|
content,
|
|
173
|
-
tagsParam,
|
|
174
|
-
injectedTagsParams,
|
|
312
|
+
tagsParam: tagsParamLocal,
|
|
175
313
|
location,
|
|
176
314
|
tagModule,
|
|
315
|
+
waitEventContextTags: {},
|
|
177
316
|
});
|
|
178
317
|
|
|
179
318
|
expect(result.valid).toEqual(false);
|
|
180
|
-
expect(result.missingTags).toEqual([
|
|
181
|
-
expect(result.unsupportedTags).toEqual([]);
|
|
319
|
+
expect(result.missingTags).toEqual([]);
|
|
182
320
|
expect(result.isBraceError).toEqual(true);
|
|
183
321
|
});
|
|
184
322
|
|
|
185
|
-
it("should
|
|
186
|
-
const
|
|
323
|
+
it("should require unsubscribe when mandatory and missing, and accept skipped unsubscribe variant", () => {
|
|
324
|
+
const tagsParamUnsubscribe = [
|
|
187
325
|
{
|
|
188
326
|
definition: {
|
|
189
327
|
supportedModules: [{ context: "DEFAULT", mandatory: true }],
|
|
@@ -191,288 +329,341 @@ describe("validateTags", () => {
|
|
|
191
329
|
},
|
|
192
330
|
},
|
|
193
331
|
];
|
|
194
|
-
// Content does not include {{unsubscribe}}, so it would be missing
|
|
195
|
-
const contentMissing = "Hello {{tag1}}";
|
|
196
|
-
const injectedTagsParams = [];
|
|
197
332
|
const location = { query: { module: "DEFAULT" } };
|
|
198
333
|
const tagModule = null;
|
|
199
334
|
|
|
200
|
-
|
|
335
|
+
const contentMissing = "Hello world";
|
|
201
336
|
const resultMissing = validateTags({
|
|
202
337
|
content: contentMissing,
|
|
203
|
-
tagsParam,
|
|
204
|
-
injectedTagsParams,
|
|
338
|
+
tagsParam: tagsParamUnsubscribe,
|
|
205
339
|
location,
|
|
206
340
|
tagModule,
|
|
341
|
+
waitEventContextTags: {},
|
|
207
342
|
});
|
|
208
343
|
expect(resultMissing.missingTags).toContain("unsubscribe");
|
|
344
|
+
expect(resultMissing.valid).toBe(false);
|
|
209
345
|
|
|
210
|
-
// Now, content includes a tag that triggers skipTags logic for unsubscribe
|
|
211
|
-
// e.g., {{unsubscribe(#123456)}} matches the skipTags regex
|
|
212
346
|
const contentWithSkippedUnsubscribe = "Hello {{unsubscribe(#123456)}}";
|
|
213
347
|
const resultSkipped = validateTags({
|
|
214
348
|
content: contentWithSkippedUnsubscribe,
|
|
215
|
-
tagsParam,
|
|
216
|
-
injectedTagsParams,
|
|
349
|
+
tagsParam: tagsParamUnsubscribe,
|
|
217
350
|
location,
|
|
218
351
|
tagModule,
|
|
352
|
+
waitEventContextTags: {},
|
|
219
353
|
});
|
|
220
354
|
expect(resultSkipped.missingTags).not.toContain("unsubscribe");
|
|
221
355
|
expect(resultSkipped.valid).toBe(true);
|
|
222
|
-
});
|
|
223
|
-
});
|
|
224
356
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
357
|
+
const contentWithWhitespaceUnsubscribe = "Hello {{ unsubscribe }}";
|
|
358
|
+
const resultWhitespace = validateTags({
|
|
359
|
+
content: contentWithWhitespaceUnsubscribe,
|
|
360
|
+
tagsParam: tagsParamUnsubscribe,
|
|
361
|
+
location,
|
|
362
|
+
tagModule,
|
|
363
|
+
});
|
|
364
|
+
expect(resultWhitespace.missingTags).not.toContain("unsubscribe");
|
|
365
|
+
expect(resultWhitespace.valid).toBe(true);
|
|
366
|
+
expect(resultWhitespace.unsupportedTags ?? []).toEqual([]);
|
|
230
367
|
});
|
|
231
368
|
|
|
232
|
-
it(
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
children: [],
|
|
244
|
-
},
|
|
245
|
-
],
|
|
369
|
+
it('should treat tags from waitEventContextTags as supported', () => {
|
|
370
|
+
const content = 'Hello {{waitEvent.orderId}}';
|
|
371
|
+
const tagsParam = [];
|
|
372
|
+
const injectedTagsParams = [];
|
|
373
|
+
const location = { query: { module: 'DEFAULT' } };
|
|
374
|
+
const tagModule = null;
|
|
375
|
+
const waitEventContextTags = {
|
|
376
|
+
block1: {
|
|
377
|
+
eventName: 'Order Placed',
|
|
378
|
+
blockName: 'Wait Block',
|
|
379
|
+
tags: [{ tagName: 'waitEvent.orderId', label: 'Order ID' }],
|
|
246
380
|
},
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const result = validateTags({
|
|
384
|
+
content,
|
|
385
|
+
tagsParam,
|
|
386
|
+
injectedTagsParams,
|
|
387
|
+
location,
|
|
388
|
+
tagModule,
|
|
389
|
+
eventContextTags: [],
|
|
390
|
+
waitEventContextTags,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
expect(result.valid).toEqual(true);
|
|
394
|
+
expect(result.missingTags).toEqual([]);
|
|
395
|
+
expect(result.isBraceError).toEqual(false);
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
describe('validateTags wrapper (v2 consumers)', () => {
|
|
400
|
+
const tagsWithUnsubscribe = [
|
|
401
|
+
{
|
|
402
|
+
definition: {
|
|
403
|
+
supportedModules: [{ context: 'DEFAULT', mandatory: true }],
|
|
404
|
+
value: 'unsubscribe',
|
|
255
405
|
},
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
"Node 2.1",
|
|
264
|
-
]);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it("should ignore nodes without names", () => {
|
|
268
|
-
const data = [
|
|
269
|
-
{
|
|
270
|
-
name: "Node 1",
|
|
271
|
-
children: [
|
|
272
|
-
{
|
|
273
|
-
name: "Node 1.1",
|
|
274
|
-
children: [],
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
children: [],
|
|
278
|
-
},
|
|
279
|
-
],
|
|
406
|
+
},
|
|
407
|
+
];
|
|
408
|
+
const tagsOutboundUnsubscribe = [
|
|
409
|
+
{
|
|
410
|
+
definition: {
|
|
411
|
+
supportedModules: [{ context: 'outbound', mandatory: true }],
|
|
412
|
+
value: 'unsubscribe',
|
|
280
413
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
{
|
|
289
|
-
name: "Node 2.2",
|
|
290
|
-
},
|
|
291
|
-
],
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
const tagsDefaultLowercase = [
|
|
417
|
+
{
|
|
418
|
+
definition: {
|
|
419
|
+
supportedModules: [{ context: 'default', mandatory: true }],
|
|
420
|
+
value: 'unsubscribe',
|
|
292
421
|
},
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
expect(result).toEqual([
|
|
296
|
-
"Node 1",
|
|
297
|
-
"Node 1.1",
|
|
298
|
-
"Node 2",
|
|
299
|
-
"Node 2.1",
|
|
300
|
-
"Node 2.2",
|
|
301
|
-
]);
|
|
302
|
-
});
|
|
303
|
-
});
|
|
422
|
+
},
|
|
423
|
+
];
|
|
304
424
|
|
|
425
|
+
describe('module selection (location.query.module vs tagModule vs DEFAULT)', () => {
|
|
426
|
+
it('uses location.query.module when set and tagModule not provided', () => {
|
|
427
|
+
const content = 'Hello world';
|
|
428
|
+
const result = validateTags({
|
|
429
|
+
content,
|
|
430
|
+
tagsParam: tagsWithUnsubscribe,
|
|
431
|
+
location: { query: { module: 'DEFAULT' } },
|
|
432
|
+
tagModule: null,
|
|
433
|
+
});
|
|
434
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
435
|
+
expect(result.valid).toBe(false);
|
|
436
|
+
});
|
|
305
437
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
438
|
+
it('uses tagModule override when provided (overrides location.query.module)', () => {
|
|
439
|
+
const content = 'Hello world';
|
|
440
|
+
const resultWithOutbound = validateTags({
|
|
441
|
+
content,
|
|
442
|
+
tagsParam: tagsOutboundUnsubscribe,
|
|
443
|
+
location: { query: { module: 'DEFAULT' } },
|
|
444
|
+
tagModule: 'outbound',
|
|
445
|
+
});
|
|
446
|
+
expect(resultWithOutbound.missingTags).toContain('unsubscribe');
|
|
447
|
+
expect(resultWithOutbound.valid).toBe(false);
|
|
448
|
+
|
|
449
|
+
const resultWithDefault = validateTags({
|
|
450
|
+
content,
|
|
451
|
+
tagsParam: tagsWithUnsubscribe,
|
|
452
|
+
location: { query: { module: 'outbound' } },
|
|
453
|
+
tagModule: 'DEFAULT',
|
|
454
|
+
});
|
|
455
|
+
expect(resultWithDefault.missingTags).toContain('unsubscribe');
|
|
456
|
+
expect(resultWithDefault.valid).toBe(false);
|
|
457
|
+
});
|
|
317
458
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
459
|
+
it('uses DEFAULT (lowercase) when location is undefined', () => {
|
|
460
|
+
const content = 'Hello world';
|
|
461
|
+
const result = validateTags({
|
|
462
|
+
content,
|
|
463
|
+
tagsParam: tagsDefaultLowercase,
|
|
464
|
+
location: undefined,
|
|
465
|
+
tagModule: null,
|
|
466
|
+
});
|
|
467
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
468
|
+
expect(result.valid).toBe(false);
|
|
469
|
+
});
|
|
326
470
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
471
|
+
it('uses DEFAULT when location.query is undefined', () => {
|
|
472
|
+
const content = 'Hello world';
|
|
473
|
+
const result = validateTags({
|
|
474
|
+
content,
|
|
475
|
+
tagsParam: tagsDefaultLowercase,
|
|
476
|
+
location: {},
|
|
477
|
+
tagModule: null,
|
|
478
|
+
});
|
|
479
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
480
|
+
expect(result.valid).toBe(false);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('uses DEFAULT when location.query.module is falsy', () => {
|
|
484
|
+
const content = 'Hello world';
|
|
485
|
+
const result = validateTags({
|
|
486
|
+
content,
|
|
487
|
+
tagsParam: tagsDefaultLowercase,
|
|
488
|
+
location: { query: { module: '' } },
|
|
489
|
+
tagModule: null,
|
|
490
|
+
});
|
|
491
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
492
|
+
expect(result.valid).toBe(false);
|
|
493
|
+
});
|
|
334
494
|
});
|
|
335
495
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
496
|
+
describe('content passed to contentForBraceCheck and contentForUnsubscribeScan', () => {
|
|
497
|
+
it('uses same content for brace check (isBraceError when unbalanced)', () => {
|
|
498
|
+
const content = 'Hello {{tag1}, {{tag2}}';
|
|
499
|
+
const result = validateTags({
|
|
500
|
+
content,
|
|
501
|
+
tagsParam: [],
|
|
502
|
+
location: { query: { module: 'DEFAULT' } },
|
|
503
|
+
});
|
|
504
|
+
expect(result.isBraceError).toBe(true);
|
|
505
|
+
expect(result.valid).toBe(false);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('uses same content for unsubscribe scan (missing unsubscribe when required)', () => {
|
|
509
|
+
const content = 'Hello {{other}}';
|
|
510
|
+
const result = validateTags({
|
|
511
|
+
content,
|
|
512
|
+
tagsParam: tagsWithUnsubscribe,
|
|
513
|
+
location: { query: { module: 'DEFAULT' } },
|
|
514
|
+
});
|
|
515
|
+
expect(result.missingTags).toContain('unsubscribe');
|
|
516
|
+
expect(result.valid).toBe(false);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('content with {{ unsubscribe }} satisfies unsubscribe requirement', () => {
|
|
520
|
+
const content = 'Hello {{ unsubscribe }}';
|
|
521
|
+
const result = validateTags({
|
|
522
|
+
content,
|
|
523
|
+
tagsParam: tagsWithUnsubscribe,
|
|
524
|
+
location: { query: { module: 'DEFAULT' } },
|
|
525
|
+
});
|
|
526
|
+
expect(result.missingTags).not.toContain('unsubscribe');
|
|
527
|
+
expect(result.valid).toBe(true);
|
|
528
|
+
});
|
|
347
529
|
});
|
|
348
530
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
|
|
531
|
+
describe('isFullMode', () => {
|
|
532
|
+
it('when true skips unsubscribe check (no missingTags for mandatory unsubscribe)', () => {
|
|
533
|
+
const content = 'Hello world';
|
|
534
|
+
const result = validateTags({
|
|
535
|
+
content,
|
|
536
|
+
tagsParam: tagsWithUnsubscribe,
|
|
537
|
+
location: { query: { module: 'DEFAULT' } },
|
|
538
|
+
isFullMode: true,
|
|
539
|
+
});
|
|
540
|
+
expect(result.missingTags).toEqual([]);
|
|
541
|
+
expect(result.valid).toBe(true);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('when true still runs brace check', () => {
|
|
545
|
+
const content = 'Hello {{tag1}';
|
|
546
|
+
const result = validateTags({
|
|
547
|
+
content,
|
|
548
|
+
tagsParam: tagsWithUnsubscribe,
|
|
549
|
+
location: { query: { module: 'DEFAULT' } },
|
|
550
|
+
isFullMode: true,
|
|
551
|
+
});
|
|
552
|
+
expect(result.isBraceError).toBe(true);
|
|
553
|
+
expect(result.valid).toBe(false);
|
|
554
|
+
});
|
|
363
555
|
});
|
|
364
556
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
557
|
+
describe('tagsParam null or empty', () => {
|
|
558
|
+
it('when null: only brace check runs, no missing-tag logic', () => {
|
|
559
|
+
const content = 'Hello {{a}}';
|
|
560
|
+
const result = validateTags({
|
|
561
|
+
content,
|
|
562
|
+
tagsParam: null,
|
|
563
|
+
location: { query: { module: 'DEFAULT' } },
|
|
564
|
+
});
|
|
565
|
+
expect(result.missingTags).toEqual([]);
|
|
566
|
+
expect(result.isBraceError).toBe(false);
|
|
567
|
+
expect(result.valid).toBe(true);
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
it('when empty array: only brace check runs', () => {
|
|
571
|
+
const content = 'Hello {{a}}';
|
|
572
|
+
const result = validateTags({
|
|
573
|
+
content,
|
|
574
|
+
tagsParam: [],
|
|
575
|
+
location: { query: { module: 'DEFAULT' } },
|
|
576
|
+
});
|
|
577
|
+
expect(result.missingTags).toEqual([]);
|
|
578
|
+
expect(result.valid).toBe(true);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it('when null with unbalanced braces: returns isBraceError', () => {
|
|
582
|
+
const content = 'Hello {{a}';
|
|
583
|
+
const result = validateTags({
|
|
584
|
+
content,
|
|
585
|
+
tagsParam: null,
|
|
586
|
+
location: { query: { module: 'DEFAULT' } },
|
|
587
|
+
});
|
|
588
|
+
expect(result.isBraceError).toBe(true);
|
|
589
|
+
expect(result.valid).toBe(false);
|
|
590
|
+
expect(result.missingTags).toEqual([]);
|
|
591
|
+
});
|
|
376
592
|
});
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
{
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const tagObject = {};
|
|
452
|
-
const eventContextTags = [{ tagName: "leaderboard", subTags: ["userId"]}];
|
|
453
|
-
const isLiquidFlow = false;
|
|
454
|
-
const result = checkSupport(response, tagObject, eventContextTags, isLiquidFlow);
|
|
455
|
-
expect(result).toEqual( [ 'leaderboard' ]);
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
it("test for checking loyalty tags in that are coming in forwardedTags", () => {
|
|
459
|
-
const response = { data: [{ name: "leaderboard", children: [{name: "person.userId"}]}]};
|
|
460
|
-
const tagObject = {};
|
|
461
|
-
const isLiquidFlow = true;
|
|
462
|
-
// forwardedTags contains tag hierarchy with parent tags and their subtags
|
|
463
|
-
// needed for loyalty email liquid tag resolution
|
|
464
|
-
const forwardedTags = {
|
|
465
|
-
leaderboard: {
|
|
466
|
-
name: "Leaderboard",
|
|
467
|
-
subtags: {
|
|
468
|
-
"person.userId": {
|
|
469
|
-
name: "User ID",
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
const result = checkSupport(response, tagObject, [], isLiquidFlow, forwardedTags);
|
|
475
|
-
expect(result).toEqual( [ 'leaderboard', 'person.userId' ]);
|
|
593
|
+
|
|
594
|
+
describe('v2 consumer call patterns', () => {
|
|
595
|
+
it('Whatsapp-style: content, tagsParam, location, tagModule (getDefaultTags), isFullMode', () => {
|
|
596
|
+
const content = 'Hello {{ unsubscribe }}';
|
|
597
|
+
const result = validateTags({
|
|
598
|
+
content: content,
|
|
599
|
+
tagsParam: tagsWithUnsubscribe,
|
|
600
|
+
location: { query: { module: 'DEFAULT' } },
|
|
601
|
+
tagModule: 'DEFAULT',
|
|
602
|
+
isFullMode: false,
|
|
603
|
+
});
|
|
604
|
+
expect(result.valid).toBe(true);
|
|
605
|
+
expect(result.isBraceError).toBe(false);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it('Zalo / Rcs / MobilePushNew / EmailWrapper: same pattern as Whatsapp', () => {
|
|
609
|
+
const result = validateTags({
|
|
610
|
+
content: 'Hi {{ unsubscribe }}',
|
|
611
|
+
tagsParam: tagsWithUnsubscribe,
|
|
612
|
+
location: { query: { module: 'DEFAULT' } },
|
|
613
|
+
tagModule: 'DEFAULT',
|
|
614
|
+
isFullMode: false,
|
|
615
|
+
});
|
|
616
|
+
expect(result.valid).toBe(true);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
it('Line / Viber: tagModule "outbound"', () => {
|
|
620
|
+
const contentMissing = 'Hello';
|
|
621
|
+
const resultMissing = validateTags({
|
|
622
|
+
content: contentMissing,
|
|
623
|
+
tagsParam: tagsOutboundUnsubscribe,
|
|
624
|
+
location: { query: { module: 'inbound' } },
|
|
625
|
+
tagModule: 'outbound',
|
|
626
|
+
isFullMode: false,
|
|
627
|
+
});
|
|
628
|
+
expect(resultMissing.missingTags).toContain('unsubscribe');
|
|
629
|
+
expect(resultMissing.valid).toBe(false);
|
|
630
|
+
|
|
631
|
+
const resultOk = validateTags({
|
|
632
|
+
content: 'Hello {{ unsubscribe }}',
|
|
633
|
+
tagsParam: tagsOutboundUnsubscribe,
|
|
634
|
+
location: {},
|
|
635
|
+
tagModule: 'outbound',
|
|
636
|
+
isFullMode: false,
|
|
637
|
+
});
|
|
638
|
+
expect(resultOk.valid).toBe(true);
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it('WebPush: validationConfig spread (content + tagsParam, location, tagModule, isFullMode)', () => {
|
|
642
|
+
const validationConfig = {
|
|
643
|
+
tagsParam: tagsWithUnsubscribe,
|
|
644
|
+
location: { query: { module: 'DEFAULT' } },
|
|
645
|
+
tagModule: 'DEFAULT',
|
|
646
|
+
};
|
|
647
|
+
const result = validateTags({
|
|
648
|
+
content: 'Hello {{ unsubscribe }}',
|
|
649
|
+
...validationConfig,
|
|
650
|
+
isFullMode: false,
|
|
651
|
+
});
|
|
652
|
+
expect(result.valid).toBe(true);
|
|
653
|
+
expect(result.isBraceError).toBe(false);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it('WebPush with isFullMode: only brace check', () => {
|
|
657
|
+
const result = validateTags({
|
|
658
|
+
content: 'Hello world',
|
|
659
|
+
tagsParam: tagsWithUnsubscribe,
|
|
660
|
+
location: { query: { module: 'DEFAULT' } },
|
|
661
|
+
tagModule: 'DEFAULT',
|
|
662
|
+
isFullMode: true,
|
|
663
|
+
});
|
|
664
|
+
expect(result.valid).toBe(true);
|
|
665
|
+
expect(result.missingTags).toEqual([]);
|
|
666
|
+
});
|
|
476
667
|
});
|
|
477
668
|
});
|
|
478
669
|
|
|
@@ -1048,6 +1239,10 @@ describe('getForwardedMapValues', () => {
|
|
|
1048
1239
|
expect(getForwardedMapValues(input)).toEqual(expected);
|
|
1049
1240
|
});
|
|
1050
1241
|
|
|
1242
|
+
test('should return empty object when called with no argument (default param)', () => {
|
|
1243
|
+
expect(getForwardedMapValues()).toEqual({});
|
|
1244
|
+
});
|
|
1245
|
+
|
|
1051
1246
|
test('should correctly process objects with subtags', () => {
|
|
1052
1247
|
const input = {
|
|
1053
1248
|
customer: {
|
|
@@ -1309,84 +1504,6 @@ describe('getForwardedMapValues', () => {
|
|
|
1309
1504
|
});
|
|
1310
1505
|
});
|
|
1311
1506
|
|
|
1312
|
-
describe('isInsideLiquidBlock', () => {
|
|
1313
|
-
it('returns true for index inside a single block', () => {
|
|
1314
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1315
|
-
// Index of 'a' in 'assign' inside the block
|
|
1316
|
-
const tagIndex = content.indexOf('assign');
|
|
1317
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1318
|
-
});
|
|
1319
|
-
|
|
1320
|
-
it('returns false for index outside any block', () => {
|
|
1321
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1322
|
-
// Index of 'H' in 'Hello'
|
|
1323
|
-
expect(isInsideLiquidBlock(content, 0)).toBe(false);
|
|
1324
|
-
// Index of 'W' in 'World'
|
|
1325
|
-
expect(isInsideLiquidBlock(content, content.indexOf('World'))).toBe(false);
|
|
1326
|
-
});
|
|
1327
|
-
|
|
1328
|
-
it('returns true for index at the start of a block', () => {
|
|
1329
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1330
|
-
// Index of '{' in '{%'
|
|
1331
|
-
const tagIndex = content.indexOf('{%');
|
|
1332
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1333
|
-
});
|
|
1334
|
-
|
|
1335
|
-
it('returns false for index at the end of a block (exclusive)', () => {
|
|
1336
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1337
|
-
// Index just after the closing '%}'
|
|
1338
|
-
const blockEnd = content.indexOf('%}') + 2;
|
|
1339
|
-
expect(isInsideLiquidBlock(content, blockEnd)).toBe(false);
|
|
1340
|
-
});
|
|
1341
|
-
|
|
1342
|
-
it('returns true for index inside the second of multiple blocks', () => {
|
|
1343
|
-
const content = 'A {% first %} B {% second %} C';
|
|
1344
|
-
const tagIndex = content.indexOf('second');
|
|
1345
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1346
|
-
});
|
|
1347
|
-
|
|
1348
|
-
it('returns false for index between blocks', () => {
|
|
1349
|
-
const content = 'A {% first %} B {% second %} C';
|
|
1350
|
-
// Index of 'B' (between blocks)
|
|
1351
|
-
const tagIndex = content.indexOf('B');
|
|
1352
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(false);
|
|
1353
|
-
});
|
|
1354
|
-
|
|
1355
|
-
it('returns false for empty string', () => {
|
|
1356
|
-
expect(isInsideLiquidBlock('', 0)).toBe(false);
|
|
1357
|
-
});
|
|
1358
|
-
|
|
1359
|
-
it('returns false if there are no blocks', () => {
|
|
1360
|
-
const content = 'Just some text with no blocks';
|
|
1361
|
-
expect(isInsideLiquidBlock(content, 5)).toBe(false);
|
|
1362
|
-
});
|
|
1363
|
-
|
|
1364
|
-
it('returns false for negative index', () => {
|
|
1365
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1366
|
-
expect(isInsideLiquidBlock(content, -1)).toBe(false);
|
|
1367
|
-
});
|
|
1368
|
-
|
|
1369
|
-
it('returns false for index beyond string length', () => {
|
|
1370
|
-
const content = 'Hello {% assign foo = 1 %} World';
|
|
1371
|
-
expect(isInsideLiquidBlock(content, 100)).toBe(false);
|
|
1372
|
-
});
|
|
1373
|
-
|
|
1374
|
-
it('works for nested-like blocks (not truly nested)', () => {
|
|
1375
|
-
const content = 'A {% outer {% inner %} outer %} B';
|
|
1376
|
-
// Index of 'inner' (should be inside the first block)
|
|
1377
|
-
const tagIndex = content.indexOf('inner');
|
|
1378
|
-
expect(isInsideLiquidBlock(content, tagIndex)).toBe(true);
|
|
1379
|
-
});
|
|
1380
|
-
|
|
1381
|
-
it('returns true for index at last char inside block', () => {
|
|
1382
|
-
const content = 'A {% foo %} B';
|
|
1383
|
-
// Index of last char inside block (just before %})
|
|
1384
|
-
const blockStart = content.indexOf('{%');
|
|
1385
|
-
const blockEnd = content.indexOf('%}');
|
|
1386
|
-
expect(isInsideLiquidBlock(content, blockEnd - 1)).toBe(true);
|
|
1387
|
-
});
|
|
1388
|
-
});
|
|
1389
|
-
|
|
1390
1507
|
describe('containsBase64Images', () => {
|
|
1391
1508
|
let mockCapNotification;
|
|
1392
1509
|
let mockCallback;
|