@capillarytech/creatives-library 8.0.130 → 8.0.131

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 (77) hide show
  1. package/containers/App/constants.js +1 -0
  2. package/containers/Login/index.js +1 -2
  3. package/package.json +1 -1
  4. package/services/api.js +5 -0
  5. package/tests/integration/TemplateCreation/TemplateCreation.integration.test.js +8 -3
  6. package/tests/integration/TemplateCreation/api-response.js +5 -0
  7. package/tests/integration/TemplateCreation/msw-handler.js +42 -63
  8. package/utils/common.js +7 -0
  9. package/utils/commonUtils.js +2 -6
  10. package/utils/createMobilePushPayload.js +322 -0
  11. package/utils/tests/createMobilePushPayload.test.js +1054 -0
  12. package/v2Components/CapDeviceContent/index.js +1 -1
  13. package/v2Components/CapImageUpload/index.js +57 -44
  14. package/v2Components/CapInAppCTA/index.js +1 -0
  15. package/v2Components/CapMpushCTA/constants.js +25 -0
  16. package/v2Components/CapMpushCTA/index.js +403 -0
  17. package/v2Components/CapMpushCTA/index.scss +95 -0
  18. package/v2Components/CapMpushCTA/messages.js +101 -0
  19. package/v2Components/CapTagList/index.js +178 -121
  20. package/v2Components/CapVideoUpload/constants.js +3 -0
  21. package/v2Components/CapVideoUpload/index.js +182 -115
  22. package/v2Components/CapVideoUpload/messages.js +16 -0
  23. package/v2Components/Carousel/index.js +15 -13
  24. package/v2Components/ErrorInfoNote/style.scss +1 -0
  25. package/v2Components/MobilePushPreviewV2/index.js +57 -12
  26. package/v2Components/TemplatePreview/_templatePreview.scss +218 -74
  27. package/v2Components/TemplatePreview/assets/images/Android_With_date_and_time.svg +29 -0
  28. package/v2Components/TemplatePreview/assets/images/android.svg +9 -0
  29. package/v2Components/TemplatePreview/assets/images/iOS_With_date_and_time.svg +26 -0
  30. package/v2Components/TemplatePreview/assets/images/ios.svg +9 -0
  31. package/v2Components/TemplatePreview/index.js +234 -107
  32. package/v2Components/TemplatePreview/messages.js +4 -0
  33. package/v2Components/TemplatePreview/tests/__snapshots__/index.test.js.snap +10 -10
  34. package/v2Containers/CreativesContainer/SlideBoxContent.js +127 -62
  35. package/v2Containers/CreativesContainer/index.js +193 -136
  36. package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +0 -22
  37. package/v2Containers/InApp/constants.js +1 -0
  38. package/v2Containers/InApp/index.js +13 -13
  39. package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +4748 -4658
  40. package/v2Containers/Login/index.js +1 -2
  41. package/v2Containers/MobilePush/Create/index.js +1 -0
  42. package/v2Containers/MobilePush/commonMethods.js +7 -14
  43. package/v2Containers/MobilePush/tests/commonMethods.test.js +401 -0
  44. package/v2Containers/MobilePushNew/actions.js +116 -0
  45. package/v2Containers/MobilePushNew/components/CtaButtons.js +183 -0
  46. package/v2Containers/MobilePushNew/components/MediaUploaders.js +835 -0
  47. package/v2Containers/MobilePushNew/components/PlatformContentFields.js +346 -0
  48. package/v2Containers/MobilePushNew/components/index.js +5 -0
  49. package/v2Containers/MobilePushNew/components/tests/CtaButtons.test.js +565 -0
  50. package/v2Containers/MobilePushNew/components/tests/MediaUploaders.test.js +3180 -0
  51. package/v2Containers/MobilePushNew/components/tests/PlatformContentFields.test.js +654 -0
  52. package/v2Containers/MobilePushNew/constants.js +116 -0
  53. package/v2Containers/MobilePushNew/hooks/tests/usePlatformSync.test.js +1462 -0
  54. package/v2Containers/MobilePushNew/hooks/tests/useUpload.test.js +1459 -0
  55. package/v2Containers/MobilePushNew/hooks/usePlatformSync.js +366 -0
  56. package/v2Containers/MobilePushNew/hooks/useUpload.js +740 -0
  57. package/v2Containers/MobilePushNew/index.js +2158 -0
  58. package/v2Containers/MobilePushNew/index.scss +308 -0
  59. package/v2Containers/MobilePushNew/messages.js +272 -0
  60. package/v2Containers/MobilePushNew/reducer.js +160 -0
  61. package/v2Containers/MobilePushNew/sagas.js +193 -0
  62. package/v2Containers/MobilePushNew/selectors.js +55 -0
  63. package/v2Containers/MobilePushNew/tests/reducer.test.js +741 -0
  64. package/v2Containers/MobilePushNew/tests/sagas.test.js +864 -0
  65. package/v2Containers/MobilePushNew/tests/selectors.test.js +665 -0
  66. package/v2Containers/MobilePushNew/tests/utils.test.js +421 -0
  67. package/v2Containers/MobilePushNew/utils.js +84 -0
  68. package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +1176 -976
  69. package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +684 -424
  70. package/v2Containers/TagList/index.js +56 -10
  71. package/v2Containers/Templates/_templates.scss +100 -1
  72. package/v2Containers/Templates/index.js +170 -31
  73. package/v2Containers/Templates/messages.js +8 -0
  74. package/v2Containers/Templates/sagas.js +1 -0
  75. package/v2Containers/Whatsapp/constants.js +1 -0
  76. package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +3992 -3677
  77. package/assets/loading_img.gif +0 -0
@@ -0,0 +1,654 @@
1
+ import React from "react";
2
+ import { render, fireEvent } from "@testing-library/react";
3
+ import "@testing-library/jest-dom";
4
+ import { IntlProvider } from "react-intl";
5
+ import PlatformContentFields from "../PlatformContentFields";
6
+ import {
7
+ ANDROID, IOS, DEEP_LINK, EXTERNAL_LINK,
8
+ } from "../../constants";
9
+
10
+ // Simple mock components
11
+ jest.mock("@capillarytech/cap-ui-library/CapRow", () => ({ children }) => <div>{children}</div>);
12
+ jest.mock("@capillarytech/cap-ui-library/CapColumn", () => ({ children }) => <div>{children}</div>);
13
+ jest.mock("@capillarytech/cap-ui-library/CapInput", () => ({
14
+ value, onChange, errorMessage, error, ...props
15
+ }) => (
16
+ <div>
17
+ <input value={value || ""} onChange={onChange} error={error} {...props} />
18
+ {(errorMessage || error) && <div data-testid="error-message">{errorMessage || error}</div>}
19
+ </div>
20
+ ));
21
+ jest.mock("@capillarytech/cap-ui-library/CapHeading", () => ({ children }) => <div>{children}</div>);
22
+ jest.mock("@capillarytech/cap-ui-library/CapError", () => ({ children }) => <div data-testid="cap-error">{children}</div>);
23
+ jest.mock("@capillarytech/cap-ui-library/CapDivider", () => () => <div data-testid="divider" />);
24
+ jest.mock("@capillarytech/cap-ui-library/CapSelect", () => ({
25
+ CapCustomSelect: ({ value, onChange, options }) => (
26
+ <select value={value} onChange={(e) => onChange && onChange(e.target.value)}>
27
+ {options?.map((option) => (
28
+ <option key={option.value} value={option.value}>
29
+ {option.label}
30
+ </option>
31
+ ))}
32
+ </select>
33
+ ),
34
+ }));
35
+ jest.mock("@capillarytech/cap-ui-library/CapCheckbox", () => ({ checked, onChange, children }) => (
36
+ <div>
37
+ <input type="checkbox" checked={checked} onChange={onChange} data-testid="action-checkbox" />
38
+ <span>{children}</span>
39
+ </div>
40
+ ));
41
+ jest.mock("@capillarytech/cap-ui-library/CapLabel", () => ({ children }) => <label>{children}</label>);
42
+ jest.mock("@capillarytech/cap-ui-library/CapInfoNote", () => ({ message }) => <div data-testid="info-note">{message}</div>);
43
+
44
+ // Mock child components
45
+ jest.mock("../../../TagList", () => () => <div data-testid="tag-list">TagList</div>);
46
+ jest.mock("../MediaUploaders", () => () => <div data-testid="media-uploaders">MediaUploaders</div>);
47
+ jest.mock("../CtaButtons", () => () => <div data-testid="cta-buttons">CtaButtons</div>);
48
+
49
+ // Mock messages
50
+ jest.mock("../../messages", () => ({
51
+ sameContentNote: { id: "sameContentNote", defaultMessage: "Same content note" },
52
+ title: { id: "title", defaultMessage: "Title" },
53
+ titlePlaceholder: { id: "titlePlaceholder", defaultMessage: "Enter title" },
54
+ message: { id: "message", defaultMessage: "Message" },
55
+ messagePlaceholder: { id: "messagePlaceholder", defaultMessage: "Enter message" },
56
+ mediaType: { id: "mediaType", defaultMessage: "Media Type" },
57
+ buttonsAndLinks: { id: "buttonsAndLinks", defaultMessage: "Buttons and Links" },
58
+ optionalText: { id: "optionalText", defaultMessage: "Optional" },
59
+ actionOnClickBody: { id: "actionOnClickBody", defaultMessage: "Action on click" },
60
+ actionDescription: { id: "actionDescription", defaultMessage: "Action description" },
61
+ linkType: { id: "linkType", defaultMessage: "Link Type" },
62
+ selectDeepLink: { id: "selectDeepLink", defaultMessage: "Select deep link" },
63
+ deepLink: { id: "deepLink", defaultMessage: "Deep Link" },
64
+ externalLink: { id: "externalLink", defaultMessage: "External Link" },
65
+ enterExternalLink: { id: "enterExternalLink", defaultMessage: "Enter external link" },
66
+ deepLinkKeys: { id: "deepLinkKeys", defaultMessage: "Deep Link Keys" },
67
+ deepLinkKeysPlaceholder: { id: "deepLinkKeysPlaceholder", defaultMessage: "Enter {key}" },
68
+ deepLinkKeysRequired: { id: "deepLinkKeysRequired", defaultMessage: "Deep link keys are required" },
69
+ }));
70
+
71
+ // Mock constants
72
+ jest.mock("../../constants", () => ({
73
+ ANDROID: "android",
74
+ IOS: "ios",
75
+ CAROUSEL: "CAROUSEL",
76
+ MEDIA_TYPES_OPTIONS: [
77
+ { value: "IMAGE", label: "Image" },
78
+ { value: "VIDEO", label: "Video" },
79
+ { value: "CAROUSEL", label: "Carousel" },
80
+ ],
81
+ LINK_TYPE_OPTIONS: [
82
+ { value: "DEEP_LINK", label: "Deep Link" },
83
+ { value: "EXTERNAL_LINK", label: "External Link" },
84
+ ],
85
+ DEEP_LINK: "DEEP_LINK",
86
+ EXTERNAL_LINK: "EXTERNAL_LINK",
87
+ }));
88
+
89
+ describe("PlatformContentFields", () => {
90
+ const defaultProps = {
91
+ deviceType: ANDROID,
92
+ content: {
93
+ title: "Test Title",
94
+ message: "Test Message",
95
+ mediaType: "IMAGE",
96
+ actionOnClick: false,
97
+ linkType: DEEP_LINK,
98
+ },
99
+ errors: {
100
+ title: "",
101
+ message: "",
102
+ externalLink: "",
103
+ },
104
+ handlers: {
105
+ handleTitleChange: jest.fn(),
106
+ handleMessageChange: jest.fn(),
107
+ handleMediaTypeChange: jest.fn(),
108
+ handleActionOnClickChange: jest.fn(),
109
+ handleLinkTypeChange: jest.fn(),
110
+ handleDeepLinkChange: jest.fn(),
111
+ handleExternalLinkChange: jest.fn(),
112
+ onTagSelect: jest.fn(),
113
+ handleOnTagsContextChange: jest.fn(),
114
+ },
115
+ tagListProps: {
116
+ tags: ["tag1", "tag2"],
117
+ },
118
+ mediaUploaderProps: {
119
+ uploadProps: "test",
120
+ },
121
+ ctaButtonProps: {
122
+ buttons: [],
123
+ },
124
+ linkProps: {
125
+ deepLink: [
126
+ { value: "link1", label: "Link 1" },
127
+ { value: "link2", label: "Link 2" },
128
+ ],
129
+ deepLinkValue: "link1",
130
+ externalLinkValue: "https://example.com",
131
+ },
132
+ sameContent: false,
133
+ formatMessage: jest.fn((msg) => msg.defaultMessage),
134
+ };
135
+
136
+ const renderComponent = (props = {}) => render(
137
+ <IntlProvider locale="en">
138
+ <PlatformContentFields {...defaultProps} {...props} />
139
+ </IntlProvider>
140
+ );
141
+
142
+ beforeEach(() => {
143
+ jest.clearAllMocks();
144
+ });
145
+
146
+ describe("Basic Rendering", () => {
147
+ it("should render without crashing", () => {
148
+ const { container } = renderComponent();
149
+ expect(container).toBeInTheDocument();
150
+ });
151
+
152
+ it("should render title input with correct value", () => {
153
+ const { container } = renderComponent();
154
+ const titleInput = container.querySelector('input[value="Test Title"]');
155
+ expect(titleInput).toBeInTheDocument();
156
+ });
157
+
158
+ it("should render message input with correct value", () => {
159
+ const { container } = renderComponent();
160
+ const messageInput = container.querySelector('input[value="Test Message"]');
161
+ expect(messageInput).toBeInTheDocument();
162
+ });
163
+
164
+ it("should render MediaUploaders component", () => {
165
+ const { getByTestId } = renderComponent();
166
+ expect(getByTestId("media-uploaders")).toBeInTheDocument();
167
+ });
168
+
169
+ it("should render CtaButtons component", () => {
170
+ const { getByTestId } = renderComponent();
171
+ expect(getByTestId("cta-buttons")).toBeInTheDocument();
172
+ });
173
+
174
+ it("should render TagList components", () => {
175
+ const { getAllByTestId } = renderComponent();
176
+ const tagLists = getAllByTestId("tag-list");
177
+ expect(tagLists).toHaveLength(2);
178
+ });
179
+ });
180
+
181
+ describe("Title Field", () => {
182
+ it("should call title change handler", () => {
183
+ const { container } = renderComponent();
184
+ const titleInput = container.querySelector('input[value="Test Title"]');
185
+
186
+ fireEvent.change(titleInput, { target: { value: "New Title" } });
187
+
188
+ expect(defaultProps.handlers.handleTitleChange).toHaveBeenCalled();
189
+ });
190
+
191
+ it("should display title error when present", () => {
192
+ const { getByTestId } = renderComponent({
193
+ errors: { ...defaultProps.errors, title: "Title is required" },
194
+ });
195
+
196
+ expect(getByTestId("cap-error")).toHaveTextContent("Title is required");
197
+ });
198
+ });
199
+
200
+ describe("Message Field", () => {
201
+ it("should call message change handler", () => {
202
+ const { container } = renderComponent();
203
+ const messageInput = container.querySelector('input[value="Test Message"]');
204
+
205
+ fireEvent.change(messageInput, { target: { value: "New Message" } });
206
+
207
+ expect(defaultProps.handlers.handleMessageChange).toHaveBeenCalled();
208
+ });
209
+
210
+ it("should display message error when present", () => {
211
+ const { getByTestId } = renderComponent({
212
+ errors: { ...defaultProps.errors, message: "Message is required" },
213
+ });
214
+
215
+ expect(getByTestId("cap-error")).toHaveTextContent("Message is required");
216
+ });
217
+ });
218
+
219
+ describe("Media Type Selection", () => {
220
+ it("should call media type change handler", () => {
221
+ const { container } = renderComponent();
222
+ const mediaSelect = container.querySelector('select');
223
+
224
+ fireEvent.change(mediaSelect, { target: { value: "VIDEO" } });
225
+
226
+ expect(defaultProps.handlers.handleMediaTypeChange).toHaveBeenCalled();
227
+ });
228
+ });
229
+
230
+ describe("Action on Click", () => {
231
+ it("should call action on click change handler", () => {
232
+ const { getByTestId } = renderComponent();
233
+ const checkbox = getByTestId("action-checkbox");
234
+
235
+ fireEvent.click(checkbox);
236
+
237
+ expect(defaultProps.handlers.handleActionOnClickChange).toHaveBeenCalled();
238
+ });
239
+
240
+ it("should show action controls when actionOnClick is true", () => {
241
+ const { container } = renderComponent({
242
+ content: { ...defaultProps.content, actionOnClick: true },
243
+ });
244
+
245
+ expect(container.textContent).toContain("Link Type");
246
+ });
247
+
248
+ it("should hide action controls when actionOnClick is false", () => {
249
+ const { container } = renderComponent({
250
+ content: { ...defaultProps.content, actionOnClick: false },
251
+ });
252
+
253
+ expect(container.textContent).not.toContain("Link Type");
254
+ });
255
+ });
256
+
257
+ describe("Device Type Handling", () => {
258
+ it("should render with Android device type", () => {
259
+ const { container } = renderComponent({ deviceType: ANDROID });
260
+ const titleInput = container.querySelector('#mobile-push-title-name-input-android');
261
+ expect(titleInput).toBeInTheDocument();
262
+ });
263
+
264
+ it("should render with iOS device type", () => {
265
+ const { container } = renderComponent({ deviceType: IOS });
266
+ const titleInput = container.querySelector('#mobile-push-title-name-input-ios');
267
+ expect(titleInput).toBeInTheDocument();
268
+ });
269
+ });
270
+
271
+ describe("External Link Input", () => {
272
+ it("should call external link change handler", () => {
273
+ const { container } = renderComponent({
274
+ content: { ...defaultProps.content, actionOnClick: true, linkType: EXTERNAL_LINK },
275
+ });
276
+
277
+ const externalLinkInput = container.querySelector('input[value="https://example.com"]');
278
+
279
+ if (externalLinkInput) {
280
+ fireEvent.change(externalLinkInput, { target: { value: "https://newlink.com" } });
281
+ expect(defaultProps.handlers.handleExternalLinkChange).toHaveBeenCalled();
282
+ }
283
+ });
284
+
285
+ it("should display external link error when present", () => {
286
+ const { getByTestId } = renderComponent({
287
+ content: { ...defaultProps.content, actionOnClick: true, linkType: EXTERNAL_LINK },
288
+ errors: { ...defaultProps.errors, externalLink: "Invalid URL" },
289
+ });
290
+
291
+ expect(getByTestId("cap-error")).toHaveTextContent("Invalid URL");
292
+ });
293
+ });
294
+
295
+ describe("Carousel Media Type", () => {
296
+ it("should hide action controls when mediaType is CAROUSEL", () => {
297
+ const { container } = renderComponent({
298
+ content: { ...defaultProps.content, mediaType: "CAROUSEL" },
299
+ });
300
+
301
+ expect(container.textContent).not.toContain("Buttons and Links");
302
+ expect(container.textContent).not.toContain("CtaButtons");
303
+ });
304
+
305
+ it("should show action controls when mediaType is not CAROUSEL", () => {
306
+ const { container } = renderComponent({
307
+ content: { ...defaultProps.content, mediaType: "IMAGE" },
308
+ });
309
+
310
+ expect(container.textContent).toContain("Buttons and Links");
311
+ });
312
+ });
313
+
314
+ describe("formatMessage Integration", () => {
315
+ it("should call formatMessage with correct arguments", () => {
316
+ renderComponent();
317
+
318
+ expect(defaultProps.formatMessage).toHaveBeenCalledWith(
319
+ expect.objectContaining({
320
+ id: "titlePlaceholder",
321
+ defaultMessage: "Enter title",
322
+ })
323
+ );
324
+ });
325
+ });
326
+
327
+ describe("Error Handling", () => {
328
+ it("should handle missing linkProps gracefully", () => {
329
+ const { container } = renderComponent({
330
+ linkProps: {},
331
+ });
332
+
333
+ expect(container).toBeInTheDocument();
334
+ });
335
+
336
+ it("should handle undefined values gracefully", () => {
337
+ const { container } = renderComponent({
338
+ content: {
339
+ ...defaultProps.content,
340
+ title: undefined,
341
+ message: undefined,
342
+ },
343
+ });
344
+
345
+ expect(container).toBeInTheDocument();
346
+ });
347
+ });
348
+
349
+ // Deep link keys handling - covering lines 109-120, 134, 297-303
350
+ it('should handle deep link keys with array from selection', () => {
351
+ const mockDeepLink = [
352
+ { value: 'test-deep-link', keys: ['key1', 'key2'] }
353
+ ];
354
+ const mockLinkProps = {
355
+ deepLink: mockDeepLink,
356
+ deepLinkValue: 'test-deep-link',
357
+ deepLinkKeysValue: ['key1', 'key2'],
358
+ externalLinkValue: '',
359
+ };
360
+ const mockContent = {
361
+ title: 'Test Title',
362
+ message: 'Test Message',
363
+ mediaType: 'IMAGE',
364
+ actionOnClick: true,
365
+ linkType: 'DEEP_LINK',
366
+ };
367
+
368
+ const { getByText, getByDisplayValue } = render(
369
+ <PlatformContentFields
370
+ deviceType="ANDROID"
371
+ content={mockContent}
372
+ errors={{}}
373
+ handlers={defaultProps.handlers}
374
+ tagListProps={defaultProps.tagListProps}
375
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
376
+ ctaButtonProps={defaultProps.ctaButtonProps}
377
+ linkProps={mockLinkProps}
378
+ sameContent={false}
379
+ formatMessage={defaultProps.formatMessage}
380
+ />
381
+ );
382
+
383
+ // Check that deep link keys are displayed correctly
384
+ expect(getByText('key1, key2')).toBeInTheDocument();
385
+ expect(getByDisplayValue('key1, key2')).toBeInTheDocument();
386
+ });
387
+
388
+ it('should handle deep link keys with single key from selection', () => {
389
+ const mockDeepLink = [
390
+ { value: 'test-deep-link', keys: 'single-key' }
391
+ ];
392
+ const mockLinkProps = {
393
+ deepLink: mockDeepLink,
394
+ deepLinkValue: 'test-deep-link',
395
+ deepLinkKeysValue: ['single-key'],
396
+ externalLinkValue: '',
397
+ };
398
+ const mockContent = {
399
+ title: 'Test Title',
400
+ message: 'Test Message',
401
+ mediaType: 'IMAGE',
402
+ actionOnClick: true,
403
+ linkType: 'DEEP_LINK',
404
+ };
405
+
406
+ const { getByText, getByDisplayValue } = render(
407
+ <PlatformContentFields
408
+ deviceType="ANDROID"
409
+ content={mockContent}
410
+ errors={{}}
411
+ handlers={defaultProps.handlers}
412
+ tagListProps={defaultProps.tagListProps}
413
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
414
+ ctaButtonProps={defaultProps.ctaButtonProps}
415
+ linkProps={mockLinkProps}
416
+ sameContent={false}
417
+ formatMessage={defaultProps.formatMessage}
418
+ />
419
+ );
420
+
421
+ // Check that single key is displayed correctly
422
+ expect(getByText('single-key')).toBeInTheDocument();
423
+ expect(getByDisplayValue('single-key')).toBeInTheDocument();
424
+ });
425
+
426
+ it('should handle deep link keys with no keys from selection but existing keys', () => {
427
+ const mockDeepLink = [
428
+ { value: 'test-deep-link', keys: [] } // No keys from selection
429
+ ];
430
+ const mockLinkProps = {
431
+ deepLink: mockDeepLink,
432
+ deepLinkValue: 'test-deep-link',
433
+ deepLinkKeysValue: ['existing-key1', 'existing-key2'], // But existing keys in value
434
+ externalLinkValue: '',
435
+ };
436
+ const mockContent = {
437
+ title: 'Test Title',
438
+ message: 'Test Message',
439
+ mediaType: 'IMAGE',
440
+ actionOnClick: true,
441
+ linkType: 'DEEP_LINK',
442
+ };
443
+
444
+ const { container } = render(
445
+ <PlatformContentFields
446
+ deviceType="ANDROID"
447
+ content={mockContent}
448
+ errors={{}}
449
+ handlers={defaultProps.handlers}
450
+ tagListProps={defaultProps.tagListProps}
451
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
452
+ ctaButtonProps={defaultProps.ctaButtonProps}
453
+ linkProps={mockLinkProps}
454
+ sameContent={false}
455
+ formatMessage={defaultProps.formatMessage}
456
+ />
457
+ );
458
+
459
+ // When no keys from selection, the deep link keys section should not render at all
460
+ expect(container.textContent).not.toContain('Deep Link Keys');
461
+ });
462
+
463
+ it('should handle deep link keys with no keys at all', () => {
464
+ const mockDeepLink = [
465
+ { value: 'test-deep-link', keys: [] } // No keys from selection
466
+ ];
467
+ const mockLinkProps = {
468
+ deepLink: mockDeepLink,
469
+ deepLinkValue: 'test-deep-link',
470
+ deepLinkKeysValue: [], // But no keys in the value
471
+ externalLinkValue: '',
472
+ };
473
+ const mockContent = {
474
+ title: 'Test Title',
475
+ message: 'Test Message',
476
+ mediaType: 'IMAGE',
477
+ actionOnClick: true,
478
+ linkType: 'DEEP_LINK',
479
+ };
480
+
481
+ const { container } = render(
482
+ <PlatformContentFields
483
+ deviceType="ANDROID"
484
+ content={mockContent}
485
+ errors={{}}
486
+ handlers={defaultProps.handlers}
487
+ tagListProps={defaultProps.tagListProps}
488
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
489
+ ctaButtonProps={defaultProps.ctaButtonProps}
490
+ linkProps={mockLinkProps}
491
+ sameContent={false}
492
+ formatMessage={defaultProps.formatMessage}
493
+ />
494
+ );
495
+
496
+ // When no keys from selection, the deep link keys section should not render at all
497
+ expect(container.textContent).not.toContain('Deep Link Keys');
498
+ });
499
+
500
+ it('should handle deep link keys with string value instead of array', () => {
501
+ const mockDeepLink = [
502
+ { value: 'test-deep-link', keys: 'single-key' }
503
+ ];
504
+ const mockLinkProps = {
505
+ deepLink: mockDeepLink,
506
+ deepLinkValue: 'test-deep-link',
507
+ deepLinkKeysValue: 'string-key-value',
508
+ externalLinkValue: '',
509
+ };
510
+ const mockContent = {
511
+ title: 'Test Title',
512
+ message: 'Test Message',
513
+ mediaType: 'IMAGE',
514
+ actionOnClick: true,
515
+ linkType: 'DEEP_LINK',
516
+ };
517
+
518
+ const { getByDisplayValue } = render(
519
+ <PlatformContentFields
520
+ deviceType="ANDROID"
521
+ content={mockContent}
522
+ errors={{}}
523
+ handlers={defaultProps.handlers}
524
+ tagListProps={defaultProps.tagListProps}
525
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
526
+ ctaButtonProps={defaultProps.ctaButtonProps}
527
+ linkProps={mockLinkProps}
528
+ sameContent={false}
529
+ formatMessage={defaultProps.formatMessage}
530
+ />
531
+ );
532
+
533
+ // Check that string value is displayed correctly
534
+ expect(getByDisplayValue('string-key-value')).toBeInTheDocument();
535
+ });
536
+
537
+ it('should handle deep link keys with undefined deepLinkKeysValue', () => {
538
+ const mockDeepLink = [
539
+ { value: 'test-deep-link', keys: ['key1', 'key2'] }
540
+ ];
541
+ const mockLinkProps = {
542
+ deepLink: mockDeepLink,
543
+ deepLinkValue: 'test-deep-link',
544
+ deepLinkKeysValue: undefined,
545
+ externalLinkValue: '',
546
+ };
547
+ const mockContent = {
548
+ title: 'Test Title',
549
+ message: 'Test Message',
550
+ mediaType: 'IMAGE',
551
+ actionOnClick: true,
552
+ linkType: 'DEEP_LINK',
553
+ };
554
+
555
+ const { getAllByDisplayValue } = render(
556
+ <PlatformContentFields
557
+ deviceType="ANDROID"
558
+ content={mockContent}
559
+ errors={{}}
560
+ handlers={defaultProps.handlers}
561
+ tagListProps={defaultProps.tagListProps}
562
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
563
+ ctaButtonProps={defaultProps.ctaButtonProps}
564
+ linkProps={mockLinkProps}
565
+ sameContent={false}
566
+ formatMessage={defaultProps.formatMessage}
567
+ />
568
+ );
569
+
570
+ // Check that empty string is displayed when deepLinkKeysValue is undefined
571
+ const emptyInputs = getAllByDisplayValue('');
572
+ expect(emptyInputs.length).toBeGreaterThan(0);
573
+ });
574
+
575
+ it('should handle deep link keys placeholder with fallback', () => {
576
+ const mockDeepLink = [
577
+ { value: 'test-deep-link', keys: ['key1', 'key2'] } // Need keys to trigger the section
578
+ ];
579
+ const mockLinkProps = {
580
+ deepLink: mockDeepLink,
581
+ deepLinkValue: 'test-deep-link',
582
+ deepLinkKeysValue: [],
583
+ externalLinkValue: '',
584
+ };
585
+ const mockContent = {
586
+ title: 'Test Title',
587
+ message: 'Test Message',
588
+ mediaType: 'IMAGE',
589
+ actionOnClick: true,
590
+ linkType: 'DEEP_LINK',
591
+ };
592
+
593
+ render(
594
+ <PlatformContentFields
595
+ deviceType="ANDROID"
596
+ content={mockContent}
597
+ errors={{}}
598
+ handlers={defaultProps.handlers}
599
+ tagListProps={defaultProps.tagListProps}
600
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
601
+ ctaButtonProps={defaultProps.ctaButtonProps}
602
+ linkProps={mockLinkProps}
603
+ sameContent={false}
604
+ formatMessage={defaultProps.formatMessage}
605
+ />
606
+ );
607
+
608
+ // Verify that formatMessage was called with the fallback placeholder
609
+ expect(defaultProps.formatMessage).toHaveBeenCalledWith(
610
+ expect.any(Object),
611
+ { key: 'key1, key2' }
612
+ );
613
+ });
614
+
615
+ it('should handle deep link keys error display', () => {
616
+ const mockDeepLink = [
617
+ { value: 'test-deep-link', keys: ['key1', 'key2'] }
618
+ ];
619
+ const mockLinkProps = {
620
+ deepLink: mockDeepLink,
621
+ deepLinkValue: 'test-deep-link',
622
+ deepLinkKeysValue: ['key1', 'key2'],
623
+ externalLinkValue: '',
624
+ };
625
+ const mockContent = {
626
+ title: 'Test Title',
627
+ message: 'Test Message',
628
+ mediaType: 'IMAGE',
629
+ actionOnClick: true,
630
+ linkType: 'DEEP_LINK',
631
+ };
632
+ const mockErrors = {
633
+ deepLinkKeys: 'Deep link keys error message',
634
+ };
635
+
636
+ const { getByText } = render(
637
+ <PlatformContentFields
638
+ deviceType="ANDROID"
639
+ content={mockContent}
640
+ errors={mockErrors}
641
+ handlers={defaultProps.handlers}
642
+ tagListProps={defaultProps.tagListProps}
643
+ mediaUploaderProps={defaultProps.mediaUploaderProps}
644
+ ctaButtonProps={defaultProps.ctaButtonProps}
645
+ linkProps={mockLinkProps}
646
+ sameContent={false}
647
+ formatMessage={defaultProps.formatMessage}
648
+ />
649
+ );
650
+
651
+ // Check that error message is displayed
652
+ expect(getByText('Deep link keys error message')).toBeInTheDocument();
653
+ });
654
+ });