@capillarytech/creatives-library 8.0.235 → 8.0.236-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/assets/Android.png +0 -0
- package/assets/iOS.png +0 -0
- package/config/app.js +0 -1
- package/constants/unified.js +1 -1
- package/initialReducer.js +2 -0
- package/package.json +1 -1
- package/services/api.js +5 -2
- package/services/tests/api.test.js +18 -0
- package/utils/common.js +1 -2
- package/utils/commonUtils.js +14 -1
- package/utils/transformTemplateConfig.js +0 -10
- package/v2Components/CapDeviceContent/index.js +61 -56
- package/v2Components/CapTagList/index.js +4 -0
- package/v2Components/CapWhatsappCTA/tests/index.test.js +5 -0
- package/v2Components/HtmlEditor/HTMLEditor.js +165 -80
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +532 -0
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +17 -12
- package/v2Components/HtmlEditor/_htmlEditor.scss +0 -4
- package/v2Components/HtmlEditor/_index.lazy.scss +0 -1
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +0 -98
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +125 -148
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +1 -0
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +3 -3
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +4 -7
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +35 -45
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +1 -3
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +33 -33
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +7 -6
- package/v2Components/HtmlEditor/constants.js +29 -20
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +158 -17
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +53 -143
- package/v2Components/HtmlEditor/index.js +1 -1
- package/v2Components/HtmlEditor/messages.js +85 -85
- package/v2Components/MobilePushPreviewV2/index.js +32 -7
- package/v2Components/TemplatePreview/_templatePreview.scss +31 -21
- package/v2Components/TemplatePreview/index.js +47 -32
- package/v2Components/TemplatePreview/messages.js +4 -0
- package/v2Containers/BeeEditor/index.js +82 -80
- package/v2Containers/BeePopupEditor/constants.js +10 -0
- package/v2Containers/BeePopupEditor/index.js +180 -0
- package/v2Containers/BeePopupEditor/tests/index.test.js +627 -0
- package/v2Containers/CreativesContainer/SlideBoxContent.js +69 -34
- package/v2Containers/CreativesContainer/SlideBoxHeader.js +2 -1
- package/v2Containers/CreativesContainer/constants.js +1 -0
- package/v2Containers/CreativesContainer/index.js +65 -13
- package/v2Containers/CreativesContainer/tests/__snapshots__/SlideBoxContent.test.js.snap +4 -12
- package/v2Containers/CreativesContainer/tests/__snapshots__/index.test.js.snap +15 -0
- package/v2Containers/InApp/__tests__/InAppHTMLEditor.test.js +376 -0
- package/v2Containers/InApp/__tests__/sagas.test.js +363 -0
- package/v2Containers/InApp/actions.js +7 -0
- package/v2Containers/InApp/constants.js +18 -4
- package/v2Containers/InApp/index.js +642 -355
- package/v2Containers/InApp/index.scss +4 -3
- package/v2Containers/InApp/messages.js +7 -3
- package/v2Containers/InApp/reducer.js +21 -3
- package/v2Containers/InApp/sagas.js +29 -9
- package/v2Containers/InApp/selectors.js +25 -5
- package/v2Containers/InApp/tests/index.test.js +154 -50
- package/v2Containers/InApp/tests/reducer.test.js +34 -0
- package/v2Containers/InApp/tests/sagas.test.js +61 -9
- package/v2Containers/InApp/tests/selectors.test.js +612 -0
- package/v2Containers/InAppWrapper/components/InAppWrapperView.js +162 -0
- package/v2Containers/InAppWrapper/components/__tests__/InAppWrapperView.test.js +267 -0
- package/v2Containers/InAppWrapper/components/inAppWrapperView.scss +9 -0
- package/v2Containers/InAppWrapper/constants.js +16 -0
- package/v2Containers/InAppWrapper/hooks/__tests__/useInAppWrapper.test.js +473 -0
- package/v2Containers/InAppWrapper/hooks/useInAppWrapper.js +198 -0
- package/v2Containers/InAppWrapper/index.js +148 -0
- package/v2Containers/InAppWrapper/messages.js +49 -0
- package/v2Containers/InappAdvance/index.js +1006 -0
- package/v2Containers/InappAdvance/index.scss +10 -0
- package/v2Containers/InappAdvance/tests/index.test.js +448 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/content.test.js.snap +3 -0
- package/v2Containers/Line/Container/ImageCarousel/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/Wrapper/tests/__snapshots__/index.test.js.snap +2 -0
- package/v2Containers/Line/Container/tests/__snapshots__/index.test.js.snap +9 -0
- package/v2Containers/Rcs/tests/__snapshots__/index.test.js.snap +12 -0
- package/v2Containers/SmsTrai/Edit/tests/__snapshots__/index.test.js.snap +4 -0
- package/v2Containers/TagList/index.js +65 -1
- package/v2Containers/Templates/_templates.scss +49 -1
- package/v2Containers/Templates/index.js +93 -5
- package/v2Containers/Templates/messages.js +4 -0
- package/v2Containers/Templates/reducer.js +20 -7
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +8 -88
- package/v2Containers/Templates/tests/reducer.test.js +125 -0
- package/v2Containers/Whatsapp/tests/__snapshots__/index.test.js.snap +35 -0
|
@@ -100,6 +100,9 @@ jest.mock('../components/EditorToolbar', () => {
|
|
|
100
100
|
<button onClick={() => props.onLabelInsert && props.onLabelInsert('test-label', 10)}>
|
|
101
101
|
Insert Label
|
|
102
102
|
</button>
|
|
103
|
+
<button onClick={() => props.onLabelInsert && props.onLabelInsert('test-label', null)}>
|
|
104
|
+
Insert Label (Null Position)
|
|
105
|
+
</button>
|
|
103
106
|
<button onClick={() => props.onSave && props.onSave()}>
|
|
104
107
|
Save
|
|
105
108
|
</button>
|
|
@@ -473,6 +476,40 @@ describe('HTMLEditor', () => {
|
|
|
473
476
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
474
477
|
});
|
|
475
478
|
|
|
479
|
+
it('converts string initialContent to device-specific format for inapp variant', () => {
|
|
480
|
+
// This test covers lines 104-109 in HTMLEditor.js
|
|
481
|
+
const mockUseInAppContent = require('../hooks/useInAppContent').useInAppContent;
|
|
482
|
+
const originalUseInAppContent = jest.requireActual('../hooks/useInAppContent').useInAppContent;
|
|
483
|
+
|
|
484
|
+
let capturedInitialContent = null;
|
|
485
|
+
jest.spyOn(require('../hooks/useInAppContent'), 'useInAppContent').mockImplementation((initialContent) => {
|
|
486
|
+
capturedInitialContent = initialContent;
|
|
487
|
+
return originalUseInAppContent(initialContent, {});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
render(
|
|
491
|
+
<TestWrapper>
|
|
492
|
+
<HTMLEditor
|
|
493
|
+
{...defaultProps}
|
|
494
|
+
variant="inapp"
|
|
495
|
+
initialContent="<p>String content</p>"
|
|
496
|
+
/>
|
|
497
|
+
</TestWrapper>
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
act(() => {
|
|
501
|
+
jest.runAllTimers();
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Verify that string content was converted to device-specific format
|
|
505
|
+
expect(capturedInitialContent).toEqual({
|
|
506
|
+
android: '<p>String content</p>',
|
|
507
|
+
ios: '<p>String content</p>'
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
jest.restoreAllMocks();
|
|
511
|
+
});
|
|
512
|
+
|
|
476
513
|
it('handles object initialContent for inapp variant', () => {
|
|
477
514
|
const deviceContent = {
|
|
478
515
|
android: '<p>Android content</p>',
|
|
@@ -496,6 +533,42 @@ describe('HTMLEditor', () => {
|
|
|
496
533
|
expect(screen.getByTestId('device-toggle')).toBeInTheDocument();
|
|
497
534
|
});
|
|
498
535
|
|
|
536
|
+
it('uses provided device-specific content for inapp variant', () => {
|
|
537
|
+
// This test covers lines 110-113 in HTMLEditor.js
|
|
538
|
+
const deviceContent = {
|
|
539
|
+
android: '<p>Android content</p>',
|
|
540
|
+
ios: '<p>iOS content</p>'
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
const mockUseInAppContent = require('../hooks/useInAppContent').useInAppContent;
|
|
544
|
+
const originalUseInAppContent = jest.requireActual('../hooks/useInAppContent').useInAppContent;
|
|
545
|
+
|
|
546
|
+
let capturedInitialContent = null;
|
|
547
|
+
jest.spyOn(require('../hooks/useInAppContent'), 'useInAppContent').mockImplementation((initialContent) => {
|
|
548
|
+
capturedInitialContent = initialContent;
|
|
549
|
+
return originalUseInAppContent(initialContent, {});
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
render(
|
|
553
|
+
<TestWrapper>
|
|
554
|
+
<HTMLEditor
|
|
555
|
+
{...defaultProps}
|
|
556
|
+
variant="inapp"
|
|
557
|
+
initialContent={deviceContent}
|
|
558
|
+
/>
|
|
559
|
+
</TestWrapper>
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
act(() => {
|
|
563
|
+
jest.runAllTimers();
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// Verify that object content was passed as-is
|
|
567
|
+
expect(capturedInitialContent).toEqual(deviceContent);
|
|
568
|
+
|
|
569
|
+
jest.restoreAllMocks();
|
|
570
|
+
});
|
|
571
|
+
|
|
499
572
|
it('handles inapp variant with layoutType', () => {
|
|
500
573
|
render(
|
|
501
574
|
<TestWrapper>
|
|
@@ -1805,5 +1878,464 @@ describe('HTMLEditor', () => {
|
|
|
1805
1878
|
unmount();
|
|
1806
1879
|
});
|
|
1807
1880
|
});
|
|
1881
|
+
|
|
1882
|
+
describe('handleContextChange Coverage', () => {
|
|
1883
|
+
it('calls onContextChange when provided instead of making API call', () => {
|
|
1884
|
+
const onContextChange = jest.fn();
|
|
1885
|
+
const globalActions = {
|
|
1886
|
+
fetchSchemaForEntity: jest.fn(),
|
|
1887
|
+
};
|
|
1888
|
+
|
|
1889
|
+
render(
|
|
1890
|
+
<TestWrapper>
|
|
1891
|
+
<HTMLEditor
|
|
1892
|
+
{...defaultProps}
|
|
1893
|
+
onContextChange={onContextChange}
|
|
1894
|
+
globalActions={globalActions}
|
|
1895
|
+
location={{ query: { type: 'embedded' } }}
|
|
1896
|
+
/>
|
|
1897
|
+
</TestWrapper>
|
|
1898
|
+
);
|
|
1899
|
+
|
|
1900
|
+
act(() => {
|
|
1901
|
+
jest.runAllTimers();
|
|
1902
|
+
});
|
|
1903
|
+
|
|
1904
|
+
// Wait for component to render
|
|
1905
|
+
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
1906
|
+
if (!codeEditorPane) {
|
|
1907
|
+
// Component might be in loading state, wait a bit more
|
|
1908
|
+
act(() => {
|
|
1909
|
+
jest.runAllTimers();
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
// The CodeEditorPane would call onContextChange
|
|
1914
|
+
// We verify that onContextChange is passed and would be called
|
|
1915
|
+
expect(onContextChange).toBeDefined();
|
|
1916
|
+
|
|
1917
|
+
// If onContextChange is provided, globalActions.fetchSchemaForEntity should not be called
|
|
1918
|
+
// This is tested by ensuring onContextChange is used instead
|
|
1919
|
+
});
|
|
1920
|
+
|
|
1921
|
+
it('makes API call when onContextChange is not provided but globalActions is available', () => {
|
|
1922
|
+
const globalActions = {
|
|
1923
|
+
fetchSchemaForEntity: jest.fn(),
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1926
|
+
render(
|
|
1927
|
+
<TestWrapper>
|
|
1928
|
+
<HTMLEditor
|
|
1929
|
+
{...defaultProps}
|
|
1930
|
+
onContextChange={null}
|
|
1931
|
+
globalActions={globalActions}
|
|
1932
|
+
location={{ query: { type: 'embedded' } }}
|
|
1933
|
+
/>
|
|
1934
|
+
</TestWrapper>
|
|
1935
|
+
);
|
|
1936
|
+
|
|
1937
|
+
act(() => {
|
|
1938
|
+
jest.runAllTimers();
|
|
1939
|
+
});
|
|
1940
|
+
|
|
1941
|
+
// Wait for component to render
|
|
1942
|
+
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
1943
|
+
if (!toolbar) {
|
|
1944
|
+
act(() => {
|
|
1945
|
+
jest.runAllTimers();
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
// The handleContextChange would be called by CodeEditorPane
|
|
1950
|
+
// We verify globalActions is available
|
|
1951
|
+
expect(globalActions.fetchSchemaForEntity).toBeDefined();
|
|
1952
|
+
});
|
|
1953
|
+
|
|
1954
|
+
it('does not make API call when globalActions is not available', () => {
|
|
1955
|
+
render(
|
|
1956
|
+
<TestWrapper>
|
|
1957
|
+
<HTMLEditor
|
|
1958
|
+
{...defaultProps}
|
|
1959
|
+
onContextChange={null}
|
|
1960
|
+
globalActions={null}
|
|
1961
|
+
location={{ query: { type: 'embedded' } }}
|
|
1962
|
+
/>
|
|
1963
|
+
</TestWrapper>
|
|
1964
|
+
);
|
|
1965
|
+
|
|
1966
|
+
act(() => {
|
|
1967
|
+
jest.runAllTimers();
|
|
1968
|
+
});
|
|
1969
|
+
|
|
1970
|
+
// Wait for component to render - might be in loading state
|
|
1971
|
+
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
1972
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
1973
|
+
|
|
1974
|
+
// Component should render (either loaded or loading)
|
|
1975
|
+
expect(toolbar || loading).toBeTruthy();
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
it('uses SMS layout for INAPP variant in handleContextChange', () => {
|
|
1979
|
+
const globalActions = {
|
|
1980
|
+
fetchSchemaForEntity: jest.fn(),
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
render(
|
|
1984
|
+
<TestWrapper>
|
|
1985
|
+
<HTMLEditor
|
|
1986
|
+
{...defaultProps}
|
|
1987
|
+
variant="inapp"
|
|
1988
|
+
onContextChange={null}
|
|
1989
|
+
globalActions={globalActions}
|
|
1990
|
+
location={{ query: { type: 'embedded' } }}
|
|
1991
|
+
/>
|
|
1992
|
+
</TestWrapper>
|
|
1993
|
+
);
|
|
1994
|
+
|
|
1995
|
+
act(() => {
|
|
1996
|
+
jest.runAllTimers();
|
|
1997
|
+
});
|
|
1998
|
+
|
|
1999
|
+
// Wait for component to render
|
|
2000
|
+
const deviceToggle = screen.queryByTestId('device-toggle');
|
|
2001
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2002
|
+
|
|
2003
|
+
// Component should render (either loaded or loading)
|
|
2004
|
+
expect(deviceToggle || loading).toBeTruthy();
|
|
2005
|
+
});
|
|
2006
|
+
|
|
2007
|
+
it('handles context change with ALL context type', () => {
|
|
2008
|
+
const globalActions = {
|
|
2009
|
+
fetchSchemaForEntity: jest.fn(),
|
|
2010
|
+
};
|
|
2011
|
+
|
|
2012
|
+
render(
|
|
2013
|
+
<TestWrapper>
|
|
2014
|
+
<HTMLEditor
|
|
2015
|
+
{...defaultProps}
|
|
2016
|
+
onContextChange={null}
|
|
2017
|
+
globalActions={globalActions}
|
|
2018
|
+
location={{ query: { type: 'embedded' } }}
|
|
2019
|
+
/>
|
|
2020
|
+
</TestWrapper>
|
|
2021
|
+
);
|
|
2022
|
+
|
|
2023
|
+
act(() => {
|
|
2024
|
+
jest.runAllTimers();
|
|
2025
|
+
});
|
|
2026
|
+
|
|
2027
|
+
// Component should render
|
|
2028
|
+
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
2029
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2030
|
+
expect(toolbar || loading).toBeTruthy();
|
|
2031
|
+
});
|
|
2032
|
+
|
|
2033
|
+
it('handles context change with embedded type', () => {
|
|
2034
|
+
const globalActions = {
|
|
2035
|
+
fetchSchemaForEntity: jest.fn(),
|
|
2036
|
+
};
|
|
2037
|
+
|
|
2038
|
+
render(
|
|
2039
|
+
<TestWrapper>
|
|
2040
|
+
<HTMLEditor
|
|
2041
|
+
{...defaultProps}
|
|
2042
|
+
onContextChange={null}
|
|
2043
|
+
globalActions={globalActions}
|
|
2044
|
+
location={{ query: { type: 'embedded', module: 'test' } }}
|
|
2045
|
+
/>
|
|
2046
|
+
</TestWrapper>
|
|
2047
|
+
);
|
|
2048
|
+
|
|
2049
|
+
act(() => {
|
|
2050
|
+
jest.runAllTimers();
|
|
2051
|
+
});
|
|
2052
|
+
|
|
2053
|
+
// Component should render
|
|
2054
|
+
const toolbar = screen.queryByTestId('editor-toolbar');
|
|
2055
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2056
|
+
expect(toolbar || loading).toBeTruthy();
|
|
2057
|
+
});
|
|
2058
|
+
});
|
|
2059
|
+
|
|
2060
|
+
describe('handleLabelInsert Coverage', () => {
|
|
2061
|
+
it('handles label insert when position is null and editor is ready', () => {
|
|
2062
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2063
|
+
|
|
2064
|
+
render(
|
|
2065
|
+
<TestWrapper>
|
|
2066
|
+
<HTMLEditor {...defaultProps} />
|
|
2067
|
+
</TestWrapper>
|
|
2068
|
+
);
|
|
2069
|
+
|
|
2070
|
+
act(() => {
|
|
2071
|
+
jest.runAllTimers();
|
|
2072
|
+
});
|
|
2073
|
+
|
|
2074
|
+
// Wait for component to render
|
|
2075
|
+
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2076
|
+
if (insertButton) {
|
|
2077
|
+
fireEvent.click(insertButton);
|
|
2078
|
+
|
|
2079
|
+
// Should attempt to insert via editor ref
|
|
2080
|
+
// The mock editor has insertText method, so it should work
|
|
2081
|
+
expect(CapNotification.success).toHaveBeenCalled();
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
2085
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2086
|
+
expect(codeEditorPane || loading).toBeTruthy();
|
|
2087
|
+
});
|
|
2088
|
+
|
|
2089
|
+
it('shows warning when editor is not available for label insert', () => {
|
|
2090
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2091
|
+
|
|
2092
|
+
// Mock CodeEditorPane to return null ref
|
|
2093
|
+
jest.doMock('../components/CodeEditorPane', () => {
|
|
2094
|
+
const React = require('react');
|
|
2095
|
+
return React.forwardRef(function MockCodeEditorPane() {
|
|
2096
|
+
// Return null ref
|
|
2097
|
+
React.useImperativeHandle(null, () => null);
|
|
2098
|
+
return <div data-testid="code-editor-pane">Editor</div>;
|
|
2099
|
+
});
|
|
2100
|
+
});
|
|
2101
|
+
|
|
2102
|
+
render(
|
|
2103
|
+
<TestWrapper>
|
|
2104
|
+
<HTMLEditor {...defaultProps} />
|
|
2105
|
+
</TestWrapper>
|
|
2106
|
+
);
|
|
2107
|
+
|
|
2108
|
+
act(() => {
|
|
2109
|
+
jest.runAllTimers();
|
|
2110
|
+
});
|
|
2111
|
+
|
|
2112
|
+
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2113
|
+
if (insertButton) {
|
|
2114
|
+
fireEvent.click(insertButton);
|
|
2115
|
+
// Should show warning when editor is null
|
|
2116
|
+
expect(CapNotification.warning).toHaveBeenCalled();
|
|
2117
|
+
}
|
|
2118
|
+
});
|
|
2119
|
+
|
|
2120
|
+
it('shows error when editor does not have insertText method', () => {
|
|
2121
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2122
|
+
|
|
2123
|
+
// Mock CodeEditorPane to return editor without insertText
|
|
2124
|
+
jest.doMock('../components/CodeEditorPane', () => {
|
|
2125
|
+
const React = require('react');
|
|
2126
|
+
return React.forwardRef(function MockCodeEditorPane(props, ref) {
|
|
2127
|
+
React.useImperativeHandle(ref, () => ({
|
|
2128
|
+
focus: jest.fn(),
|
|
2129
|
+
getCursor: jest.fn(() => 0),
|
|
2130
|
+
// No insertText method
|
|
2131
|
+
}));
|
|
2132
|
+
return <div data-testid="code-editor-pane">Editor</div>;
|
|
2133
|
+
});
|
|
2134
|
+
});
|
|
2135
|
+
|
|
2136
|
+
render(
|
|
2137
|
+
<TestWrapper>
|
|
2138
|
+
<HTMLEditor {...defaultProps} />
|
|
2139
|
+
</TestWrapper>
|
|
2140
|
+
);
|
|
2141
|
+
|
|
2142
|
+
act(() => {
|
|
2143
|
+
jest.runAllTimers();
|
|
2144
|
+
});
|
|
2145
|
+
|
|
2146
|
+
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2147
|
+
if (insertButton) {
|
|
2148
|
+
fireEvent.click(insertButton);
|
|
2149
|
+
// Should show error when insertText is not available
|
|
2150
|
+
expect(CapNotification.error).toHaveBeenCalled();
|
|
2151
|
+
}
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
it('handles label insert when position is provided (already inserted)', () => {
|
|
2155
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2156
|
+
|
|
2157
|
+
render(
|
|
2158
|
+
<TestWrapper>
|
|
2159
|
+
<HTMLEditor {...defaultProps} />
|
|
2160
|
+
</TestWrapper>
|
|
2161
|
+
);
|
|
2162
|
+
|
|
2163
|
+
act(() => {
|
|
2164
|
+
jest.runAllTimers();
|
|
2165
|
+
});
|
|
2166
|
+
|
|
2167
|
+
// Simulate label insert with position (already inserted by CodeEditorPane)
|
|
2168
|
+
// This would call handleLabelInsert with a valid position
|
|
2169
|
+
const insertButton = screen.queryByText('Insert Label');
|
|
2170
|
+
if (insertButton) {
|
|
2171
|
+
fireEvent.click(insertButton);
|
|
2172
|
+
// Should show success notification
|
|
2173
|
+
expect(CapNotification.success).toHaveBeenCalled();
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
|
|
2177
|
+
it('handles error during label insert', () => {
|
|
2178
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2179
|
+
|
|
2180
|
+
// Mock CodeEditorPane to throw error on insertText
|
|
2181
|
+
jest.doMock('../components/CodeEditorPane', () => {
|
|
2182
|
+
const React = require('react');
|
|
2183
|
+
return React.forwardRef(function MockCodeEditorPane(props, ref) {
|
|
2184
|
+
React.useImperativeHandle(ref, () => ({
|
|
2185
|
+
insertText: jest.fn(() => {
|
|
2186
|
+
throw new Error('Insert failed');
|
|
2187
|
+
}),
|
|
2188
|
+
focus: jest.fn(),
|
|
2189
|
+
getCursor: jest.fn(() => 0),
|
|
2190
|
+
}));
|
|
2191
|
+
return <div data-testid="code-editor-pane">Editor</div>;
|
|
2192
|
+
});
|
|
2193
|
+
});
|
|
2194
|
+
|
|
2195
|
+
render(
|
|
2196
|
+
<TestWrapper>
|
|
2197
|
+
<HTMLEditor {...defaultProps} />
|
|
2198
|
+
</TestWrapper>
|
|
2199
|
+
);
|
|
2200
|
+
|
|
2201
|
+
act(() => {
|
|
2202
|
+
jest.runAllTimers();
|
|
2203
|
+
});
|
|
2204
|
+
|
|
2205
|
+
const insertButton = screen.queryByText('Insert Label (Null Position)');
|
|
2206
|
+
if (insertButton) {
|
|
2207
|
+
fireEvent.click(insertButton);
|
|
2208
|
+
// Should show error notification
|
|
2209
|
+
expect(CapNotification.error).toHaveBeenCalled();
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
});
|
|
2213
|
+
|
|
2214
|
+
describe('handleSave Coverage', () => {
|
|
2215
|
+
it('calls onSave callback when save is triggered', () => {
|
|
2216
|
+
const onSave = jest.fn();
|
|
2217
|
+
|
|
2218
|
+
render(
|
|
2219
|
+
<TestWrapper>
|
|
2220
|
+
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
2221
|
+
</TestWrapper>
|
|
2222
|
+
);
|
|
2223
|
+
|
|
2224
|
+
act(() => {
|
|
2225
|
+
jest.runAllTimers();
|
|
2226
|
+
});
|
|
2227
|
+
|
|
2228
|
+
const saveButton = screen.queryByText('Save');
|
|
2229
|
+
if (saveButton) {
|
|
2230
|
+
fireEvent.click(saveButton);
|
|
2231
|
+
// onSave should be called
|
|
2232
|
+
expect(onSave).toHaveBeenCalled();
|
|
2233
|
+
} else {
|
|
2234
|
+
// Component might be in loading state
|
|
2235
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2236
|
+
expect(loading).toBeTruthy();
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
|
|
2240
|
+
it('shows success notification on save', () => {
|
|
2241
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2242
|
+
const onSave = jest.fn();
|
|
2243
|
+
|
|
2244
|
+
render(
|
|
2245
|
+
<TestWrapper>
|
|
2246
|
+
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
2247
|
+
</TestWrapper>
|
|
2248
|
+
);
|
|
2249
|
+
|
|
2250
|
+
act(() => {
|
|
2251
|
+
jest.runAllTimers();
|
|
2252
|
+
});
|
|
2253
|
+
|
|
2254
|
+
const saveButton = screen.queryByText('Save');
|
|
2255
|
+
if (saveButton) {
|
|
2256
|
+
fireEvent.click(saveButton);
|
|
2257
|
+
expect(CapNotification.success).toHaveBeenCalled();
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
2260
|
+
|
|
2261
|
+
it('handles save error gracefully', () => {
|
|
2262
|
+
const CapNotification = require('@capillarytech/cap-ui-library/CapNotification');
|
|
2263
|
+
const onSave = jest.fn(() => {
|
|
2264
|
+
throw new Error('Save failed');
|
|
2265
|
+
});
|
|
2266
|
+
|
|
2267
|
+
render(
|
|
2268
|
+
<TestWrapper>
|
|
2269
|
+
<HTMLEditor {...defaultProps} onSave={onSave} />
|
|
2270
|
+
</TestWrapper>
|
|
2271
|
+
);
|
|
2272
|
+
|
|
2273
|
+
act(() => {
|
|
2274
|
+
jest.runAllTimers();
|
|
2275
|
+
});
|
|
2276
|
+
|
|
2277
|
+
const saveButton = screen.queryByText('Save');
|
|
2278
|
+
if (saveButton) {
|
|
2279
|
+
fireEvent.click(saveButton);
|
|
2280
|
+
expect(CapNotification.error).toHaveBeenCalled();
|
|
2281
|
+
}
|
|
2282
|
+
});
|
|
2283
|
+
});
|
|
2284
|
+
|
|
2285
|
+
describe('handleValidationErrorClick Coverage', () => {
|
|
2286
|
+
it('navigates to error line when editor has navigateToLine method', () => {
|
|
2287
|
+
mockUseValidationImpl.mockReturnValueOnce({
|
|
2288
|
+
isValidating: false,
|
|
2289
|
+
getAllIssues: () => [{ message: 'Test error', line: 5, column: 10, severity: 'error' }],
|
|
2290
|
+
isClean: () => false,
|
|
2291
|
+
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
2292
|
+
});
|
|
2293
|
+
|
|
2294
|
+
render(
|
|
2295
|
+
<TestWrapper>
|
|
2296
|
+
<HTMLEditor {...defaultProps} />
|
|
2297
|
+
</TestWrapper>
|
|
2298
|
+
);
|
|
2299
|
+
|
|
2300
|
+
act(() => {
|
|
2301
|
+
jest.runAllTimers();
|
|
2302
|
+
});
|
|
2303
|
+
|
|
2304
|
+
// Click on error
|
|
2305
|
+
const errorButton = screen.queryByText('Error at Line 5');
|
|
2306
|
+
if (errorButton) {
|
|
2307
|
+
fireEvent.click(errorButton);
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// Should attempt to navigate to line
|
|
2311
|
+
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
2312
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2313
|
+
expect(codeEditorPane || loading).toBeTruthy();
|
|
2314
|
+
});
|
|
2315
|
+
|
|
2316
|
+
it('focuses editor when navigateToLine is not available', () => {
|
|
2317
|
+
mockUseValidationImpl.mockReturnValueOnce({
|
|
2318
|
+
isValidating: false,
|
|
2319
|
+
getAllIssues: () => [{ message: 'Test error', line: 5, column: 10, severity: 'error' }],
|
|
2320
|
+
isClean: () => false,
|
|
2321
|
+
summary: { totalErrors: 1, totalWarnings: 0 }
|
|
2322
|
+
});
|
|
2323
|
+
|
|
2324
|
+
render(
|
|
2325
|
+
<TestWrapper>
|
|
2326
|
+
<HTMLEditor {...defaultProps} />
|
|
2327
|
+
</TestWrapper>
|
|
2328
|
+
);
|
|
2329
|
+
|
|
2330
|
+
act(() => {
|
|
2331
|
+
jest.runAllTimers();
|
|
2332
|
+
});
|
|
2333
|
+
|
|
2334
|
+
// Component should render
|
|
2335
|
+
const codeEditorPane = screen.queryByTestId('code-editor-pane');
|
|
2336
|
+
const loading = screen.queryByText('Initializing HTML Editor...');
|
|
2337
|
+
expect(codeEditorPane || loading).toBeTruthy();
|
|
2338
|
+
});
|
|
2339
|
+
});
|
|
1808
2340
|
});
|
|
1809
2341
|
|
|
@@ -9,19 +9,23 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|
|
9
9
|
import '@testing-library/jest-dom';
|
|
10
10
|
|
|
11
11
|
// Mock CapSpin before importing the component
|
|
12
|
-
jest.mock('@capillarytech/cap-ui-library/CapSpin', () => {
|
|
13
|
-
return
|
|
14
|
-
return <div data-testid="cap-spin" data-size={size}>Loading...</div>;
|
|
15
|
-
};
|
|
12
|
+
jest.mock('@capillarytech/cap-ui-library/CapSpin', () => function MockCapSpin({ size }) {
|
|
13
|
+
return <div data-testid="cap-spin" data-size={size}>Loading...</div>;
|
|
16
14
|
});
|
|
17
15
|
|
|
18
16
|
// Mock the HTMLEditor component
|
|
19
17
|
jest.mock('../HTMLEditor', () => {
|
|
18
|
+
const MockHTMLEditor = function MockHTMLEditor(props) {
|
|
19
|
+
return (
|
|
20
|
+
<div data-testid="html-editor">
|
|
21
|
+
Mock HTML Editor -
|
|
22
|
+
{props.variant}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
20
26
|
return {
|
|
21
27
|
__esModule: true,
|
|
22
|
-
default:
|
|
23
|
-
return <div data-testid="html-editor">Mock HTML Editor - {props.variant}</div>;
|
|
24
|
-
}
|
|
28
|
+
default: MockHTMLEditor,
|
|
25
29
|
};
|
|
26
30
|
});
|
|
27
31
|
|
|
@@ -113,7 +117,8 @@ describe('index.lazy.js', () => {
|
|
|
113
117
|
expect(screen.getByTestId('html-editor')).toBeInTheDocument();
|
|
114
118
|
});
|
|
115
119
|
|
|
116
|
-
|
|
120
|
+
const editor = screen.getByTestId('html-editor');
|
|
121
|
+
expect(editor.textContent).toBe('Mock HTML Editor -email');
|
|
117
122
|
});
|
|
118
123
|
|
|
119
124
|
it('shows fallback while loading', async () => {
|
|
@@ -134,7 +139,7 @@ describe('index.lazy.js', () => {
|
|
|
134
139
|
<HTMLEditorLazy
|
|
135
140
|
variant="inapp"
|
|
136
141
|
onSave={mockOnSave}
|
|
137
|
-
readOnly
|
|
142
|
+
readOnly
|
|
138
143
|
className="custom-class"
|
|
139
144
|
/>
|
|
140
145
|
);
|
|
@@ -143,7 +148,8 @@ describe('index.lazy.js', () => {
|
|
|
143
148
|
expect(screen.getByTestId('html-editor')).toBeInTheDocument();
|
|
144
149
|
});
|
|
145
150
|
|
|
146
|
-
|
|
151
|
+
const editor = screen.getByTestId('html-editor');
|
|
152
|
+
expect(editor.textContent).toBe('Mock HTML Editor -inapp');
|
|
147
153
|
});
|
|
148
154
|
|
|
149
155
|
it('has correct display name', async () => {
|
|
@@ -215,7 +221,7 @@ describe('index.lazy.js', () => {
|
|
|
215
221
|
render(
|
|
216
222
|
<HTMLEditorLazy
|
|
217
223
|
className="custom-editor"
|
|
218
|
-
readOnly
|
|
224
|
+
readOnly
|
|
219
225
|
showFullscreenButton={false}
|
|
220
226
|
autoSave={false}
|
|
221
227
|
autoSaveInterval={60000}
|
|
@@ -529,4 +535,3 @@ describe('index.lazy.js', () => {
|
|
|
529
535
|
});
|
|
530
536
|
});
|
|
531
537
|
});
|
|
532
|
-
|
|
@@ -267,10 +267,6 @@
|
|
|
267
267
|
|
|
268
268
|
// Focus states and accessibility
|
|
269
269
|
.html-editor {
|
|
270
|
-
&:focus-within {
|
|
271
|
-
outline: 0.125rem solid map-get($CAP_PRIMARY, base); // 2px = 0.125rem
|
|
272
|
-
outline-offset: -0.125rem; // -2px = -0.125rem
|
|
273
|
-
}
|
|
274
270
|
|
|
275
271
|
// High contrast mode support
|
|
276
272
|
@media (prefers-contrast: high) {
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
align-items: center;
|
|
14
14
|
min-height: 25rem; // 400px = 25rem
|
|
15
15
|
background: $CAP_G11; // Light background similar to #fafbfc
|
|
16
|
-
border: 0.0625rem solid $CAP_G07; // 1px border similar to #dfe2e7
|
|
17
16
|
border-radius: 0.5rem; // 8px = 0.5rem
|
|
18
17
|
flex-direction: column;
|
|
19
18
|
gap: 1rem; // 16px = 1rem
|