@capillarytech/creatives-library 8.0.324 → 8.0.325-alpha.1
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/utils/tests/tagValidations.test.js +0 -34
- package/v2Components/CapTagList/index.js +22 -14
- package/v2Components/CapTagList/style.scss +0 -48
- package/v2Components/CapTagListWithInput/index.js +0 -4
- package/v2Components/CapWhatsappCTA/index.js +0 -2
- package/v2Components/CommonTestAndPreview/_commonTestAndPreview.scss +1 -2
- package/v2Components/FormBuilder/index.js +0 -7
- package/v2Components/HtmlEditor/HTMLEditor.js +1 -6
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.apiErrors.test.js +0 -1
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +2 -927
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +0 -3
- package/v2Containers/BeeEditor/index.js +0 -3
- package/v2Containers/CreativesContainer/SlideBoxContent.js +1 -28
- package/v2Containers/CreativesContainer/index.js +0 -3
- package/v2Containers/Email/index.js +0 -1
- package/v2Containers/EmailWrapper/components/EmailHTMLEditor.js +1 -7
- package/v2Containers/EmailWrapper/components/EmailWrapperView.js +0 -3
- package/v2Containers/EmailWrapper/components/__tests__/EmailHTMLEditor.test.js +2 -20
- package/v2Containers/EmailWrapper/components/__tests__/EmailWrapperView.test.js +1 -16
- package/v2Containers/EmailWrapper/hooks/useEmailWrapper.js +0 -3
- package/v2Containers/EmailWrapper/index.js +0 -4
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.edgeCases.test.js +0 -1
- package/v2Containers/EmailWrapper/tests/useEmailWrapper.test.js +0 -9
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +0 -19
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +0 -3
- package/v2Containers/InAppWrapper/index.js +0 -3
- package/v2Containers/MobilePush/Create/index.js +0 -2
- package/v2Containers/MobilePush/Edit/index.js +0 -2
- package/v2Containers/MobilepushWrapper/index.js +1 -3
- package/v2Containers/Rcs/index.js +0 -1
- package/v2Containers/Sms/Create/index.js +0 -2
- package/v2Containers/Sms/Edit/index.js +0 -2
- package/v2Containers/SmsTrai/Edit/index.js +0 -2
- package/v2Containers/SmsWrapper/index.js +0 -2
- package/v2Containers/TagList/index.js +2 -41
- package/v2Containers/TagList/messages.js +0 -4
- package/v2Containers/TagList/tests/TagList.test.js +20 -122
- package/v2Containers/TagList/tests/mockdata.js +0 -17
- package/v2Containers/Viber/index.js +0 -5
- package/v2Containers/WebPush/Create/hooks/useTagManagement.js +2 -0
- package/v2Containers/WebPush/Create/index.js +1 -9
- package/v2Containers/Whatsapp/index.js +0 -5
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +0 -20
- package/v2Containers/Zalo/index.js +0 -2
- package/v2Components/CapTagListWithInput/__tests__/CapTagListWithInput.test.js +0 -63
|
@@ -11,8 +11,6 @@ import {
|
|
|
11
11
|
import '@testing-library/jest-dom';
|
|
12
12
|
import { IntlProvider } from 'react-intl';
|
|
13
13
|
import HTMLEditor from '../HTMLEditor';
|
|
14
|
-
import { VALIDATION_SEVERITY } from '../constants';
|
|
15
|
-
import { ISSUE_SOURCES } from '../utils/validationConstants';
|
|
16
14
|
|
|
17
15
|
// Options to control CodeEditorPane mock behavior
|
|
18
16
|
const mockCodeEditorOptions = {
|
|
@@ -21,7 +19,6 @@ const mockCodeEditorOptions = {
|
|
|
21
19
|
setRef: true,
|
|
22
20
|
navigateToLineThrows: false,
|
|
23
21
|
includeNavigateToLine: true,
|
|
24
|
-
omitGetCursor: false,
|
|
25
22
|
};
|
|
26
23
|
|
|
27
24
|
|
|
@@ -169,14 +166,11 @@ jest.mock('../components/CodeEditorPane', () => {
|
|
|
169
166
|
|
|
170
167
|
const methods = {
|
|
171
168
|
focus: jest.fn(),
|
|
169
|
+
getCursor: jest.fn(() => 0),
|
|
172
170
|
getValue: jest.fn(() => value),
|
|
173
171
|
setValue: jest.fn((newValue) => setValue(newValue)),
|
|
174
172
|
};
|
|
175
173
|
|
|
176
|
-
if (!mockCodeEditorOptions.omitGetCursor) {
|
|
177
|
-
methods.getCursor = jest.fn(() => 0);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
174
|
if (mockCodeEditorOptions.includeNavigateToLine) {
|
|
181
175
|
methods.navigateToLine = jest.fn(() => {
|
|
182
176
|
if (mockCodeEditorOptions.navigateToLineThrows) {
|
|
@@ -205,20 +199,7 @@ jest.mock('../components/CodeEditorPane', () => {
|
|
|
205
199
|
}
|
|
206
200
|
|
|
207
201
|
return (
|
|
208
|
-
<div
|
|
209
|
-
data-testid="code-editor-pane"
|
|
210
|
-
data-tags={JSON.stringify(props.tags ?? null)}
|
|
211
|
-
data-injected-tags={JSON.stringify(props.injectedTags ?? null)}
|
|
212
|
-
data-location={JSON.stringify(props.location ?? null)}
|
|
213
|
-
data-event-context-tags={JSON.stringify(props.eventContextTags ?? null)}
|
|
214
|
-
data-wait-event-context-tags={JSON.stringify(props.waitEventContextTags ?? null)}
|
|
215
|
-
data-selected-offer-details={JSON.stringify(props.selectedOfferDetails ?? null)}
|
|
216
|
-
data-channel={JSON.stringify(props.channel ?? null)}
|
|
217
|
-
data-user-locale={props.userLocale ?? ''}
|
|
218
|
-
data-module-filter-enabled={String(!!props.moduleFilterEnabled)}
|
|
219
|
-
data-read-only={String(!!props.readOnly)}
|
|
220
|
-
data-is-fullscreen-mode={String(!!props.isFullscreenMode)}
|
|
221
|
-
>
|
|
202
|
+
<div data-testid="code-editor-pane">
|
|
222
203
|
<textarea
|
|
223
204
|
value={value}
|
|
224
205
|
onChange={(e) => {
|
|
@@ -235,18 +216,6 @@ jest.mock('../components/CodeEditorPane', () => {
|
|
|
235
216
|
>
|
|
236
217
|
Trigger Context Change
|
|
237
218
|
</button>
|
|
238
|
-
<button
|
|
239
|
-
onClick={() => props.onContextChange && props.onContextChange('ALL')}
|
|
240
|
-
data-testid="trigger-context-all"
|
|
241
|
-
>
|
|
242
|
-
Trigger Context ALL
|
|
243
|
-
</button>
|
|
244
|
-
<button
|
|
245
|
-
onClick={() => props.onContextChange && props.onContextChange(null)}
|
|
246
|
-
data-testid="trigger-context-null"
|
|
247
|
-
>
|
|
248
|
-
Trigger Context Null
|
|
249
|
-
</button>
|
|
250
219
|
{validation && (
|
|
251
220
|
<ValidationErrorDisplay
|
|
252
221
|
validation={validation}
|
|
@@ -280,9 +249,6 @@ jest.mock('../components/ValidationErrorDisplay', () => function MockValidationE
|
|
|
280
249
|
<button onClick={() => onErrorClick && onErrorClick({ line: null })}>
|
|
281
250
|
Error without Line
|
|
282
251
|
</button>
|
|
283
|
-
<button onClick={() => onErrorClick && onErrorClick(undefined)}>
|
|
284
|
-
Error undefined
|
|
285
|
-
</button>
|
|
286
252
|
</div>
|
|
287
253
|
);
|
|
288
254
|
});
|
|
@@ -418,195 +384,6 @@ describe('HTMLEditor', () => {
|
|
|
418
384
|
});
|
|
419
385
|
});
|
|
420
386
|
|
|
421
|
-
describe('waitEventContextTags', () => {
|
|
422
|
-
const waitTags = {
|
|
423
|
-
blockA: {
|
|
424
|
-
eventName: 'Order Placed',
|
|
425
|
-
blockName: 'Wait',
|
|
426
|
-
tags: [{ tagName: 'wait.foo', label: 'Foo', profileId: 'p1', profileName: 'P1' }],
|
|
427
|
-
},
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
it('passes default empty object to CodeEditorPane when waitEventContextTags is omitted', () => {
|
|
431
|
-
render(
|
|
432
|
-
<TestWrapper>
|
|
433
|
-
<HTMLEditor {...defaultProps} />
|
|
434
|
-
</TestWrapper>
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
act(() => {
|
|
438
|
-
jest.runAllTimers();
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
const pane = screen.getByTestId('code-editor-pane');
|
|
442
|
-
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify({}));
|
|
443
|
-
expect(pane).toHaveAttribute('data-is-fullscreen-mode', 'false');
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
it('forwards custom waitEventContextTags to the main CodeEditorPane', () => {
|
|
447
|
-
render(
|
|
448
|
-
<TestWrapper>
|
|
449
|
-
<HTMLEditor {...defaultProps} waitEventContextTags={waitTags} />
|
|
450
|
-
</TestWrapper>
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
act(() => {
|
|
454
|
-
jest.runAllTimers();
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
const pane = screen.getByTestId('code-editor-pane');
|
|
458
|
-
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify(waitTags));
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
it('forwards the same waitEventContextTags to fullscreen CodeEditorPane when modal opens', () => {
|
|
462
|
-
render(
|
|
463
|
-
<TestWrapper>
|
|
464
|
-
<HTMLEditor {...defaultProps} waitEventContextTags={waitTags} />
|
|
465
|
-
</TestWrapper>
|
|
466
|
-
);
|
|
467
|
-
|
|
468
|
-
act(() => {
|
|
469
|
-
jest.runAllTimers();
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
fireEvent.click(screen.getByText('Toggle Fullscreen'));
|
|
473
|
-
|
|
474
|
-
act(() => {
|
|
475
|
-
jest.runAllTimers();
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
const panes = screen.getAllByTestId('code-editor-pane');
|
|
479
|
-
expect(panes.length).toBeGreaterThanOrEqual(2);
|
|
480
|
-
const expected = JSON.stringify(waitTags);
|
|
481
|
-
panes.forEach((pane) => {
|
|
482
|
-
expect(pane).toHaveAttribute('data-wait-event-context-tags', expected);
|
|
483
|
-
});
|
|
484
|
-
const mainPane = panes.find((el) => el.getAttribute('data-is-fullscreen-mode') === 'false');
|
|
485
|
-
const fsPane = panes.find((el) => el.getAttribute('data-is-fullscreen-mode') === 'true');
|
|
486
|
-
expect(mainPane).toBeTruthy();
|
|
487
|
-
expect(fsPane).toBeTruthy();
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it('forwards waitEventContextTags for inapp variant to main and fullscreen CodeEditorPane', () => {
|
|
491
|
-
render(
|
|
492
|
-
<TestWrapper>
|
|
493
|
-
<HTMLEditor {...defaultProps} variant="inapp" waitEventContextTags={waitTags} />
|
|
494
|
-
</TestWrapper>
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
act(() => {
|
|
498
|
-
jest.runAllTimers();
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
expect(screen.getByTestId('code-editor-pane')).toHaveAttribute(
|
|
502
|
-
'data-wait-event-context-tags',
|
|
503
|
-
JSON.stringify(waitTags),
|
|
504
|
-
);
|
|
505
|
-
|
|
506
|
-
fireEvent.click(screen.getByText('Toggle Fullscreen'));
|
|
507
|
-
|
|
508
|
-
act(() => {
|
|
509
|
-
jest.runAllTimers();
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
const panes = screen.getAllByTestId('code-editor-pane');
|
|
513
|
-
const expected = JSON.stringify(waitTags);
|
|
514
|
-
panes.forEach((pane) => {
|
|
515
|
-
expect(pane).toHaveAttribute('data-wait-event-context-tags', expected);
|
|
516
|
-
});
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
it('passes explicit null waitEventContextTags through to CodeEditorPane', () => {
|
|
520
|
-
render(
|
|
521
|
-
<TestWrapper>
|
|
522
|
-
<HTMLEditor {...defaultProps} waitEventContextTags={null} />
|
|
523
|
-
</TestWrapper>
|
|
524
|
-
);
|
|
525
|
-
|
|
526
|
-
act(() => {
|
|
527
|
-
jest.runAllTimers();
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
expect(screen.getByTestId('code-editor-pane')).toHaveAttribute(
|
|
531
|
-
'data-wait-event-context-tags',
|
|
532
|
-
JSON.stringify(null),
|
|
533
|
-
);
|
|
534
|
-
});
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Covers forwardRef destructuring / defaults for tag-related props (HTMLEditor.js ~93–101)
|
|
539
|
-
* merged with HTMLEditor.defaultProps and passed through to CodeEditorPane (~663–671).
|
|
540
|
-
*/
|
|
541
|
-
describe('forwardRef tag-related props (HTMLEditor.js lines 93–101 → CodeEditorPane)', () => {
|
|
542
|
-
it('passes merged default tag-related props to CodeEditorPane when omitted on the instance', () => {
|
|
543
|
-
render(
|
|
544
|
-
<TestWrapper>
|
|
545
|
-
<HTMLEditor {...defaultProps} />
|
|
546
|
-
</TestWrapper>
|
|
547
|
-
);
|
|
548
|
-
|
|
549
|
-
act(() => {
|
|
550
|
-
jest.runAllTimers();
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
const pane = screen.getByTestId('code-editor-pane');
|
|
554
|
-
expect(pane).toHaveAttribute('data-tags', JSON.stringify([]));
|
|
555
|
-
expect(pane).toHaveAttribute('data-injected-tags', JSON.stringify({}));
|
|
556
|
-
expect(pane).toHaveAttribute('data-location', JSON.stringify(null));
|
|
557
|
-
expect(pane).toHaveAttribute('data-event-context-tags', JSON.stringify([]));
|
|
558
|
-
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify({}));
|
|
559
|
-
expect(pane).toHaveAttribute('data-selected-offer-details', JSON.stringify([]));
|
|
560
|
-
expect(pane).toHaveAttribute('data-channel', JSON.stringify(null));
|
|
561
|
-
expect(pane).toHaveAttribute('data-user-locale', 'en');
|
|
562
|
-
expect(pane).toHaveAttribute('data-module-filter-enabled', 'true');
|
|
563
|
-
expect(pane).toHaveAttribute('data-read-only', 'false');
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
it('forwards explicit tag-related props from the forwardRef parameter list to CodeEditorPane', () => {
|
|
567
|
-
const tags = [{ id: 't1' }];
|
|
568
|
-
const injectedTags = { custom: { name: 'Custom' } };
|
|
569
|
-
const location = { query: { module: 'outbound' } };
|
|
570
|
-
const eventContextTags = [{ tagName: 'entryTrigger.x' }];
|
|
571
|
-
const waitEventContextTags = { block1: { eventName: 'E', blockName: 'B', tags: [] } };
|
|
572
|
-
const selectedOfferDetails = [{ type: 'coupon' }];
|
|
573
|
-
|
|
574
|
-
render(
|
|
575
|
-
<TestWrapper>
|
|
576
|
-
<HTMLEditor
|
|
577
|
-
{...defaultProps}
|
|
578
|
-
tags={tags}
|
|
579
|
-
injectedTags={injectedTags}
|
|
580
|
-
location={location}
|
|
581
|
-
eventContextTags={eventContextTags}
|
|
582
|
-
waitEventContextTags={waitEventContextTags}
|
|
583
|
-
selectedOfferDetails={selectedOfferDetails}
|
|
584
|
-
channel="SMS"
|
|
585
|
-
userLocale="ja"
|
|
586
|
-
moduleFilterEnabled={false}
|
|
587
|
-
readOnly
|
|
588
|
-
/>
|
|
589
|
-
</TestWrapper>
|
|
590
|
-
);
|
|
591
|
-
|
|
592
|
-
act(() => {
|
|
593
|
-
jest.runAllTimers();
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
const pane = screen.getByTestId('code-editor-pane');
|
|
597
|
-
expect(pane).toHaveAttribute('data-tags', JSON.stringify(tags));
|
|
598
|
-
expect(pane).toHaveAttribute('data-injected-tags', JSON.stringify(injectedTags));
|
|
599
|
-
expect(pane).toHaveAttribute('data-location', JSON.stringify(location));
|
|
600
|
-
expect(pane).toHaveAttribute('data-event-context-tags', JSON.stringify(eventContextTags));
|
|
601
|
-
expect(pane).toHaveAttribute('data-wait-event-context-tags', JSON.stringify(waitEventContextTags));
|
|
602
|
-
expect(pane).toHaveAttribute('data-selected-offer-details', JSON.stringify(selectedOfferDetails));
|
|
603
|
-
expect(pane).toHaveAttribute('data-channel', JSON.stringify('SMS'));
|
|
604
|
-
expect(pane).toHaveAttribute('data-user-locale', 'ja');
|
|
605
|
-
expect(pane).toHaveAttribute('data-module-filter-enabled', 'false');
|
|
606
|
-
expect(pane).toHaveAttribute('data-read-only', 'true');
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
|
|
610
387
|
describe('Context Provider', () => {
|
|
611
388
|
it('provides correct context value for email variant', () => {
|
|
612
389
|
render(
|
|
@@ -2702,7 +2479,6 @@ describe('HTMLEditor', () => {
|
|
|
2702
2479
|
mockCodeEditorOptions.setRef = true;
|
|
2703
2480
|
mockCodeEditorOptions.navigateToLineThrows = false;
|
|
2704
2481
|
mockCodeEditorOptions.includeNavigateToLine = true;
|
|
2705
|
-
mockCodeEditorOptions.omitGetCursor = false;
|
|
2706
2482
|
|
|
2707
2483
|
// Clear specific mocks instead of all to avoid breaking other mocks
|
|
2708
2484
|
CapNotification.warning.mockClear();
|
|
@@ -3691,7 +3467,6 @@ describe('HTMLEditor', () => {
|
|
|
3691
3467
|
tags={null}
|
|
3692
3468
|
injectedTags={null}
|
|
3693
3469
|
eventContextTags={null}
|
|
3694
|
-
waitEventContextTags={null}
|
|
3695
3470
|
selectedOfferDetails={null}
|
|
3696
3471
|
onTagContextChange={null}
|
|
3697
3472
|
onTagSelect={null}
|
|
@@ -3717,7 +3492,6 @@ describe('HTMLEditor', () => {
|
|
|
3717
3492
|
<HTMLEditor
|
|
3718
3493
|
tags={[]}
|
|
3719
3494
|
eventContextTags={[]}
|
|
3720
|
-
waitEventContextTags={{}}
|
|
3721
3495
|
selectedOfferDetails={[]}
|
|
3722
3496
|
/>
|
|
3723
3497
|
</TestWrapper>
|
|
@@ -3767,703 +3541,4 @@ describe('HTMLEditor', () => {
|
|
|
3767
3541
|
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
3768
3542
|
});
|
|
3769
3543
|
});
|
|
3770
|
-
|
|
3771
|
-
/**
|
|
3772
|
-
* Covers remaining HTMLEditor.js branches for 100% file coverage:
|
|
3773
|
-
* useImperativeHandle getters (302–304), validation effects (350, 416, 440–441), onErrorAcknowledged (543).
|
|
3774
|
-
*/
|
|
3775
|
-
describe('HTMLEditor full file coverage', () => {
|
|
3776
|
-
beforeEach(() => {
|
|
3777
|
-
mockCodeEditorOptions.omitGetCursor = false;
|
|
3778
|
-
mockCodeEditorOptions.setRef = true;
|
|
3779
|
-
mockCodeEditorOptions.includeInsertText = true;
|
|
3780
|
-
mockCodeEditorOptions.insertTextThrows = false;
|
|
3781
|
-
|
|
3782
|
-
mockUseValidationImpl.mockReset();
|
|
3783
|
-
mockUseValidationImpl.mockImplementation(() => ({
|
|
3784
|
-
isValidating: false,
|
|
3785
|
-
getAllIssues: () => [],
|
|
3786
|
-
isClean: () => true,
|
|
3787
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3788
|
-
}));
|
|
3789
|
-
|
|
3790
|
-
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
3791
|
-
content: '<p>Test content</p>',
|
|
3792
|
-
updateContent: jest.fn(),
|
|
3793
|
-
saveContent: jest.fn(),
|
|
3794
|
-
markAsSaved: jest.fn(),
|
|
3795
|
-
isLoading: false,
|
|
3796
|
-
isDirty: false,
|
|
3797
|
-
hasContent: true,
|
|
3798
|
-
getContentSize: jest.fn(() => 20),
|
|
3799
|
-
});
|
|
3800
|
-
|
|
3801
|
-
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
3802
|
-
content: '<p>Android content</p>',
|
|
3803
|
-
deviceContent: {
|
|
3804
|
-
android: '<p>Android content</p>',
|
|
3805
|
-
ios: '<p>iOS content</p>',
|
|
3806
|
-
},
|
|
3807
|
-
activeDevice: 'android',
|
|
3808
|
-
keepContentSame: false,
|
|
3809
|
-
updateContent: jest.fn(),
|
|
3810
|
-
saveContent: jest.fn(),
|
|
3811
|
-
markAsSaved: jest.fn(),
|
|
3812
|
-
switchDevice: jest.fn(),
|
|
3813
|
-
toggleContentSync: jest.fn(),
|
|
3814
|
-
getDeviceContent: (device) => `<p>${device} content</p>`,
|
|
3815
|
-
setDeviceContent: jest.fn(),
|
|
3816
|
-
getContentSize: () => 20,
|
|
3817
|
-
isLoading: false,
|
|
3818
|
-
isDirty: false,
|
|
3819
|
-
hasContent: true,
|
|
3820
|
-
});
|
|
3821
|
-
});
|
|
3822
|
-
|
|
3823
|
-
it('ref exposes getValidation, getContent, and isContentEmpty (lines 302–304)', async () => {
|
|
3824
|
-
const ref = React.createRef();
|
|
3825
|
-
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
3826
|
-
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
3827
|
-
const { intl } = intlProvider.getChildContext();
|
|
3828
|
-
|
|
3829
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3830
|
-
isValidating: false,
|
|
3831
|
-
getAllIssues: () => [],
|
|
3832
|
-
isClean: () => true,
|
|
3833
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3834
|
-
});
|
|
3835
|
-
|
|
3836
|
-
render(
|
|
3837
|
-
<TestWrapper>
|
|
3838
|
-
<WrappedHTMLEditor
|
|
3839
|
-
{...defaultProps}
|
|
3840
|
-
intl={intl}
|
|
3841
|
-
ref={ref}
|
|
3842
|
-
initialContent="<p>hi</p>"
|
|
3843
|
-
/>
|
|
3844
|
-
</TestWrapper>
|
|
3845
|
-
);
|
|
3846
|
-
|
|
3847
|
-
act(() => {
|
|
3848
|
-
jest.runAllTimers();
|
|
3849
|
-
});
|
|
3850
|
-
|
|
3851
|
-
await waitFor(() => {
|
|
3852
|
-
expect(ref.current).toBeTruthy();
|
|
3853
|
-
});
|
|
3854
|
-
|
|
3855
|
-
expect(ref.current.getValidation()).toBeDefined();
|
|
3856
|
-
expect(ref.current.getContent()).toBe('<p>Test content</p>');
|
|
3857
|
-
expect(ref.current.isContentEmpty()).toBe(false);
|
|
3858
|
-
});
|
|
3859
|
-
|
|
3860
|
-
it('skips main validation notify effect while isValidating is true (line 350)', () => {
|
|
3861
|
-
const onValidationChange = jest.fn();
|
|
3862
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3863
|
-
isValidating: true,
|
|
3864
|
-
hasBlockingErrors: false,
|
|
3865
|
-
getAllIssues: () => [],
|
|
3866
|
-
isClean: () => true,
|
|
3867
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3868
|
-
});
|
|
3869
|
-
|
|
3870
|
-
render(
|
|
3871
|
-
<TestWrapper>
|
|
3872
|
-
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
3873
|
-
</TestWrapper>
|
|
3874
|
-
);
|
|
3875
|
-
|
|
3876
|
-
act(() => {
|
|
3877
|
-
jest.runAllTimers();
|
|
3878
|
-
});
|
|
3879
|
-
|
|
3880
|
-
expect(onValidationChange).toHaveBeenCalled();
|
|
3881
|
-
const withComplete = onValidationChange.mock.calls.filter((c) => c[0]?.validationComplete === true);
|
|
3882
|
-
expect(withComplete.length).toBe(0);
|
|
3883
|
-
});
|
|
3884
|
-
|
|
3885
|
-
it('skips api validation errors effect while validation is still running (line 416)', () => {
|
|
3886
|
-
const onValidationChange = jest.fn();
|
|
3887
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3888
|
-
isValidating: true,
|
|
3889
|
-
hasBlockingErrors: false,
|
|
3890
|
-
getAllIssues: () => [],
|
|
3891
|
-
isClean: () => true,
|
|
3892
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3893
|
-
});
|
|
3894
|
-
|
|
3895
|
-
render(
|
|
3896
|
-
<TestWrapper>
|
|
3897
|
-
<HTMLEditor
|
|
3898
|
-
{...defaultProps}
|
|
3899
|
-
onValidationChange={onValidationChange}
|
|
3900
|
-
apiValidationErrors={{ liquidErrors: ['x'], standardErrors: [] }}
|
|
3901
|
-
/>
|
|
3902
|
-
</TestWrapper>
|
|
3903
|
-
);
|
|
3904
|
-
|
|
3905
|
-
act(() => {
|
|
3906
|
-
jest.runAllTimers();
|
|
3907
|
-
});
|
|
3908
|
-
|
|
3909
|
-
onValidationChange.mockClear();
|
|
3910
|
-
|
|
3911
|
-
act(() => {
|
|
3912
|
-
jest.runAllTimers();
|
|
3913
|
-
});
|
|
3914
|
-
|
|
3915
|
-
const afterClear = onValidationChange.mock.calls.filter((c) => c[0]?.validationComplete === true);
|
|
3916
|
-
expect(afterClear.length).toBe(0);
|
|
3917
|
-
});
|
|
3918
|
-
|
|
3919
|
-
it('api validation errors effect notifies parent when issue counts change (lines 440–441)', () => {
|
|
3920
|
-
const onValidationChange = jest.fn();
|
|
3921
|
-
let issues = [];
|
|
3922
|
-
|
|
3923
|
-
// Keep summary primitives stable so the main validation effect (line 343) does not re-run
|
|
3924
|
-
// when only getAllIssues() starts returning API-style issues; the api-only effect (409)
|
|
3925
|
-
// still runs because `validation` reference changes each render.
|
|
3926
|
-
mockUseValidationImpl.mockImplementation(() => ({
|
|
3927
|
-
isValidating: false,
|
|
3928
|
-
hasBlockingErrors: false,
|
|
3929
|
-
getAllIssues: () => issues,
|
|
3930
|
-
isClean: () => issues.length === 0,
|
|
3931
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
3932
|
-
lastValidated: { getTime: () => 0 },
|
|
3933
|
-
}));
|
|
3934
|
-
|
|
3935
|
-
const { rerender } = render(
|
|
3936
|
-
<TestWrapper>
|
|
3937
|
-
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
3938
|
-
</TestWrapper>
|
|
3939
|
-
);
|
|
3940
|
-
|
|
3941
|
-
act(() => {
|
|
3942
|
-
jest.runAllTimers();
|
|
3943
|
-
});
|
|
3944
|
-
|
|
3945
|
-
onValidationChange.mockClear();
|
|
3946
|
-
|
|
3947
|
-
issues = [{ rule: 'liquid-api-validation', message: 'API error', source: 'liquid' }];
|
|
3948
|
-
|
|
3949
|
-
rerender(
|
|
3950
|
-
<TestWrapper>
|
|
3951
|
-
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
3952
|
-
</TestWrapper>
|
|
3953
|
-
);
|
|
3954
|
-
|
|
3955
|
-
act(() => {
|
|
3956
|
-
jest.runAllTimers();
|
|
3957
|
-
});
|
|
3958
|
-
|
|
3959
|
-
expect(onValidationChange).toHaveBeenCalled();
|
|
3960
|
-
const lastCall = onValidationChange.mock.calls[onValidationChange.mock.calls.length - 1][0];
|
|
3961
|
-
expect(lastCall.validationComplete).toBe(true);
|
|
3962
|
-
expect(lastCall.issueCounts.errors).toBeGreaterThanOrEqual(1);
|
|
3963
|
-
});
|
|
3964
|
-
|
|
3965
|
-
it('calls onErrorAcknowledged when user clicks a validation error (lines 542–543)', () => {
|
|
3966
|
-
const onErrorAcknowledged = jest.fn();
|
|
3967
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3968
|
-
isValidating: false,
|
|
3969
|
-
getAllIssues: () => [{ source: 'htmlhint', rule: 'rule1', message: 'E' }],
|
|
3970
|
-
isClean: () => false,
|
|
3971
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
3972
|
-
});
|
|
3973
|
-
|
|
3974
|
-
render(
|
|
3975
|
-
<TestWrapper>
|
|
3976
|
-
<HTMLEditor {...defaultProps} onErrorAcknowledged={onErrorAcknowledged} />
|
|
3977
|
-
</TestWrapper>
|
|
3978
|
-
);
|
|
3979
|
-
|
|
3980
|
-
act(() => {
|
|
3981
|
-
jest.runAllTimers();
|
|
3982
|
-
});
|
|
3983
|
-
|
|
3984
|
-
fireEvent.click(screen.getByText('Error without Line'));
|
|
3985
|
-
expect(onErrorAcknowledged).toHaveBeenCalled();
|
|
3986
|
-
});
|
|
3987
|
-
|
|
3988
|
-
it('getIssueCounts covers isBlockingError branches (standard API, liquid error, sanitizer, warning)', async () => {
|
|
3989
|
-
const ref = React.createRef();
|
|
3990
|
-
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
3991
|
-
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
3992
|
-
const { intl } = intlProvider.getChildContext();
|
|
3993
|
-
|
|
3994
|
-
mockUseValidationImpl.mockReturnValue({
|
|
3995
|
-
isValidating: false,
|
|
3996
|
-
hasBlockingErrors: true,
|
|
3997
|
-
getAllIssues: () => [
|
|
3998
|
-
{ rule: 'standard-api-validation', message: 'a' },
|
|
3999
|
-
{ source: ISSUE_SOURCES.LIQUID, severity: VALIDATION_SEVERITY.ERROR, message: 'b' },
|
|
4000
|
-
{ rule: 'sanitizer.invalidInput', message: 'c' },
|
|
4001
|
-
{ rule: 'html-warn', message: 'd' },
|
|
4002
|
-
],
|
|
4003
|
-
isClean: () => false,
|
|
4004
|
-
summary: { totalErrors: 4, totalWarnings: 0 },
|
|
4005
|
-
});
|
|
4006
|
-
|
|
4007
|
-
render(
|
|
4008
|
-
<TestWrapper>
|
|
4009
|
-
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
4010
|
-
</TestWrapper>
|
|
4011
|
-
);
|
|
4012
|
-
|
|
4013
|
-
act(() => {
|
|
4014
|
-
jest.runAllTimers();
|
|
4015
|
-
});
|
|
4016
|
-
|
|
4017
|
-
await waitFor(() => {
|
|
4018
|
-
expect(ref.current).toBeTruthy();
|
|
4019
|
-
});
|
|
4020
|
-
|
|
4021
|
-
expect(ref.current.getIssueCounts()).toEqual({
|
|
4022
|
-
errors: 3,
|
|
4023
|
-
warnings: 1,
|
|
4024
|
-
total: 4,
|
|
4025
|
-
});
|
|
4026
|
-
});
|
|
4027
|
-
|
|
4028
|
-
it('maps ALL context to DEFAULT in handleContextChange', () => {
|
|
4029
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
4030
|
-
const location = { query: { type: 'full' } };
|
|
4031
|
-
|
|
4032
|
-
render(
|
|
4033
|
-
<TestWrapper>
|
|
4034
|
-
<HTMLEditor
|
|
4035
|
-
{...defaultProps}
|
|
4036
|
-
globalActions={globalActions}
|
|
4037
|
-
location={location}
|
|
4038
|
-
variant="email"
|
|
4039
|
-
/>
|
|
4040
|
-
</TestWrapper>
|
|
4041
|
-
);
|
|
4042
|
-
|
|
4043
|
-
act(() => {
|
|
4044
|
-
jest.runAllTimers();
|
|
4045
|
-
});
|
|
4046
|
-
|
|
4047
|
-
fireEvent.click(screen.getByTestId('trigger-context-all'));
|
|
4048
|
-
|
|
4049
|
-
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
4050
|
-
layout: 'EMAIL',
|
|
4051
|
-
type: 'TAG',
|
|
4052
|
-
context: 'default',
|
|
4053
|
-
embedded: 'full',
|
|
4054
|
-
});
|
|
4055
|
-
});
|
|
4056
|
-
|
|
4057
|
-
it('email variant runs updateContent when initialContent differs from stored content', () => {
|
|
4058
|
-
const updateContent = jest.fn();
|
|
4059
|
-
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
4060
|
-
content: '<p>Stale</p>',
|
|
4061
|
-
updateContent,
|
|
4062
|
-
saveContent: jest.fn(),
|
|
4063
|
-
markAsSaved: jest.fn(),
|
|
4064
|
-
isLoading: false,
|
|
4065
|
-
isDirty: false,
|
|
4066
|
-
hasContent: true,
|
|
4067
|
-
getContentSize: jest.fn(() => 20),
|
|
4068
|
-
});
|
|
4069
|
-
|
|
4070
|
-
render(
|
|
4071
|
-
<TestWrapper>
|
|
4072
|
-
<HTMLEditor {...defaultProps} initialContent="<p>Initial content</p>" />
|
|
4073
|
-
</TestWrapper>
|
|
4074
|
-
);
|
|
4075
|
-
|
|
4076
|
-
act(() => {
|
|
4077
|
-
jest.runAllTimers();
|
|
4078
|
-
});
|
|
4079
|
-
|
|
4080
|
-
expect(updateContent).toHaveBeenCalledWith('<p>Initial content</p>', true);
|
|
4081
|
-
});
|
|
4082
|
-
|
|
4083
|
-
it('inapp variant runs updateContent when device content differs from initialContent string', () => {
|
|
4084
|
-
const updateContent = jest.fn();
|
|
4085
|
-
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
4086
|
-
activeDevice: 'android',
|
|
4087
|
-
keepContentSame: false,
|
|
4088
|
-
updateContent,
|
|
4089
|
-
saveContent: jest.fn(),
|
|
4090
|
-
markAsSaved: jest.fn(),
|
|
4091
|
-
switchDevice: jest.fn(),
|
|
4092
|
-
toggleContentSync: jest.fn(),
|
|
4093
|
-
getDeviceContent: () => '<p>android content</p>',
|
|
4094
|
-
setDeviceContent: jest.fn(),
|
|
4095
|
-
getContentSize: () => 20,
|
|
4096
|
-
isLoading: false,
|
|
4097
|
-
isDirty: false,
|
|
4098
|
-
hasContent: true,
|
|
4099
|
-
});
|
|
4100
|
-
|
|
4101
|
-
render(
|
|
4102
|
-
<TestWrapper>
|
|
4103
|
-
<HTMLEditor
|
|
4104
|
-
{...defaultProps}
|
|
4105
|
-
variant="inapp"
|
|
4106
|
-
initialContent="<p>Initial content</p>"
|
|
4107
|
-
/>
|
|
4108
|
-
</TestWrapper>
|
|
4109
|
-
);
|
|
4110
|
-
|
|
4111
|
-
act(() => {
|
|
4112
|
-
jest.runAllTimers();
|
|
4113
|
-
});
|
|
4114
|
-
|
|
4115
|
-
expect(updateContent).toHaveBeenCalledWith('<p>Initial content</p>', true);
|
|
4116
|
-
});
|
|
4117
|
-
|
|
4118
|
-
it('handleLabelInsert uses numeric cursor when getCursor is not a function', () => {
|
|
4119
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
4120
|
-
mockCodeEditorOptions.omitGetCursor = true;
|
|
4121
|
-
|
|
4122
|
-
render(
|
|
4123
|
-
<TestWrapper>
|
|
4124
|
-
<HTMLEditor {...defaultProps} />
|
|
4125
|
-
</TestWrapper>
|
|
4126
|
-
);
|
|
4127
|
-
|
|
4128
|
-
act(() => {
|
|
4129
|
-
jest.runAllTimers();
|
|
4130
|
-
});
|
|
4131
|
-
|
|
4132
|
-
fireEvent.click(screen.getByText('Insert Label (Null Position)'));
|
|
4133
|
-
|
|
4134
|
-
expect(CapNotification.success).toHaveBeenCalled();
|
|
4135
|
-
});
|
|
4136
|
-
|
|
4137
|
-
it('handleValidationErrorClick with undefined error focuses editor when ref is set', () => {
|
|
4138
|
-
mockUseValidationImpl.mockReturnValue({
|
|
4139
|
-
isValidating: false,
|
|
4140
|
-
getAllIssues: () => [{ source: 'htmlhint', rule: 'rule1', message: 'E' }],
|
|
4141
|
-
isClean: () => false,
|
|
4142
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
4143
|
-
});
|
|
4144
|
-
|
|
4145
|
-
render(
|
|
4146
|
-
<TestWrapper>
|
|
4147
|
-
<HTMLEditor {...defaultProps} />
|
|
4148
|
-
</TestWrapper>
|
|
4149
|
-
);
|
|
4150
|
-
|
|
4151
|
-
act(() => {
|
|
4152
|
-
jest.runAllTimers();
|
|
4153
|
-
});
|
|
4154
|
-
|
|
4155
|
-
fireEvent.click(screen.getByText('Error undefined'));
|
|
4156
|
-
});
|
|
4157
|
-
|
|
4158
|
-
it('handleValidationErrorClick skips editor when ref is not available', () => {
|
|
4159
|
-
mockCodeEditorOptions.setRef = false;
|
|
4160
|
-
mockUseValidationImpl.mockReturnValue({
|
|
4161
|
-
isValidating: false,
|
|
4162
|
-
getAllIssues: () => [{ source: 'htmlhint', rule: 'rule1', message: 'E' }],
|
|
4163
|
-
isClean: () => false,
|
|
4164
|
-
summary: { totalErrors: 1, totalWarnings: 0 },
|
|
4165
|
-
});
|
|
4166
|
-
|
|
4167
|
-
render(
|
|
4168
|
-
<TestWrapper>
|
|
4169
|
-
<HTMLEditor {...defaultProps} onErrorAcknowledged={jest.fn()} />
|
|
4170
|
-
</TestWrapper>
|
|
4171
|
-
);
|
|
4172
|
-
|
|
4173
|
-
act(() => {
|
|
4174
|
-
jest.runAllTimers();
|
|
4175
|
-
});
|
|
4176
|
-
|
|
4177
|
-
fireEvent.click(screen.getByText('Error at Line 5'));
|
|
4178
|
-
});
|
|
4179
|
-
|
|
4180
|
-
it('getIssueCounts treats nullish issues as non-blocking (isBlockingError issue || {})', async () => {
|
|
4181
|
-
const ref = React.createRef();
|
|
4182
|
-
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
4183
|
-
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
4184
|
-
const { intl } = intlProvider.getChildContext();
|
|
4185
|
-
|
|
4186
|
-
mockUseValidationImpl.mockReturnValue({
|
|
4187
|
-
isValidating: false,
|
|
4188
|
-
hasBlockingErrors: false,
|
|
4189
|
-
getAllIssues: () => [undefined, null, {}],
|
|
4190
|
-
isClean: () => false,
|
|
4191
|
-
summary: { totalErrors: 0, totalWarnings: 3 },
|
|
4192
|
-
});
|
|
4193
|
-
|
|
4194
|
-
render(
|
|
4195
|
-
<TestWrapper>
|
|
4196
|
-
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
4197
|
-
</TestWrapper>
|
|
4198
|
-
);
|
|
4199
|
-
|
|
4200
|
-
act(() => {
|
|
4201
|
-
jest.runAllTimers();
|
|
4202
|
-
});
|
|
4203
|
-
|
|
4204
|
-
await waitFor(() => {
|
|
4205
|
-
expect(ref.current).toBeTruthy();
|
|
4206
|
-
});
|
|
4207
|
-
|
|
4208
|
-
expect(ref.current.getIssueCounts()).toEqual({
|
|
4209
|
-
errors: 0,
|
|
4210
|
-
warnings: 3,
|
|
4211
|
-
total: 3,
|
|
4212
|
-
});
|
|
4213
|
-
});
|
|
4214
|
-
|
|
4215
|
-
it('handleContextChange uses embedded full when location.query is missing', () => {
|
|
4216
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
4217
|
-
const location = { pathname: '/creatives' };
|
|
4218
|
-
|
|
4219
|
-
render(
|
|
4220
|
-
<TestWrapper>
|
|
4221
|
-
<HTMLEditor
|
|
4222
|
-
{...defaultProps}
|
|
4223
|
-
globalActions={globalActions}
|
|
4224
|
-
location={location}
|
|
4225
|
-
variant="email"
|
|
4226
|
-
/>
|
|
4227
|
-
</TestWrapper>
|
|
4228
|
-
);
|
|
4229
|
-
|
|
4230
|
-
act(() => {
|
|
4231
|
-
jest.runAllTimers();
|
|
4232
|
-
});
|
|
4233
|
-
|
|
4234
|
-
fireEvent.click(screen.getByTestId('trigger-context-change'));
|
|
4235
|
-
|
|
4236
|
-
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
4237
|
-
layout: 'EMAIL',
|
|
4238
|
-
type: 'TAG',
|
|
4239
|
-
context: 'test-context',
|
|
4240
|
-
embedded: 'full',
|
|
4241
|
-
});
|
|
4242
|
-
});
|
|
4243
|
-
|
|
4244
|
-
it('handleSave succeeds when content.content is undefined', () => {
|
|
4245
|
-
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
4246
|
-
const onSave = jest.fn();
|
|
4247
|
-
|
|
4248
|
-
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
4249
|
-
content: undefined,
|
|
4250
|
-
updateContent: jest.fn(),
|
|
4251
|
-
saveContent: jest.fn(),
|
|
4252
|
-
markAsSaved: jest.fn(),
|
|
4253
|
-
isLoading: false,
|
|
4254
|
-
isDirty: false,
|
|
4255
|
-
hasContent: false,
|
|
4256
|
-
getContentSize: jest.fn(() => 0),
|
|
4257
|
-
});
|
|
4258
|
-
|
|
4259
|
-
render(
|
|
4260
|
-
<TestWrapper>
|
|
4261
|
-
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
4262
|
-
</TestWrapper>
|
|
4263
|
-
);
|
|
4264
|
-
|
|
4265
|
-
act(() => {
|
|
4266
|
-
jest.runAllTimers();
|
|
4267
|
-
});
|
|
4268
|
-
|
|
4269
|
-
fireEvent.click(screen.getByText('Save'));
|
|
4270
|
-
|
|
4271
|
-
expect(onSave).toHaveBeenCalledWith({ html: undefined, css: undefined, javascript: undefined });
|
|
4272
|
-
expect(CapNotification.success).toHaveBeenCalled();
|
|
4273
|
-
});
|
|
4274
|
-
|
|
4275
|
-
it('countIssuesBySeverity handles null allIssues from getAllIssues', async () => {
|
|
4276
|
-
const ref = React.createRef();
|
|
4277
|
-
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
4278
|
-
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
4279
|
-
const { intl } = intlProvider.getChildContext();
|
|
4280
|
-
|
|
4281
|
-
mockUseValidationImpl.mockReturnValue({
|
|
4282
|
-
isValidating: false,
|
|
4283
|
-
getAllIssues: () => null,
|
|
4284
|
-
isClean: () => true,
|
|
4285
|
-
summary: { totalErrors: 0, totalWarnings: 0 },
|
|
4286
|
-
});
|
|
4287
|
-
|
|
4288
|
-
render(
|
|
4289
|
-
<TestWrapper>
|
|
4290
|
-
<WrappedHTMLEditor {...defaultProps} intl={intl} ref={ref} />
|
|
4291
|
-
</TestWrapper>
|
|
4292
|
-
);
|
|
4293
|
-
|
|
4294
|
-
act(() => {
|
|
4295
|
-
jest.runAllTimers();
|
|
4296
|
-
});
|
|
4297
|
-
|
|
4298
|
-
await waitFor(() => {
|
|
4299
|
-
expect(ref.current).toBeTruthy();
|
|
4300
|
-
});
|
|
4301
|
-
|
|
4302
|
-
expect(ref.current.getIssueCounts()).toEqual({
|
|
4303
|
-
errors: 0,
|
|
4304
|
-
warnings: 0,
|
|
4305
|
-
total: 0,
|
|
4306
|
-
});
|
|
4307
|
-
});
|
|
4308
|
-
|
|
4309
|
-
it('handleContextChange lowercases null contextData to empty string', () => {
|
|
4310
|
-
const globalActions = { fetchSchemaForEntity: jest.fn() };
|
|
4311
|
-
const location = { query: { type: 'full' } };
|
|
4312
|
-
|
|
4313
|
-
render(
|
|
4314
|
-
<TestWrapper>
|
|
4315
|
-
<HTMLEditor
|
|
4316
|
-
{...defaultProps}
|
|
4317
|
-
globalActions={globalActions}
|
|
4318
|
-
location={location}
|
|
4319
|
-
variant="email"
|
|
4320
|
-
/>
|
|
4321
|
-
</TestWrapper>
|
|
4322
|
-
);
|
|
4323
|
-
|
|
4324
|
-
act(() => {
|
|
4325
|
-
jest.runAllTimers();
|
|
4326
|
-
});
|
|
4327
|
-
|
|
4328
|
-
fireEvent.click(screen.getByTestId('trigger-context-null'));
|
|
4329
|
-
|
|
4330
|
-
expect(globalActions.fetchSchemaForEntity).toHaveBeenCalledWith({
|
|
4331
|
-
layout: 'EMAIL',
|
|
4332
|
-
type: 'TAG',
|
|
4333
|
-
context: '',
|
|
4334
|
-
embedded: 'full',
|
|
4335
|
-
});
|
|
4336
|
-
});
|
|
4337
|
-
|
|
4338
|
-
it('inapp initialContent object falls back to ANDROID when active device slot is missing', () => {
|
|
4339
|
-
const updateContent = jest.fn();
|
|
4340
|
-
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
4341
|
-
activeDevice: 'android',
|
|
4342
|
-
keepContentSame: false,
|
|
4343
|
-
updateContent,
|
|
4344
|
-
saveContent: jest.fn(),
|
|
4345
|
-
markAsSaved: jest.fn(),
|
|
4346
|
-
switchDevice: jest.fn(),
|
|
4347
|
-
toggleContentSync: jest.fn(),
|
|
4348
|
-
getDeviceContent: () => '<p>old</p>',
|
|
4349
|
-
setDeviceContent: jest.fn(),
|
|
4350
|
-
getContentSize: () => 20,
|
|
4351
|
-
isLoading: false,
|
|
4352
|
-
isDirty: false,
|
|
4353
|
-
hasContent: true,
|
|
4354
|
-
});
|
|
4355
|
-
|
|
4356
|
-
render(
|
|
4357
|
-
<TestWrapper>
|
|
4358
|
-
<HTMLEditor
|
|
4359
|
-
{...defaultProps}
|
|
4360
|
-
variant="inapp"
|
|
4361
|
-
initialContent={{ ios: '<p>ios only</p>' }}
|
|
4362
|
-
/>
|
|
4363
|
-
</TestWrapper>
|
|
4364
|
-
);
|
|
4365
|
-
|
|
4366
|
-
act(() => {
|
|
4367
|
-
jest.runAllTimers();
|
|
4368
|
-
});
|
|
4369
|
-
|
|
4370
|
-
expect(updateContent).toHaveBeenCalledWith('', true);
|
|
4371
|
-
});
|
|
4372
|
-
|
|
4373
|
-
it('inapp initialContent effect skips when updateContent is not available', () => {
|
|
4374
|
-
require('../hooks/useInAppContent').useInAppContent = () => ({
|
|
4375
|
-
activeDevice: 'android',
|
|
4376
|
-
keepContentSame: false,
|
|
4377
|
-
updateContent: undefined,
|
|
4378
|
-
saveContent: jest.fn(),
|
|
4379
|
-
markAsSaved: jest.fn(),
|
|
4380
|
-
switchDevice: jest.fn(),
|
|
4381
|
-
toggleContentSync: jest.fn(),
|
|
4382
|
-
getDeviceContent: () => '<p>x</p>',
|
|
4383
|
-
setDeviceContent: jest.fn(),
|
|
4384
|
-
getContentSize: () => 20,
|
|
4385
|
-
isLoading: false,
|
|
4386
|
-
isDirty: false,
|
|
4387
|
-
hasContent: true,
|
|
4388
|
-
});
|
|
4389
|
-
|
|
4390
|
-
render(
|
|
4391
|
-
<TestWrapper>
|
|
4392
|
-
<HTMLEditor
|
|
4393
|
-
{...defaultProps}
|
|
4394
|
-
variant="inapp"
|
|
4395
|
-
initialContent="<p>y</p>"
|
|
4396
|
-
/>
|
|
4397
|
-
</TestWrapper>
|
|
4398
|
-
);
|
|
4399
|
-
|
|
4400
|
-
act(() => {
|
|
4401
|
-
jest.runAllTimers();
|
|
4402
|
-
});
|
|
4403
|
-
|
|
4404
|
-
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
4405
|
-
});
|
|
4406
|
-
|
|
4407
|
-
it('uses default forwardRef props when only intl is supplied', () => {
|
|
4408
|
-
const WrappedHTMLEditor = HTMLEditor.WrappedComponent || HTMLEditor;
|
|
4409
|
-
const intlProvider = new IntlProvider({ locale: 'en', messages: {} }, {});
|
|
4410
|
-
const { intl } = intlProvider.getChildContext();
|
|
4411
|
-
|
|
4412
|
-
render(
|
|
4413
|
-
<TestWrapper>
|
|
4414
|
-
<WrappedHTMLEditor intl={intl} />
|
|
4415
|
-
</TestWrapper>
|
|
4416
|
-
);
|
|
4417
|
-
|
|
4418
|
-
act(() => {
|
|
4419
|
-
jest.runAllTimers();
|
|
4420
|
-
});
|
|
4421
|
-
|
|
4422
|
-
expect(screen.getByTestId('editor-toolbar')).toBeInTheDocument();
|
|
4423
|
-
});
|
|
4424
|
-
|
|
4425
|
-
it('main validation effect notifies when isContentEmpty changes (hasChanged branch)', () => {
|
|
4426
|
-
const onValidationChange = jest.fn();
|
|
4427
|
-
let hookContent = '';
|
|
4428
|
-
|
|
4429
|
-
require('../hooks/useEditorContent').useEditorContent = () => ({
|
|
4430
|
-
content: hookContent,
|
|
4431
|
-
updateContent: jest.fn(),
|
|
4432
|
-
saveContent: jest.fn(),
|
|
4433
|
-
markAsSaved: jest.fn(),
|
|
4434
|
-
isLoading: false,
|
|
4435
|
-
isDirty: false,
|
|
4436
|
-
hasContent: !!hookContent,
|
|
4437
|
-
getContentSize: jest.fn(() => hookContent.length),
|
|
4438
|
-
});
|
|
4439
|
-
|
|
4440
|
-
const { rerender } = render(
|
|
4441
|
-
<TestWrapper>
|
|
4442
|
-
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
4443
|
-
</TestWrapper>
|
|
4444
|
-
);
|
|
4445
|
-
|
|
4446
|
-
act(() => {
|
|
4447
|
-
jest.runAllTimers();
|
|
4448
|
-
});
|
|
4449
|
-
|
|
4450
|
-
onValidationChange.mockClear();
|
|
4451
|
-
|
|
4452
|
-
hookContent = '<p>not empty</p>';
|
|
4453
|
-
|
|
4454
|
-
rerender(
|
|
4455
|
-
<TestWrapper>
|
|
4456
|
-
<HTMLEditor {...defaultProps} onValidationChange={onValidationChange} />
|
|
4457
|
-
</TestWrapper>
|
|
4458
|
-
);
|
|
4459
|
-
|
|
4460
|
-
act(() => {
|
|
4461
|
-
jest.runAllTimers();
|
|
4462
|
-
});
|
|
4463
|
-
|
|
4464
|
-
expect(onValidationChange).toHaveBeenCalled();
|
|
4465
|
-
const lastCall = onValidationChange.mock.calls[onValidationChange.mock.calls.length - 1][0];
|
|
4466
|
-
expect(lastCall.isContentEmpty).toBe(false);
|
|
4467
|
-
});
|
|
4468
|
-
});
|
|
4469
3544
|
});
|