@capillarytech/creatives-library 9.0.13 → 9.0.14
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/package.json +1 -1
- package/services/api.js +10 -0
- package/services/tests/api.test.js +83 -0
- package/v2Components/CommonTestAndPreview/UnifiedPreview/WhatsAppPreviewContent.js +5 -3
- package/v2Components/CommonTestAndPreview/index.js +7 -0
- package/v2Components/NavigationBar/index.js +27 -0
- package/v2Components/NavigationBar/messages.js +4 -0
- package/v2Components/NavigationBar/tests/index.test.js +19 -0
- package/v2Components/NewCallTask/index.js +6 -1
- package/v2Components/TemplatePreview/index.js +4 -2
- package/v2Containers/Cap/index.js +3 -1
- package/v2Containers/CommunicationFlow/CommunicationFlow.js +130 -20
- package/v2Containers/CommunicationFlow/CommunicationFlow.scss +154 -0
- package/v2Containers/CommunicationFlow/CommunicationFlowCard.js +240 -0
- package/v2Containers/CommunicationFlow/DemoPage.js +47 -0
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlow.test.js +369 -2
- package/v2Containers/CommunicationFlow/Tests/CommunicationFlowCard.test.js +619 -0
- package/v2Containers/CommunicationFlow/Tests/DemoPage.test.js +77 -0
- package/v2Containers/CommunicationFlow/Tests/getContentBody.test.js +933 -0
- package/v2Containers/CommunicationFlow/constants.js +45 -10
- package/v2Containers/CommunicationFlow/index.js +5 -2
- package/v2Containers/CommunicationFlow/messages.js +20 -0
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.js +94 -31
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/ChannelSelectionStep.scss +14 -11
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js +1144 -32
- package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/extractContentForPreview.js +183 -0
- package/v2Containers/CommunicationFlow/steps/CommunicationStrategyStep/CommunicationStrategyStep.js +3 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/DeliverySettingsSection.js +39 -0
- package/v2Containers/CommunicationFlow/steps/DeliverySettingsStep/Tests/DeliverySettingsSection.test.js +6 -2
- package/v2Containers/CommunicationFlow/utils/getContentBody.js +369 -0
- package/v2Containers/CommunicationFlow/utils/getContentBody.scss +19 -0
- package/v2Containers/CommunicationFlow/utils/getEnabledSteps.js +1 -1
- package/v2Containers/CreativesContainer/constants.js +6 -0
- package/v2Containers/CreativesContainer/index.js +68 -1
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +2 -2
- package/v2Containers/Templates/index.js +2 -2
- package/v2Containers/TemplatesV2/index.js +9 -1
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +41 -34
package/v2Containers/CommunicationFlow/steps/ChannelSelectionStep/Tests/ChannelSelectionStep.test.js
CHANGED
|
@@ -17,6 +17,21 @@ jest.setTimeout(30000);
|
|
|
17
17
|
// Larger wait budget for individual menu/portal transitions under parallel load.
|
|
18
18
|
const WAIT_OPTIONS = { timeout: 10000 };
|
|
19
19
|
|
|
20
|
+
jest.mock('v2Components/TestAndPreviewSlidebox', () => function MockTestAndPreviewSlidebox({ show, onClose, channel }) {
|
|
21
|
+
if (!show) return null;
|
|
22
|
+
return (
|
|
23
|
+
<div data-testid="test-and-preview-mock" data-channel={channel}>
|
|
24
|
+
<button type="button" data-testid="tap-close" onClick={onClose}>Close preview</button>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
jest.mock('../../../../Whatsapp/utils', () => ({
|
|
30
|
+
getWhatsappDocPreview: jest.fn(() => ({ docType: 'pdf', docUrl: 'http://doc' })),
|
|
31
|
+
getWhatsappQuickReply: jest.fn(() => null),
|
|
32
|
+
getWhatsappCarouselButtonView: jest.fn(() => null),
|
|
33
|
+
}));
|
|
34
|
+
|
|
20
35
|
jest.mock('../../../../CreativesContainer', () => function MockCreativesContainer({
|
|
21
36
|
getCreativesData,
|
|
22
37
|
handleCloseCreatives,
|
|
@@ -90,8 +105,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
90
105
|
);
|
|
91
106
|
|
|
92
107
|
expect(screen.getAllByText('Content template').length).toBeGreaterThanOrEqual(1);
|
|
93
|
-
expect(screen.
|
|
94
|
-
expect(screen.getByRole('button', { name: /content template/i })).toBeInTheDocument();
|
|
108
|
+
expect(screen.getByRole('button', { name: /add creative/i })).toBeInTheDocument();
|
|
95
109
|
expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
|
|
96
110
|
});
|
|
97
111
|
|
|
@@ -150,7 +164,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
150
164
|
/>,
|
|
151
165
|
);
|
|
152
166
|
|
|
153
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
167
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
154
168
|
await waitFor(() => {
|
|
155
169
|
expect(screen.getByRole('menu')).toBeInTheDocument();
|
|
156
170
|
}, WAIT_OPTIONS);
|
|
@@ -188,7 +202,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
188
202
|
/>,
|
|
189
203
|
);
|
|
190
204
|
|
|
191
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
205
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
192
206
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
193
207
|
await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
|
|
194
208
|
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
@@ -236,7 +250,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
236
250
|
});
|
|
237
251
|
});
|
|
238
252
|
|
|
239
|
-
it('preview menu
|
|
253
|
+
it('preview menu opens test-and-preview slidebox', async () => {
|
|
240
254
|
renderStep(
|
|
241
255
|
<ChannelSelectionStep
|
|
242
256
|
value={{
|
|
@@ -253,7 +267,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
253
267
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
254
268
|
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
255
269
|
|
|
256
|
-
expect(
|
|
270
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
257
271
|
});
|
|
258
272
|
|
|
259
273
|
it('delivery settings section forwards onDeliverySettingChange to onChange', async () => {
|
|
@@ -285,6 +299,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
285
299
|
onChange={onChange}
|
|
286
300
|
channels={CHANNELS}
|
|
287
301
|
incentivesData={{
|
|
302
|
+
enabled: true,
|
|
288
303
|
types: [
|
|
289
304
|
{ value: 'badges', label: 'Badges', isActive: true },
|
|
290
305
|
{ value: 'coupons', label: 'Coupons', isActive: true },
|
|
@@ -316,6 +331,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
316
331
|
onChange={onChange}
|
|
317
332
|
channels={CHANNELS}
|
|
318
333
|
incentivesData={{
|
|
334
|
+
enabled: true,
|
|
319
335
|
types: [{ value: 'badges', label: 'Badges', isActive: true }],
|
|
320
336
|
}}
|
|
321
337
|
/>,
|
|
@@ -342,7 +358,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
342
358
|
{
|
|
343
359
|
contentId: 'mp1',
|
|
344
360
|
channel: 'MOBILEPUSH',
|
|
345
|
-
templateData: {
|
|
361
|
+
templateData: { androidContent: { title: 'Push title here' } },
|
|
346
362
|
},
|
|
347
363
|
{
|
|
348
364
|
contentId: 'em2',
|
|
@@ -362,7 +378,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
362
378
|
expect(screen.getByText('from@example.com')).toBeInTheDocument();
|
|
363
379
|
});
|
|
364
380
|
|
|
365
|
-
it('mobile push preview
|
|
381
|
+
it('mobile push preview shows message body when title is absent', () => {
|
|
366
382
|
renderStep(
|
|
367
383
|
<ChannelSelectionStep
|
|
368
384
|
value={{
|
|
@@ -370,7 +386,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
370
386
|
{
|
|
371
387
|
contentId: 'mp-body',
|
|
372
388
|
channel: 'MOBILEPUSH',
|
|
373
|
-
templateData: {
|
|
389
|
+
templateData: { androidContent: { message: 'Body only preview text' } },
|
|
374
390
|
},
|
|
375
391
|
],
|
|
376
392
|
}}
|
|
@@ -422,7 +438,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
422
438
|
/>,
|
|
423
439
|
);
|
|
424
440
|
|
|
425
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
441
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
426
442
|
expect(screen.queryByRole('menu')).not.toBeInTheDocument();
|
|
427
443
|
});
|
|
428
444
|
|
|
@@ -436,7 +452,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
436
452
|
/>,
|
|
437
453
|
);
|
|
438
454
|
|
|
439
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
455
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
440
456
|
expect(screen.queryByRole('menu')).not.toBeInTheDocument();
|
|
441
457
|
});
|
|
442
458
|
|
|
@@ -462,14 +478,14 @@ describe('ChannelSelectionStep', () => {
|
|
|
462
478
|
renderStep(
|
|
463
479
|
<ChannelSelectionStep value={null} onChange={jest.fn()} channels={CHANNELS} />,
|
|
464
480
|
);
|
|
465
|
-
expect(screen.
|
|
481
|
+
expect(screen.getByRole('button', { name: /add creative/i })).toBeInTheDocument();
|
|
466
482
|
});
|
|
467
483
|
|
|
468
484
|
it('uses CHANNELS fallback when channels prop is null', async () => {
|
|
469
485
|
renderStep(
|
|
470
486
|
<ChannelSelectionStep value={{ contentItems: [] }} onChange={jest.fn()} channels={null} />,
|
|
471
487
|
);
|
|
472
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
488
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
473
489
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
474
490
|
expect(within(screen.getByRole('menu')).getByText('SMS')).toBeInTheDocument();
|
|
475
491
|
});
|
|
@@ -492,7 +508,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
492
508
|
]}
|
|
493
509
|
/>,
|
|
494
510
|
);
|
|
495
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
511
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
496
512
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
497
513
|
await userEvent.click(screen.getByText('Custom'));
|
|
498
514
|
expect(onChange).toHaveBeenCalledWith({ channel: 'CUSTOM', channels: [] });
|
|
@@ -504,7 +520,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
504
520
|
renderStep(
|
|
505
521
|
<ChannelSelectionStep value={{ contentItems: [] }} onChange={onChange} channels={CHANNELS} />,
|
|
506
522
|
);
|
|
507
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
523
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
508
524
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
509
525
|
await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
|
|
510
526
|
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
@@ -542,14 +558,14 @@ describe('ChannelSelectionStep', () => {
|
|
|
542
558
|
value={{ contentItems: [] }}
|
|
543
559
|
onChange={jest.fn()}
|
|
544
560
|
channels={CHANNELS}
|
|
545
|
-
creativesMode="
|
|
561
|
+
creativesMode="create"
|
|
546
562
|
/>,
|
|
547
563
|
);
|
|
548
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
564
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
549
565
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
550
566
|
await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
|
|
551
567
|
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
552
|
-
expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-mode', '
|
|
568
|
+
expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-mode', 'create');
|
|
553
569
|
});
|
|
554
570
|
|
|
555
571
|
it('appends to selectedOfferDetails when an incentive is already selected', async () => {
|
|
@@ -561,6 +577,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
561
577
|
channels={CHANNELS}
|
|
562
578
|
selectedOfferDetails={[{ type: 'existing' }]}
|
|
563
579
|
incentivesData={{
|
|
580
|
+
enabled: true,
|
|
564
581
|
types: [{ value: 'badges', label: 'Badges', isActive: true }],
|
|
565
582
|
}}
|
|
566
583
|
/>,
|
|
@@ -573,12 +590,16 @@ describe('ChannelSelectionStep', () => {
|
|
|
573
590
|
});
|
|
574
591
|
});
|
|
575
592
|
|
|
576
|
-
it('
|
|
593
|
+
it('line message text and non-string smsBody fallback show in preview', () => {
|
|
577
594
|
renderStep(
|
|
578
595
|
<ChannelSelectionStep
|
|
579
596
|
value={{
|
|
580
597
|
contentItems: [
|
|
581
|
-
{
|
|
598
|
+
{
|
|
599
|
+
contentId: 'line1',
|
|
600
|
+
channel: 'LINE',
|
|
601
|
+
templateData: { messageBody: JSON.stringify({ messages: [{ text: 'Line message preview' }] }) },
|
|
602
|
+
},
|
|
582
603
|
{ contentId: 'sms-num', channel: 'SMS', templateData: { smsBody: 42 } },
|
|
583
604
|
],
|
|
584
605
|
}}
|
|
@@ -598,22 +619,22 @@ describe('ChannelSelectionStep', () => {
|
|
|
598
619
|
{
|
|
599
620
|
contentId: 'mpush1',
|
|
600
621
|
channel: 'MPUSH',
|
|
601
|
-
templateData: {
|
|
622
|
+
templateData: { androidContent: { title: 'Direct MPUSH title' } },
|
|
602
623
|
},
|
|
603
624
|
{
|
|
604
625
|
contentId: 'and',
|
|
605
626
|
channel: 'MOBILEPUSH',
|
|
606
|
-
templateData: { androidContent: {
|
|
627
|
+
templateData: { androidContent: { title: 'Android title' } },
|
|
607
628
|
},
|
|
608
629
|
{
|
|
609
630
|
contentId: 'ios',
|
|
610
631
|
channel: 'MOBILEPUSH',
|
|
611
|
-
templateData: { iosContent: {
|
|
632
|
+
templateData: { iosContent: { message: 'Ios body text' } },
|
|
612
633
|
},
|
|
613
634
|
{
|
|
614
635
|
contentId: 'and-body',
|
|
615
636
|
channel: 'MOBILEPUSH',
|
|
616
|
-
templateData: { androidContent: {
|
|
637
|
+
templateData: { androidContent: { message: 'Android notif body' } },
|
|
617
638
|
},
|
|
618
639
|
],
|
|
619
640
|
}}
|
|
@@ -685,11 +706,11 @@ describe('ChannelSelectionStep', () => {
|
|
|
685
706
|
channels={CHANNELS}
|
|
686
707
|
/>,
|
|
687
708
|
);
|
|
688
|
-
expect(screen.
|
|
709
|
+
expect(screen.getByLabelText('Show more content options icon')).toBeInTheDocument();
|
|
689
710
|
parseSpy.mockRestore();
|
|
690
711
|
});
|
|
691
712
|
|
|
692
|
-
it('Viber preview
|
|
713
|
+
it('Viber preview renders card when parsed JSON has no text (empty messageBody)', () => {
|
|
693
714
|
renderStep(
|
|
694
715
|
<ChannelSelectionStep
|
|
695
716
|
value={{
|
|
@@ -705,7 +726,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
705
726
|
channels={CHANNELS}
|
|
706
727
|
/>,
|
|
707
728
|
);
|
|
708
|
-
expect(screen.
|
|
729
|
+
expect(screen.getByLabelText('Show more content options icon')).toBeInTheDocument();
|
|
709
730
|
});
|
|
710
731
|
|
|
711
732
|
it('does not open creatives when clicked channel is in channelsToDisable', async () => {
|
|
@@ -719,7 +740,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
719
740
|
channelsToDisable={['SMS']}
|
|
720
741
|
/>,
|
|
721
742
|
);
|
|
722
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
743
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
723
744
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
724
745
|
// SMS is in channelsToDisable — clicking it should not call onChange or show creatives
|
|
725
746
|
await userEvent.click(within(screen.getByRole('menu')).getByText('SMS'));
|
|
@@ -774,7 +795,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
774
795
|
channelsToDisable={['EMAIL']}
|
|
775
796
|
/>,
|
|
776
797
|
);
|
|
777
|
-
await userEvent.click(screen.getByRole('button', { name: /
|
|
798
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
778
799
|
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
779
800
|
// Click the disabled EMAIL menu item — handleChannelSelect should return early
|
|
780
801
|
await userEvent.click(within(screen.getByRole('menu')).getByText('Email'));
|
|
@@ -782,8 +803,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
782
803
|
expect(screen.queryByTestId('creatives-mock')).not.toBeInTheDocument();
|
|
783
804
|
});
|
|
784
805
|
|
|
785
|
-
it('ios notification body shows as preview when iosContent.
|
|
786
|
-
// Covers line 125: templateData.iosContent.notificationBody fallback branch
|
|
806
|
+
it('ios notification body shows as preview when iosContent.message is present', () => {
|
|
787
807
|
renderStep(
|
|
788
808
|
<ChannelSelectionStep
|
|
789
809
|
value={{
|
|
@@ -791,7 +811,7 @@ describe('ChannelSelectionStep', () => {
|
|
|
791
811
|
{
|
|
792
812
|
contentId: 'ios-nb',
|
|
793
813
|
channel: 'MOBILEPUSH',
|
|
794
|
-
templateData: { iosContent: {
|
|
814
|
+
templateData: { iosContent: { message: 'IOS notif body text' } },
|
|
795
815
|
},
|
|
796
816
|
],
|
|
797
817
|
}}
|
|
@@ -801,4 +821,1096 @@ describe('ChannelSelectionStep', () => {
|
|
|
801
821
|
);
|
|
802
822
|
expect(screen.getByText(/IOS notif body text/)).toBeInTheDocument();
|
|
803
823
|
});
|
|
824
|
+
|
|
825
|
+
// ── handleCloseTestAndPreview ────────────────────────────────────────────────
|
|
826
|
+
|
|
827
|
+
it('closing TestAndPreview via onClose hides the slidebox', async () => {
|
|
828
|
+
renderStep(
|
|
829
|
+
<ChannelSelectionStep
|
|
830
|
+
value={{
|
|
831
|
+
contentItems: [{ contentId: 'c-close', channel: 'SMS', templateData: { smsBody: 'x' } }],
|
|
832
|
+
}}
|
|
833
|
+
onChange={jest.fn()}
|
|
834
|
+
channels={CHANNELS}
|
|
835
|
+
/>,
|
|
836
|
+
);
|
|
837
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
838
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
839
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
840
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
841
|
+
|
|
842
|
+
await userEvent.click(screen.getByTestId('tap-close'));
|
|
843
|
+
|
|
844
|
+
expect(screen.queryByTestId('test-and-preview-mock')).not.toBeInTheDocument();
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
// ── PREVIEW_TEST_UNSUPPORTED_CHANNELS ────────────────────────────────────────
|
|
848
|
+
|
|
849
|
+
it.each(['LINE', 'FACEBOOK', 'CALLTASK'])(
|
|
850
|
+
'%s content card omits the "Preview and Test" menu item',
|
|
851
|
+
async (channel) => {
|
|
852
|
+
renderStep(
|
|
853
|
+
<ChannelSelectionStep
|
|
854
|
+
value={{
|
|
855
|
+
contentItems: [{ contentId: 'unsupported-1', channel, templateData: { messageBody: 'body' } }],
|
|
856
|
+
}}
|
|
857
|
+
onChange={jest.fn()}
|
|
858
|
+
channels={CHANNELS}
|
|
859
|
+
/>,
|
|
860
|
+
);
|
|
861
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
862
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
863
|
+
expect(within(screen.getByRole('menu')).queryByText('Preview and Test')).not.toBeInTheDocument();
|
|
864
|
+
},
|
|
865
|
+
);
|
|
866
|
+
|
|
867
|
+
// ── extractContentForPreview — EMAIL ─────────────────────────────────────────
|
|
868
|
+
|
|
869
|
+
it('EMAIL preview passes EMAIL channel to TestAndPreview slidebox', async () => {
|
|
870
|
+
renderStep(
|
|
871
|
+
<ChannelSelectionStep
|
|
872
|
+
value={{
|
|
873
|
+
contentItems: [{
|
|
874
|
+
contentId: 'ep1',
|
|
875
|
+
channel: 'EMAIL',
|
|
876
|
+
templateData: { emailSubject: 'Hello', emailBody: '<p>Body</p>' },
|
|
877
|
+
}],
|
|
878
|
+
}}
|
|
879
|
+
onChange={jest.fn()}
|
|
880
|
+
channels={CHANNELS}
|
|
881
|
+
/>,
|
|
882
|
+
);
|
|
883
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
884
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
885
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
886
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
887
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'EMAIL');
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
it('EMAIL preview uses emailHtml when emailBody is absent', async () => {
|
|
891
|
+
renderStep(
|
|
892
|
+
<ChannelSelectionStep
|
|
893
|
+
value={{
|
|
894
|
+
contentItems: [{
|
|
895
|
+
contentId: 'ep2',
|
|
896
|
+
channel: 'EMAIL',
|
|
897
|
+
templateData: { emailHtml: '<b>HTML</b>' },
|
|
898
|
+
}],
|
|
899
|
+
}}
|
|
900
|
+
onChange={jest.fn()}
|
|
901
|
+
channels={CHANNELS}
|
|
902
|
+
/>,
|
|
903
|
+
);
|
|
904
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
905
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
906
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
907
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
// ── extractContentForPreview — MOBILEPUSH / MPUSH ────────────────────────────
|
|
911
|
+
|
|
912
|
+
it('MOBILEPUSH preview passes MOBILEPUSH channel and handles expandableDetails optional chaining', async () => {
|
|
913
|
+
renderStep(
|
|
914
|
+
<ChannelSelectionStep
|
|
915
|
+
value={{
|
|
916
|
+
contentItems: [{
|
|
917
|
+
contentId: 'mp-ep',
|
|
918
|
+
channel: 'MOBILEPUSH',
|
|
919
|
+
templateData: {
|
|
920
|
+
accountId: 'acc1',
|
|
921
|
+
androidContent: {
|
|
922
|
+
title: 'And title',
|
|
923
|
+
message: 'And msg',
|
|
924
|
+
expandableDetails: {
|
|
925
|
+
image: 'http://img.png',
|
|
926
|
+
media: ['http://vid.mp4'],
|
|
927
|
+
ctas: [{ label: 'Click', actionLink: 'http://link' }],
|
|
928
|
+
carouselData: [{ id: 1 }],
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
iosContent: { title: 'iOS title', message: 'iOS msg' },
|
|
932
|
+
},
|
|
933
|
+
}],
|
|
934
|
+
}}
|
|
935
|
+
onChange={jest.fn()}
|
|
936
|
+
channels={CHANNELS}
|
|
937
|
+
/>,
|
|
938
|
+
);
|
|
939
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
940
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
941
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
942
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
943
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'MOBILEPUSH');
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
it('MOBILEPUSH preview with no expandableDetails uses fallback empty values', async () => {
|
|
947
|
+
renderStep(
|
|
948
|
+
<ChannelSelectionStep
|
|
949
|
+
value={{
|
|
950
|
+
contentItems: [{
|
|
951
|
+
contentId: 'mp-no-exp',
|
|
952
|
+
channel: 'MOBILEPUSH',
|
|
953
|
+
templateData: { androidContent: {}, iosContent: {} },
|
|
954
|
+
}],
|
|
955
|
+
}}
|
|
956
|
+
onChange={jest.fn()}
|
|
957
|
+
channels={CHANNELS}
|
|
958
|
+
/>,
|
|
959
|
+
);
|
|
960
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
961
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
962
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
963
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
it('MPUSH preview hits the MOBILEPUSH branch (same code path)', async () => {
|
|
967
|
+
renderStep(
|
|
968
|
+
<ChannelSelectionStep
|
|
969
|
+
value={{
|
|
970
|
+
contentItems: [{
|
|
971
|
+
contentId: 'mpush-ep',
|
|
972
|
+
channel: 'MPUSH',
|
|
973
|
+
templateData: { androidContent: { title: 'MPUSH title' } },
|
|
974
|
+
}],
|
|
975
|
+
}}
|
|
976
|
+
onChange={jest.fn()}
|
|
977
|
+
channels={CHANNELS}
|
|
978
|
+
/>,
|
|
979
|
+
);
|
|
980
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
981
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
982
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
983
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
984
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'MPUSH');
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// ── extractContentForPreview — INAPP ─────────────────────────────────────────
|
|
988
|
+
|
|
989
|
+
it('INAPP preview with image sets templateMediaType IMAGE', async () => {
|
|
990
|
+
renderStep(
|
|
991
|
+
<ChannelSelectionStep
|
|
992
|
+
value={{
|
|
993
|
+
contentItems: [{
|
|
994
|
+
contentId: 'inapp-img',
|
|
995
|
+
channel: 'INAPP',
|
|
996
|
+
templateData: {
|
|
997
|
+
accountId: 'acc-inapp',
|
|
998
|
+
androidContent: {
|
|
999
|
+
title: 'InApp title',
|
|
1000
|
+
message: 'InApp msg',
|
|
1001
|
+
bodyType: 'BANNER',
|
|
1002
|
+
expandableDetails: {
|
|
1003
|
+
image: 'http://inapp-img.png',
|
|
1004
|
+
ctas: [{ label: 'Go', actionLink: 'http://deep' }],
|
|
1005
|
+
},
|
|
1006
|
+
},
|
|
1007
|
+
iosContent: {
|
|
1008
|
+
expandableDetails: { image: 'http://ios-img.png' },
|
|
1009
|
+
},
|
|
1010
|
+
},
|
|
1011
|
+
}],
|
|
1012
|
+
}}
|
|
1013
|
+
onChange={jest.fn()}
|
|
1014
|
+
channels={CHANNELS}
|
|
1015
|
+
/>,
|
|
1016
|
+
);
|
|
1017
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1018
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1019
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1020
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1021
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'INAPP');
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
it('INAPP preview without image sets templateMediaType TEXT and deepLinkValue null when no CTAs', async () => {
|
|
1025
|
+
renderStep(
|
|
1026
|
+
<ChannelSelectionStep
|
|
1027
|
+
value={{
|
|
1028
|
+
contentItems: [{
|
|
1029
|
+
contentId: 'inapp-noimg',
|
|
1030
|
+
channel: 'INAPP',
|
|
1031
|
+
templateData: {
|
|
1032
|
+
androidContent: { title: 'Title', expandableDetails: { ctas: [] } },
|
|
1033
|
+
iosContent: {},
|
|
1034
|
+
},
|
|
1035
|
+
}],
|
|
1036
|
+
}}
|
|
1037
|
+
onChange={jest.fn()}
|
|
1038
|
+
channels={CHANNELS}
|
|
1039
|
+
/>,
|
|
1040
|
+
);
|
|
1041
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1042
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1043
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1044
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// ── extractContentForPreview — WHATSAPP ──────────────────────────────────────
|
|
1048
|
+
|
|
1049
|
+
it('WHATSAPP preview with IMAGE mediaType populates whatsappImageSrc', async () => {
|
|
1050
|
+
renderStep(
|
|
1051
|
+
<ChannelSelectionStep
|
|
1052
|
+
value={{
|
|
1053
|
+
contentItems: [{
|
|
1054
|
+
contentId: 'wa-img',
|
|
1055
|
+
channel: 'WHATSAPP',
|
|
1056
|
+
templateData: {
|
|
1057
|
+
messageBody: 'WA body',
|
|
1058
|
+
accountId: 'wa-acc',
|
|
1059
|
+
sourceAccountIdentifier: 'wa-src',
|
|
1060
|
+
accountName: 'WA Name',
|
|
1061
|
+
templateConfigs: {
|
|
1062
|
+
mediaType: 'IMAGE',
|
|
1063
|
+
whatsappMedia: { url: 'http://img.png', header: 'Header', footer: 'Footer' },
|
|
1064
|
+
cards: [],
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
}],
|
|
1068
|
+
}}
|
|
1069
|
+
onChange={jest.fn()}
|
|
1070
|
+
channels={CHANNELS}
|
|
1071
|
+
/>,
|
|
1072
|
+
);
|
|
1073
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1074
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1075
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1076
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1077
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'WHATSAPP');
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
it('WHATSAPP preview with VIDEO mediaType populates whatsappVideoPreviewImg', async () => {
|
|
1081
|
+
renderStep(
|
|
1082
|
+
<ChannelSelectionStep
|
|
1083
|
+
value={{
|
|
1084
|
+
contentItems: [{
|
|
1085
|
+
contentId: 'wa-vid',
|
|
1086
|
+
channel: 'WHATSAPP',
|
|
1087
|
+
templateData: {
|
|
1088
|
+
messageBody: 'WA video',
|
|
1089
|
+
templateConfigs: {
|
|
1090
|
+
mediaType: 'VIDEO',
|
|
1091
|
+
whatsappMedia: { previewUrl: 'http://thumb.png' },
|
|
1092
|
+
},
|
|
1093
|
+
},
|
|
1094
|
+
}],
|
|
1095
|
+
}}
|
|
1096
|
+
onChange={jest.fn()}
|
|
1097
|
+
channels={CHANNELS}
|
|
1098
|
+
/>,
|
|
1099
|
+
);
|
|
1100
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1101
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1102
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1103
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it('WHATSAPP preview with DOCUMENT + docParams calls getWhatsappDocPreview', async () => {
|
|
1107
|
+
renderStep(
|
|
1108
|
+
<ChannelSelectionStep
|
|
1109
|
+
value={{
|
|
1110
|
+
contentItems: [{
|
|
1111
|
+
contentId: 'wa-doc',
|
|
1112
|
+
channel: 'WHATSAPP',
|
|
1113
|
+
templateData: {
|
|
1114
|
+
templateConfigs: {
|
|
1115
|
+
mediaType: 'DOCUMENT',
|
|
1116
|
+
whatsappMedia: {
|
|
1117
|
+
previewUrl: 'http://doc-thumb.png',
|
|
1118
|
+
docParams: { fileName: 'file.pdf', fileSize: 100 },
|
|
1119
|
+
},
|
|
1120
|
+
},
|
|
1121
|
+
},
|
|
1122
|
+
}],
|
|
1123
|
+
}}
|
|
1124
|
+
onChange={jest.fn()}
|
|
1125
|
+
channels={CHANNELS}
|
|
1126
|
+
/>,
|
|
1127
|
+
);
|
|
1128
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1129
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1130
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1131
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1132
|
+
});
|
|
1133
|
+
|
|
1134
|
+
it('WHATSAPP preview with DOCUMENT without docParams sets docPreview null', async () => {
|
|
1135
|
+
renderStep(
|
|
1136
|
+
<ChannelSelectionStep
|
|
1137
|
+
value={{
|
|
1138
|
+
contentItems: [{
|
|
1139
|
+
contentId: 'wa-doc-null',
|
|
1140
|
+
channel: 'WHATSAPP',
|
|
1141
|
+
templateData: {
|
|
1142
|
+
templateConfigs: {
|
|
1143
|
+
mediaType: 'DOCUMENT',
|
|
1144
|
+
whatsappMedia: {},
|
|
1145
|
+
},
|
|
1146
|
+
},
|
|
1147
|
+
}],
|
|
1148
|
+
}}
|
|
1149
|
+
onChange={jest.fn()}
|
|
1150
|
+
channels={CHANNELS}
|
|
1151
|
+
/>,
|
|
1152
|
+
);
|
|
1153
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1154
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1155
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1156
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
it('WHATSAPP preview with CTA buttons populates ctaData', async () => {
|
|
1160
|
+
renderStep(
|
|
1161
|
+
<ChannelSelectionStep
|
|
1162
|
+
value={{
|
|
1163
|
+
contentItems: [{
|
|
1164
|
+
contentId: 'wa-cta',
|
|
1165
|
+
channel: 'WHATSAPP',
|
|
1166
|
+
templateData: {
|
|
1167
|
+
templateConfigs: {
|
|
1168
|
+
buttonType: 'CTA',
|
|
1169
|
+
buttons: [{ label: 'Buy', url: 'http://buy' }],
|
|
1170
|
+
},
|
|
1171
|
+
},
|
|
1172
|
+
}],
|
|
1173
|
+
}}
|
|
1174
|
+
onChange={jest.fn()}
|
|
1175
|
+
channels={CHANNELS}
|
|
1176
|
+
/>,
|
|
1177
|
+
);
|
|
1178
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1179
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1180
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1181
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
it('WHATSAPP preview with QUICK_REPLY buttons populates quickReplyData', async () => {
|
|
1185
|
+
renderStep(
|
|
1186
|
+
<ChannelSelectionStep
|
|
1187
|
+
value={{
|
|
1188
|
+
contentItems: [{
|
|
1189
|
+
contentId: 'wa-qr',
|
|
1190
|
+
channel: 'WHATSAPP',
|
|
1191
|
+
templateData: {
|
|
1192
|
+
templateConfigs: {
|
|
1193
|
+
buttonType: 'QUICK_REPLY',
|
|
1194
|
+
buttons: [{ text: 'Yes' }, { text: 'No' }],
|
|
1195
|
+
cards: [{ id: 'card1' }],
|
|
1196
|
+
},
|
|
1197
|
+
},
|
|
1198
|
+
}],
|
|
1199
|
+
}}
|
|
1200
|
+
onChange={jest.fn()}
|
|
1201
|
+
channels={CHANNELS}
|
|
1202
|
+
/>,
|
|
1203
|
+
);
|
|
1204
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1205
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1206
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1207
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
// ── extractContentForPreview — VIBER ─────────────────────────────────────────
|
|
1211
|
+
|
|
1212
|
+
it('VIBER preview with string messageBody containing video and accountDetails object', async () => {
|
|
1213
|
+
const messageBody = JSON.stringify({
|
|
1214
|
+
content: {
|
|
1215
|
+
text: 'Hello from Viber',
|
|
1216
|
+
image: { url: 'http://img.png' },
|
|
1217
|
+
button: { text: 'Click' },
|
|
1218
|
+
video: { url: 'http://vid.mp4', thumbnailUrl: 'http://thumb.jpg' },
|
|
1219
|
+
},
|
|
1220
|
+
});
|
|
1221
|
+
renderStep(
|
|
1222
|
+
<ChannelSelectionStep
|
|
1223
|
+
value={{
|
|
1224
|
+
contentItems: [{
|
|
1225
|
+
contentId: 'vib-full',
|
|
1226
|
+
channel: 'VIBER',
|
|
1227
|
+
templateData: {
|
|
1228
|
+
messageBody,
|
|
1229
|
+
accountDetails: { name: 'My Brand', accountName: 'fallback' },
|
|
1230
|
+
},
|
|
1231
|
+
}],
|
|
1232
|
+
}}
|
|
1233
|
+
onChange={jest.fn()}
|
|
1234
|
+
channels={CHANNELS}
|
|
1235
|
+
/>,
|
|
1236
|
+
);
|
|
1237
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1238
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1239
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1240
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1241
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'VIBER');
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
it('VIBER preview with string accountDetails', async () => {
|
|
1245
|
+
renderStep(
|
|
1246
|
+
<ChannelSelectionStep
|
|
1247
|
+
value={{
|
|
1248
|
+
contentItems: [{
|
|
1249
|
+
contentId: 'vib-str-acct',
|
|
1250
|
+
channel: 'VIBER',
|
|
1251
|
+
templateData: {
|
|
1252
|
+
messageBody: JSON.stringify({ content: { text: 'Hi' } }),
|
|
1253
|
+
accountDetails: JSON.stringify({ accountName: 'Brand Name' }),
|
|
1254
|
+
},
|
|
1255
|
+
}],
|
|
1256
|
+
}}
|
|
1257
|
+
onChange={jest.fn()}
|
|
1258
|
+
channels={CHANNELS}
|
|
1259
|
+
/>,
|
|
1260
|
+
);
|
|
1261
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1262
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1263
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1264
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
it('VIBER preview with object messageBody (non-string) uses it directly', async () => {
|
|
1268
|
+
renderStep(
|
|
1269
|
+
<ChannelSelectionStep
|
|
1270
|
+
value={{
|
|
1271
|
+
contentItems: [{
|
|
1272
|
+
contentId: 'vib-obj',
|
|
1273
|
+
channel: 'VIBER',
|
|
1274
|
+
templateData: {
|
|
1275
|
+
messageBody: { content: { text: 'Obj message' } },
|
|
1276
|
+
accountDetails: {},
|
|
1277
|
+
},
|
|
1278
|
+
}],
|
|
1279
|
+
}}
|
|
1280
|
+
onChange={jest.fn()}
|
|
1281
|
+
channels={CHANNELS}
|
|
1282
|
+
/>,
|
|
1283
|
+
);
|
|
1284
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1285
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1286
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1287
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
it('VIBER preview: messageBody JSON.parse throws falls back to empty object', async () => {
|
|
1291
|
+
const spy = jest.spyOn(JSON, 'parse').mockImplementationOnce(() => {
|
|
1292
|
+
throw new SyntaxError('bad json');
|
|
1293
|
+
});
|
|
1294
|
+
renderStep(
|
|
1295
|
+
<ChannelSelectionStep
|
|
1296
|
+
value={{
|
|
1297
|
+
contentItems: [{
|
|
1298
|
+
contentId: 'vib-throws',
|
|
1299
|
+
channel: 'VIBER',
|
|
1300
|
+
templateData: { messageBody: 'invalid json', accountDetails: {} },
|
|
1301
|
+
}],
|
|
1302
|
+
}}
|
|
1303
|
+
onChange={jest.fn()}
|
|
1304
|
+
channels={CHANNELS}
|
|
1305
|
+
/>,
|
|
1306
|
+
);
|
|
1307
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1308
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1309
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1310
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1311
|
+
spy.mockRestore();
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
it('VIBER preview: accountDetails JSON.parse throws falls back to empty object', async () => {
|
|
1315
|
+
const realParse = JSON.parse;
|
|
1316
|
+
let callCount = 0;
|
|
1317
|
+
const spy = jest.spyOn(JSON, 'parse').mockImplementation((...args) => {
|
|
1318
|
+
callCount += 1;
|
|
1319
|
+
// Let messageBody parse succeed, fail on accountDetails parse (second call)
|
|
1320
|
+
if (callCount === 2) throw new SyntaxError('bad acct json');
|
|
1321
|
+
return realParse.apply(JSON, args);
|
|
1322
|
+
});
|
|
1323
|
+
renderStep(
|
|
1324
|
+
<ChannelSelectionStep
|
|
1325
|
+
value={{
|
|
1326
|
+
contentItems: [{
|
|
1327
|
+
contentId: 'vib-acct-throws',
|
|
1328
|
+
channel: 'VIBER',
|
|
1329
|
+
templateData: {
|
|
1330
|
+
messageBody: JSON.stringify({ content: { text: 'ok' } }),
|
|
1331
|
+
accountDetails: 'bad-json',
|
|
1332
|
+
},
|
|
1333
|
+
}],
|
|
1334
|
+
}}
|
|
1335
|
+
onChange={jest.fn()}
|
|
1336
|
+
channels={CHANNELS}
|
|
1337
|
+
/>,
|
|
1338
|
+
);
|
|
1339
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1340
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1341
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1342
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1343
|
+
spy.mockRestore();
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
it('VIBER preview with no content wrapper uses parsedBody directly', async () => {
|
|
1347
|
+
renderStep(
|
|
1348
|
+
<ChannelSelectionStep
|
|
1349
|
+
value={{
|
|
1350
|
+
contentItems: [{
|
|
1351
|
+
contentId: 'vib-nokey',
|
|
1352
|
+
channel: 'VIBER',
|
|
1353
|
+
templateData: {
|
|
1354
|
+
messageBody: JSON.stringify({ text: 'Flat text', image: { url: 'http://img' } }),
|
|
1355
|
+
},
|
|
1356
|
+
}],
|
|
1357
|
+
}}
|
|
1358
|
+
onChange={jest.fn()}
|
|
1359
|
+
channels={CHANNELS}
|
|
1360
|
+
/>,
|
|
1361
|
+
);
|
|
1362
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1363
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1364
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1365
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
// ── extractContentForPreview — RCS ───────────────────────────────────────────
|
|
1369
|
+
|
|
1370
|
+
it('RCS preview with IMAGE media type renders without error', async () => {
|
|
1371
|
+
renderStep(
|
|
1372
|
+
<ChannelSelectionStep
|
|
1373
|
+
value={{
|
|
1374
|
+
contentItems: [{
|
|
1375
|
+
contentId: 'rcs-img',
|
|
1376
|
+
channel: 'RCS',
|
|
1377
|
+
templateData: {
|
|
1378
|
+
rcsContent: {
|
|
1379
|
+
cardContent: [{
|
|
1380
|
+
title: 'RCS title',
|
|
1381
|
+
description: 'RCS desc',
|
|
1382
|
+
media: { mediaType: 'IMAGE', mediaUrl: 'http://rcs-img.png' },
|
|
1383
|
+
suggestions: [{ text: 'Reply1' }],
|
|
1384
|
+
}],
|
|
1385
|
+
},
|
|
1386
|
+
},
|
|
1387
|
+
}],
|
|
1388
|
+
}}
|
|
1389
|
+
onChange={jest.fn()}
|
|
1390
|
+
channels={CHANNELS}
|
|
1391
|
+
/>,
|
|
1392
|
+
);
|
|
1393
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1394
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1395
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1396
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1397
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'RCS');
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
it('RCS preview with VIDEO media type includes rcsThumbnailSrc', async () => {
|
|
1401
|
+
renderStep(
|
|
1402
|
+
<ChannelSelectionStep
|
|
1403
|
+
value={{
|
|
1404
|
+
contentItems: [{
|
|
1405
|
+
contentId: 'rcs-vid',
|
|
1406
|
+
channel: 'RCS',
|
|
1407
|
+
templateData: {
|
|
1408
|
+
rcsContent: {
|
|
1409
|
+
cardContent: [{
|
|
1410
|
+
media: { mediaType: 'VIDEO', mediaUrl: 'http://vid.mp4', thumbnailUrl: 'http://thumb.jpg' },
|
|
1411
|
+
suggestions: [],
|
|
1412
|
+
}],
|
|
1413
|
+
},
|
|
1414
|
+
},
|
|
1415
|
+
}],
|
|
1416
|
+
}}
|
|
1417
|
+
onChange={jest.fn()}
|
|
1418
|
+
channels={CHANNELS}
|
|
1419
|
+
/>,
|
|
1420
|
+
);
|
|
1421
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1422
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1423
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1424
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
it('RCS preview with no rcsContent uses empty fallbacks', async () => {
|
|
1428
|
+
renderStep(
|
|
1429
|
+
<ChannelSelectionStep
|
|
1430
|
+
value={{
|
|
1431
|
+
contentItems: [{ contentId: 'rcs-empty', channel: 'RCS', templateData: {} }],
|
|
1432
|
+
}}
|
|
1433
|
+
onChange={jest.fn()}
|
|
1434
|
+
channels={CHANNELS}
|
|
1435
|
+
/>,
|
|
1436
|
+
);
|
|
1437
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1438
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1439
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1440
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
// ── extractContentForPreview — WEBPUSH ───────────────────────────────────────
|
|
1444
|
+
|
|
1445
|
+
it('WEBPUSH preview passes messageContent.content to slidebox', async () => {
|
|
1446
|
+
renderStep(
|
|
1447
|
+
<ChannelSelectionStep
|
|
1448
|
+
value={{
|
|
1449
|
+
contentItems: [{
|
|
1450
|
+
contentId: 'wp-1',
|
|
1451
|
+
channel: 'WEBPUSH',
|
|
1452
|
+
templateData: {
|
|
1453
|
+
messageContent: {
|
|
1454
|
+
content: { title: 'WP title', message: 'WP message', icon: 'http://icon.png' },
|
|
1455
|
+
},
|
|
1456
|
+
},
|
|
1457
|
+
}],
|
|
1458
|
+
}}
|
|
1459
|
+
onChange={jest.fn()}
|
|
1460
|
+
channels={CHANNELS}
|
|
1461
|
+
/>,
|
|
1462
|
+
);
|
|
1463
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1464
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1465
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1466
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1467
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'WEBPUSH');
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
it('WEBPUSH preview with missing messageContent uses empty object fallback', async () => {
|
|
1471
|
+
renderStep(
|
|
1472
|
+
<ChannelSelectionStep
|
|
1473
|
+
value={{
|
|
1474
|
+
contentItems: [{ contentId: 'wp-empty', channel: 'WEBPUSH', templateData: {} }],
|
|
1475
|
+
}}
|
|
1476
|
+
onChange={jest.fn()}
|
|
1477
|
+
channels={CHANNELS}
|
|
1478
|
+
/>,
|
|
1479
|
+
);
|
|
1480
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1481
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1482
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1483
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
// ── extractContentForPreview — ZALO ──────────────────────────────────────────
|
|
1487
|
+
|
|
1488
|
+
it('ZALO preview uses templateConfigs.template as templatePreviewUrl', async () => {
|
|
1489
|
+
renderStep(
|
|
1490
|
+
<ChannelSelectionStep
|
|
1491
|
+
value={{
|
|
1492
|
+
contentItems: [{
|
|
1493
|
+
contentId: 'zalo-1',
|
|
1494
|
+
channel: 'ZALO',
|
|
1495
|
+
templateData: {
|
|
1496
|
+
templateConfigs: { template: 'http://zalo-template.json' },
|
|
1497
|
+
templatePreviewUrl: 'http://fallback.json',
|
|
1498
|
+
},
|
|
1499
|
+
}],
|
|
1500
|
+
}}
|
|
1501
|
+
onChange={jest.fn()}
|
|
1502
|
+
channels={CHANNELS}
|
|
1503
|
+
/>,
|
|
1504
|
+
);
|
|
1505
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1506
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1507
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1508
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1509
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'ZALO');
|
|
1510
|
+
});
|
|
1511
|
+
|
|
1512
|
+
it('ZALO preview falls back to templatePreviewUrl when templateConfigs.template is absent', async () => {
|
|
1513
|
+
renderStep(
|
|
1514
|
+
<ChannelSelectionStep
|
|
1515
|
+
value={{
|
|
1516
|
+
contentItems: [{
|
|
1517
|
+
contentId: 'zalo-2',
|
|
1518
|
+
channel: 'ZALO',
|
|
1519
|
+
templateData: { templatePreviewUrl: 'http://preview.json' },
|
|
1520
|
+
}],
|
|
1521
|
+
}}
|
|
1522
|
+
onChange={jest.fn()}
|
|
1523
|
+
channels={CHANNELS}
|
|
1524
|
+
/>,
|
|
1525
|
+
);
|
|
1526
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1527
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1528
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1529
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
// ── extractContentForPreview — fallback (Facebook) ───────────────────────────
|
|
1533
|
+
|
|
1534
|
+
it('fallback: unmapped channel (not in any extractContentForPreview branch) passes templateData directly', async () => {
|
|
1535
|
+
// 'TWITTER' is not in PREVIEW_TEST_UNSUPPORTED_CHANNELS → "Preview and Test" shows.
|
|
1536
|
+
// 'TWITTER' also doesn't match any if-branch in extractContentForPreview → hits line 352 fallback.
|
|
1537
|
+
renderStep(
|
|
1538
|
+
<ChannelSelectionStep
|
|
1539
|
+
value={{
|
|
1540
|
+
contentItems: [{
|
|
1541
|
+
contentId: 'fallback-twitter',
|
|
1542
|
+
channel: 'TWITTER',
|
|
1543
|
+
templateData: { messageBody: 'Fallback content' },
|
|
1544
|
+
}],
|
|
1545
|
+
}}
|
|
1546
|
+
onChange={jest.fn()}
|
|
1547
|
+
channels={CHANNELS}
|
|
1548
|
+
/>,
|
|
1549
|
+
);
|
|
1550
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1551
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1552
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1553
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1554
|
+
});
|
|
1555
|
+
|
|
1556
|
+
// ── extractContentForPreview — SMS via messageBody ────────────────────────────
|
|
1557
|
+
|
|
1558
|
+
it('SMS preview uses messageBody when smsBody is absent', async () => {
|
|
1559
|
+
renderStep(
|
|
1560
|
+
<ChannelSelectionStep
|
|
1561
|
+
value={{
|
|
1562
|
+
contentItems: [{
|
|
1563
|
+
contentId: 'sms-mb',
|
|
1564
|
+
channel: 'SMS',
|
|
1565
|
+
templateData: { messageBody: 'From messageBody' },
|
|
1566
|
+
}],
|
|
1567
|
+
}}
|
|
1568
|
+
onChange={jest.fn()}
|
|
1569
|
+
channels={CHANNELS}
|
|
1570
|
+
/>,
|
|
1571
|
+
);
|
|
1572
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1573
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1574
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1575
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1576
|
+
expect(screen.getByTestId('test-and-preview-mock')).toHaveAttribute('data-channel', 'SMS');
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
// ── getCardType — INAPP and WECHAT return undefined ─────────────────────────
|
|
1580
|
+
|
|
1581
|
+
it('INAPP content card renders without crash (getCardType returns undefined for INAPP)', () => {
|
|
1582
|
+
renderStep(
|
|
1583
|
+
<ChannelSelectionStep
|
|
1584
|
+
value={{
|
|
1585
|
+
contentItems: [{
|
|
1586
|
+
contentId: 'inapp-card',
|
|
1587
|
+
channel: 'INAPP',
|
|
1588
|
+
templateData: { androidContent: { title: 'InApp Card Title' } },
|
|
1589
|
+
}],
|
|
1590
|
+
}}
|
|
1591
|
+
onChange={jest.fn()}
|
|
1592
|
+
channels={CHANNELS}
|
|
1593
|
+
/>,
|
|
1594
|
+
);
|
|
1595
|
+
expect(screen.getByLabelText('Show more content options icon')).toBeInTheDocument();
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
it('WECHAT content card renders without crash (getCardType returns undefined for WECHAT)', () => {
|
|
1599
|
+
renderStep(
|
|
1600
|
+
<ChannelSelectionStep
|
|
1601
|
+
value={{
|
|
1602
|
+
contentItems: [{
|
|
1603
|
+
contentId: 'wechat-card',
|
|
1604
|
+
channel: 'WECHAT',
|
|
1605
|
+
templateData: { messageBody: 'WeChat message' },
|
|
1606
|
+
}],
|
|
1607
|
+
}}
|
|
1608
|
+
onChange={jest.fn()}
|
|
1609
|
+
channels={CHANNELS}
|
|
1610
|
+
/>,
|
|
1611
|
+
);
|
|
1612
|
+
expect(screen.getByLabelText('Show more content options icon')).toBeInTheDocument();
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
// ── is_drag_drop → BEE editor ────────────────────────────────────────────────
|
|
1616
|
+
|
|
1617
|
+
it('edit content with is_drag_drop=1 passes BEE as editor to CreativesContainer', async () => {
|
|
1618
|
+
renderStep(
|
|
1619
|
+
<ChannelSelectionStep
|
|
1620
|
+
value={{
|
|
1621
|
+
contentItems: [{
|
|
1622
|
+
contentId: 'bee-item',
|
|
1623
|
+
channel: 'EMAIL',
|
|
1624
|
+
templateData: { emailSubject: 'BEE subject', is_drag_drop: 1 },
|
|
1625
|
+
}],
|
|
1626
|
+
}}
|
|
1627
|
+
onChange={jest.fn()}
|
|
1628
|
+
channels={CHANNELS}
|
|
1629
|
+
/>,
|
|
1630
|
+
);
|
|
1631
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1632
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1633
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));
|
|
1634
|
+
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1635
|
+
});
|
|
1636
|
+
|
|
1637
|
+
it('edit content with is_drag_drop=true also passes BEE editor', async () => {
|
|
1638
|
+
renderStep(
|
|
1639
|
+
<ChannelSelectionStep
|
|
1640
|
+
value={{
|
|
1641
|
+
contentItems: [{
|
|
1642
|
+
contentId: 'bee-bool',
|
|
1643
|
+
channel: 'EMAIL',
|
|
1644
|
+
templateData: { emailSubject: 'BEE bool', is_drag_drop: true },
|
|
1645
|
+
}],
|
|
1646
|
+
}}
|
|
1647
|
+
onChange={jest.fn()}
|
|
1648
|
+
channels={CHANNELS}
|
|
1649
|
+
/>,
|
|
1650
|
+
);
|
|
1651
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1652
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1653
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));
|
|
1654
|
+
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1657
|
+
// ── WEBPUSH edit: messageContent.content extraction ──────────────────────────
|
|
1658
|
+
|
|
1659
|
+
it('edit WEBPUSH item extracts messageContent.content for CreativesContainer templateData', async () => {
|
|
1660
|
+
renderStep(
|
|
1661
|
+
<ChannelSelectionStep
|
|
1662
|
+
value={{
|
|
1663
|
+
contentItems: [{
|
|
1664
|
+
contentId: 'wp-edit',
|
|
1665
|
+
// templateData has a `channel` field so the WEBPUSH branch in the edit IIFE fires
|
|
1666
|
+
channel: 'WEBPUSH',
|
|
1667
|
+
templateData: {
|
|
1668
|
+
channel: 'WEBPUSH',
|
|
1669
|
+
messageContent: {
|
|
1670
|
+
content: { title: 'WP Edit Title', message: 'WP Edit Msg' },
|
|
1671
|
+
},
|
|
1672
|
+
},
|
|
1673
|
+
}],
|
|
1674
|
+
}}
|
|
1675
|
+
onChange={jest.fn()}
|
|
1676
|
+
channels={CHANNELS}
|
|
1677
|
+
/>,
|
|
1678
|
+
);
|
|
1679
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1680
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1681
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Edit'));
|
|
1682
|
+
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1683
|
+
expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-mode', 'edit');
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
// ── FTP channel filtered from dropdown ───────────────────────────────────────
|
|
1687
|
+
|
|
1688
|
+
it('FTP channel is filtered out of the channel dropdown', async () => {
|
|
1689
|
+
renderStep(
|
|
1690
|
+
<ChannelSelectionStep
|
|
1691
|
+
value={{ contentItems: [] }}
|
|
1692
|
+
onChange={jest.fn()}
|
|
1693
|
+
channels={[
|
|
1694
|
+
{
|
|
1695
|
+
value: 'SMS', label: 'SMS', iconType: 'sms', isActive: true, paneKey: 'sms', channelProp: 'sms',
|
|
1696
|
+
},
|
|
1697
|
+
{
|
|
1698
|
+
value: 'ftp', label: 'FTP', iconType: 'sms', isActive: true, paneKey: 'ftp', channelProp: 'ftp',
|
|
1699
|
+
},
|
|
1700
|
+
]}
|
|
1701
|
+
/>,
|
|
1702
|
+
);
|
|
1703
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
1704
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1705
|
+
expect(within(screen.getByRole('menu')).getByText('SMS')).toBeInTheDocument();
|
|
1706
|
+
expect(within(screen.getByRole('menu')).queryByText('FTP')).not.toBeInTheDocument();
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
// ── getSenderDetailsDisplay returns null for other channels ──────────────────
|
|
1710
|
+
|
|
1711
|
+
it('WHATSAPP content card has no sender details row (getSenderDetailsDisplay returns null)', () => {
|
|
1712
|
+
renderStep(
|
|
1713
|
+
<ChannelSelectionStep
|
|
1714
|
+
value={{
|
|
1715
|
+
contentItems: [{
|
|
1716
|
+
contentId: 'wa-no-sender',
|
|
1717
|
+
channel: 'WHATSAPP',
|
|
1718
|
+
templateData: { messageBody: 'WA msg', templateConfigs: {} },
|
|
1719
|
+
}],
|
|
1720
|
+
}}
|
|
1721
|
+
onChange={jest.fn()}
|
|
1722
|
+
channels={CHANNELS}
|
|
1723
|
+
/>,
|
|
1724
|
+
);
|
|
1725
|
+
expect(screen.queryByText(/sender ID/i)).not.toBeInTheDocument();
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
// ── incentive coupons/points keys are skipped ────────────────────────────────
|
|
1729
|
+
|
|
1730
|
+
it('selecting a coupons incentive type from the menu is silently skipped', async () => {
|
|
1731
|
+
const onChange = jest.fn();
|
|
1732
|
+
renderStep(
|
|
1733
|
+
<ChannelSelectionStep
|
|
1734
|
+
value={{ contentItems: [] }}
|
|
1735
|
+
onChange={onChange}
|
|
1736
|
+
channels={CHANNELS}
|
|
1737
|
+
incentivesData={{
|
|
1738
|
+
enabled: true,
|
|
1739
|
+
types: [
|
|
1740
|
+
{ value: 'coupons', label: 'Coupons', isActive: true },
|
|
1741
|
+
{ value: 'badges', label: 'Badges', isActive: true },
|
|
1742
|
+
],
|
|
1743
|
+
}}
|
|
1744
|
+
/>,
|
|
1745
|
+
);
|
|
1746
|
+
// Only non-coupons/non-points items appear in the dropdown; selecting coupons is impossible
|
|
1747
|
+
// through the rendered dropdown since it's filtered. Verify badges still works.
|
|
1748
|
+
await userEvent.click(screen.getByRole('button', { name: /^add incentive$/i }));
|
|
1749
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1750
|
+
expect(within(screen.getByRole('menu')).queryByText('Coupons')).not.toBeInTheDocument();
|
|
1751
|
+
expect(within(screen.getByRole('menu')).getByText('Badges')).toBeInTheDocument();
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
it('incentive dropdown on card: selecting points type key is skipped (filtered from menu)', async () => {
|
|
1755
|
+
const onChange = jest.fn();
|
|
1756
|
+
renderStep(
|
|
1757
|
+
<ChannelSelectionStep
|
|
1758
|
+
value={{
|
|
1759
|
+
contentItems: [{ contentId: 'c1', channel: 'SMS', templateData: {} }],
|
|
1760
|
+
}}
|
|
1761
|
+
onChange={onChange}
|
|
1762
|
+
channels={CHANNELS}
|
|
1763
|
+
incentivesData={{
|
|
1764
|
+
enabled: true,
|
|
1765
|
+
types: [
|
|
1766
|
+
{ value: 'points', label: 'Points', isActive: true },
|
|
1767
|
+
{ value: 'promotions', label: 'Promotions', isActive: true },
|
|
1768
|
+
],
|
|
1769
|
+
}}
|
|
1770
|
+
/>,
|
|
1771
|
+
);
|
|
1772
|
+
const addIncentiveLinks = screen.getAllByRole('button', { name: /add incentive/i });
|
|
1773
|
+
await userEvent.click(addIncentiveLinks[addIncentiveLinks.length - 1]);
|
|
1774
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1775
|
+
expect(within(screen.getByRole('menu')).queryByText('Points')).not.toBeInTheDocument();
|
|
1776
|
+
expect(within(screen.getByRole('menu')).getByText('Promotions')).toBeInTheDocument();
|
|
1777
|
+
});
|
|
1778
|
+
|
|
1779
|
+
// ── Viber getContentPreview with object messageBody (non-string) ──────────────
|
|
1780
|
+
|
|
1781
|
+
it('Viber card preview text uses parsed content.text when messageBody is a non-string object', () => {
|
|
1782
|
+
renderStep(
|
|
1783
|
+
<ChannelSelectionStep
|
|
1784
|
+
value={{
|
|
1785
|
+
contentItems: [{
|
|
1786
|
+
contentId: 'vib-obj-preview',
|
|
1787
|
+
channel: 'VIBER',
|
|
1788
|
+
templateData: { messageBody: { content: { text: 'Obj viber text for preview' } } },
|
|
1789
|
+
}],
|
|
1790
|
+
}}
|
|
1791
|
+
onChange={jest.fn()}
|
|
1792
|
+
channels={CHANNELS}
|
|
1793
|
+
/>,
|
|
1794
|
+
);
|
|
1795
|
+
// getContentPreview for VIBER parses the object messageBody to extract content.text
|
|
1796
|
+
expect(screen.getByLabelText('Show more content options icon')).toBeInTheDocument();
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
// ── selectedChannelConfig derived from CHANNELS ───────────────────────────────
|
|
1800
|
+
|
|
1801
|
+
it('opening creatives for EMAIL derives correct channelProp from CHANNELS', async () => {
|
|
1802
|
+
const onChange = jest.fn();
|
|
1803
|
+
renderStep(
|
|
1804
|
+
<ChannelSelectionStep
|
|
1805
|
+
value={{ contentItems: [] }}
|
|
1806
|
+
onChange={onChange}
|
|
1807
|
+
channels={CHANNELS}
|
|
1808
|
+
/>,
|
|
1809
|
+
);
|
|
1810
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
1811
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1812
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Email'));
|
|
1813
|
+
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1814
|
+
expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-channel', 'email');
|
|
1815
|
+
});
|
|
1816
|
+
|
|
1817
|
+
it('opening creatives for MOBILEPUSH derives correct channelProp', async () => {
|
|
1818
|
+
const onChange = jest.fn();
|
|
1819
|
+
renderStep(
|
|
1820
|
+
<ChannelSelectionStep
|
|
1821
|
+
value={{ contentItems: [] }}
|
|
1822
|
+
onChange={onChange}
|
|
1823
|
+
channels={CHANNELS}
|
|
1824
|
+
/>,
|
|
1825
|
+
);
|
|
1826
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
1827
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1828
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Mobile push'));
|
|
1829
|
+
await waitFor(() => expect(screen.getByTestId('creatives-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1830
|
+
expect(screen.getByTestId('creatives-mock')).toHaveAttribute('data-creatives-channel', 'mobilepush');
|
|
1831
|
+
});
|
|
1832
|
+
|
|
1833
|
+
// ── messageMetaConfigId from templateId ──────────────────────────────────────
|
|
1834
|
+
|
|
1835
|
+
it('preview passes messageMetaConfigId from templateConfigs.templateId', async () => {
|
|
1836
|
+
renderStep(
|
|
1837
|
+
<ChannelSelectionStep
|
|
1838
|
+
value={{
|
|
1839
|
+
contentItems: [{
|
|
1840
|
+
contentId: 'sms-tid',
|
|
1841
|
+
channel: 'SMS',
|
|
1842
|
+
templateData: {
|
|
1843
|
+
smsBody: 'Body',
|
|
1844
|
+
templateConfigs: { templateId: 'tmpl-42' },
|
|
1845
|
+
},
|
|
1846
|
+
}],
|
|
1847
|
+
}}
|
|
1848
|
+
onChange={jest.fn()}
|
|
1849
|
+
channels={CHANNELS}
|
|
1850
|
+
/>,
|
|
1851
|
+
);
|
|
1852
|
+
await userEvent.click(screen.getByLabelText('Show more content options icon'));
|
|
1853
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1854
|
+
await userEvent.click(within(screen.getByRole('menu')).getByText('Preview and Test'));
|
|
1855
|
+
await waitFor(() => expect(screen.getByTestId('test-and-preview-mock')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1856
|
+
});
|
|
1857
|
+
|
|
1858
|
+
// ── handleChannelsVisibleChange / handleIncentivesVisibleChange ───────────────
|
|
1859
|
+
|
|
1860
|
+
it('closing the channel dropdown via onVisibleChange(false) hides the overlay', async () => {
|
|
1861
|
+
renderStep(
|
|
1862
|
+
<ChannelSelectionStep
|
|
1863
|
+
value={{ contentItems: [] }}
|
|
1864
|
+
onChange={jest.fn()}
|
|
1865
|
+
channels={CHANNELS}
|
|
1866
|
+
/>,
|
|
1867
|
+
);
|
|
1868
|
+
await userEvent.click(screen.getByRole('button', { name: /add creative/i }));
|
|
1869
|
+
await waitFor(() => expect(screen.getByRole('menu')).toBeInTheDocument(), WAIT_OPTIONS);
|
|
1870
|
+
// Click outside to close
|
|
1871
|
+
await userEvent.keyboard('{Escape}');
|
|
1872
|
+
// After close the menu should not be visible (or at least no crash)
|
|
1873
|
+
expect(screen.getByRole('button', { name: /add creative/i })).toBeInTheDocument();
|
|
1874
|
+
});
|
|
1875
|
+
|
|
1876
|
+
// ── availableIncentiveTypes null/empty guard ─────────────────────────────────
|
|
1877
|
+
|
|
1878
|
+
it('incentivesData with null types does not crash and shows no incentive button', () => {
|
|
1879
|
+
renderStep(
|
|
1880
|
+
<ChannelSelectionStep
|
|
1881
|
+
value={{ contentItems: [] }}
|
|
1882
|
+
onChange={jest.fn()}
|
|
1883
|
+
channels={CHANNELS}
|
|
1884
|
+
incentivesData={{ enabled: true, types: null }}
|
|
1885
|
+
/>,
|
|
1886
|
+
);
|
|
1887
|
+
expect(screen.queryByRole('button', { name: /add incentive/i })).not.toBeInTheDocument();
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1890
|
+
it('incentivesData with empty types array returns null from renderIncentiveDropdownOverlay', () => {
|
|
1891
|
+
// Exercises line 471: availableIncentiveTypes?.length === 0 → return null
|
|
1892
|
+
renderStep(
|
|
1893
|
+
<ChannelSelectionStep
|
|
1894
|
+
value={{ contentItems: [] }}
|
|
1895
|
+
onChange={jest.fn()}
|
|
1896
|
+
channels={CHANNELS}
|
|
1897
|
+
incentivesData={{ enabled: true, types: [] }}
|
|
1898
|
+
/>,
|
|
1899
|
+
);
|
|
1900
|
+
expect(screen.queryByRole('button', { name: /add incentive/i })).not.toBeInTheDocument();
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
// ── config.context.ouId passed to TestAndPreview ──────────────────────────────
|
|
1904
|
+
|
|
1905
|
+
it('config prop is accepted without crash', () => {
|
|
1906
|
+
renderStep(
|
|
1907
|
+
<ChannelSelectionStep
|
|
1908
|
+
value={{ contentItems: [] }}
|
|
1909
|
+
onChange={jest.fn()}
|
|
1910
|
+
channels={CHANNELS}
|
|
1911
|
+
config={{ context: { ouId: 'ou-123' } }}
|
|
1912
|
+
/>,
|
|
1913
|
+
);
|
|
1914
|
+
expect(screen.getByRole('button', { name: /add creative/i })).toBeInTheDocument();
|
|
1915
|
+
});
|
|
804
1916
|
});
|