@capillarytech/creatives-library 8.0.242-alpha.1 → 8.0.242-alpha.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "8.0.242-alpha.1",
4
+ "version": "8.0.242-alpha.10",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -77,6 +77,7 @@ const HTMLEditor = ({
77
77
  onContextChange = null,
78
78
  globalActions = null,
79
79
  isLiquidEnabled = false, // Controls Liquid tab visibility in ValidationTabs
80
+ isFullMode = true, // Full mode vs library mode - controls layout and visibility
80
81
  ...props
81
82
  }) => {
82
83
  // Separate refs for main and modal editors to avoid conflicts
@@ -434,9 +435,14 @@ const HTMLEditor = ({
434
435
  );
435
436
  }
436
437
 
438
+ // Add library-mode class when not in full mode
439
+ // Note: isFullMode defaults to true, so library mode is when isFullMode === false
440
+ const isLibraryMode = isFullMode === false;
441
+ const editorClassName = `html-editor html-editor--${variant} ${isLibraryMode ? 'html-editor--library-mode' : ''} ${className}`.trim();
442
+
437
443
  return (
438
444
  <EditorProvider value={contextValue}>
439
- <div className={`html-editor html-editor--${variant} ${className}`} {...props}>
445
+ <div className={editorClassName} {...props}>
440
446
  {/* Editor Toolbar - Conditional based on variant */}
441
447
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
442
448
  <EditorToolbar
@@ -504,7 +510,7 @@ const HTMLEditor = ({
504
510
  width="90vw"
505
511
  className="html-editor-fullscreen-modal"
506
512
  >
507
- <CapRow className="html-editor-fullscreen">
513
+ <CapRow className={`html-editor-fullscreen html-editor-fullscreen--${variant} ${isLibraryMode ? 'html-editor-fullscreen--library-mode' : ''}`}>
508
514
  {/* Editor Toolbar - Conditional based on variant */}
509
515
  {variant === HTML_EDITOR_VARIANTS.EMAIL ? (
510
516
  <EditorToolbar
@@ -596,6 +602,7 @@ HTMLEditor.propTypes = {
596
602
  onContextChange: PropTypes.func, // Deprecated: use globalActions instead
597
603
  globalActions: PropTypes.object,
598
604
  isLiquidEnabled: PropTypes.bool, // Controls Liquid tab visibility in validation
605
+ isFullMode: PropTypes.bool, // Full mode vs library mode
599
606
  };
600
607
 
601
608
  HTMLEditor.defaultProps = {
@@ -623,6 +630,7 @@ HTMLEditor.defaultProps = {
623
630
  onContextChange: null,
624
631
  globalActions: null, // Redux actions for API calls
625
632
  isLiquidEnabled: false,
633
+ isFullMode: true, // Default to full mode
626
634
  };
627
635
 
628
636
  // Export with forwardRef to allow direct access to CodeEditorPane via ref
@@ -5,39 +5,71 @@
5
5
  */
6
6
 
7
7
  // Import Cap UI variables (correct path)
8
- @import '~@capillarytech/cap-ui-library/styles/_variables.scss';
8
+ @import "~@capillarytech/cap-ui-library/styles/_variables.scss";
9
9
 
10
10
  // Fullscreen modal header styling - using proper CSS specificity instead of !important
11
11
  .html-editor-fullscreen {
12
- .html-editor__header--fullscreen {
13
- display: flex;
14
- align-items: center;
15
- background-color: $CAP_G10;
16
- border-bottom: 0.0625rem solid $CAP_G08;
17
- padding-right: 1rem;
12
+ // INAPP variant only: Fullscreen header styling
13
+ &--inapp {
14
+ .html-editor__header--fullscreen {
15
+ display: flex;
16
+ align-items: center;
17
+ background-color: $CAP_G10;
18
+ border-bottom: 0.0625rem solid $CAP_G08;
19
+ padding-right: 1rem;
18
20
 
19
- // Device toggle takes available space
20
- .device-toggle {
21
- flex: 1;
22
- }
21
+ // Device toggle takes available space
22
+ .device-toggle {
23
+ flex: 1;
24
+ }
23
25
 
24
- // Toolbar styling in fullscreen mode
25
- .editor-toolbar {
26
- background-color: transparent;
27
- border-bottom: none;
28
- padding: 0;
29
- min-height: 3.25rem;
30
- height: 3.25rem;
31
- position: relative;
26
+ // Toolbar styling in fullscreen mode - INAPP variant only
27
+ .editor-toolbar {
28
+ background-color: transparent;
29
+ border-bottom: none;
30
+ padding: 0;
31
+ min-height: 3.25rem;
32
+ height: 3.25rem;
33
+ position: relative;
32
34
 
33
- // Right-align toolbar actions
34
- &__right {
35
- margin-left: auto;
35
+ // Right-align toolbar actions
36
+ &__right {
37
+ margin-left: auto;
38
+ }
39
+ }
40
+ }
41
+
42
+ // Library mode: Position absolute for toolbar in fullscreen (INAPP variant only)
43
+ // Using higher specificity to override _fullscreenModal.scss styles
44
+ &.html-editor-fullscreen--library-mode {
45
+ .html-editor__header--fullscreen {
46
+ .editor-toolbar,
47
+ .editor-toolbar.editor-toolbar,
48
+ .ant-layout-header.editor-toolbar {
49
+ position: absolute;
50
+ }
36
51
  }
37
52
  }
38
53
  }
39
54
  }
40
55
 
56
+ // Additional library mode overrides for fullscreen with maximum specificity
57
+ // These match the high-specificity selectors in _fullscreenModal.scss
58
+ .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
59
+ .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
60
+ .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
61
+ .html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
62
+ .html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
63
+ .html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
64
+ .ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
65
+ .ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
66
+ .ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
67
+ .ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
68
+ .ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
69
+ .ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar {
70
+ position: absolute;
71
+ }
72
+
41
73
  .html-editor {
42
74
  height: 37.5rem; // Fixed height instead of 100vh (600px = 37.5rem)
43
75
  max-height: 80vh; // Responsive maximum height
@@ -49,6 +81,23 @@
49
81
  border-radius: 0.25rem;
50
82
  overflow: hidden;
51
83
 
84
+ // Library mode: Apply specific styles when isFullMode is false
85
+ &.html-editor--library-mode {
86
+ .preview-pane {
87
+ max-height: calc(95vh - 5%);
88
+
89
+ &__content {
90
+ height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
91
+ max-height: calc(95vh - 50%);
92
+ }
93
+
94
+ iframe {
95
+ height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
96
+ max-height: calc(95vh - 50%);
97
+ }
98
+ }
99
+ }
100
+
52
101
  &.fullscreen {
53
102
  position: fixed;
54
103
  top: 0;
@@ -124,8 +173,19 @@
124
173
  }
125
174
  }
126
175
  }
127
- }
128
176
 
177
+ // Library mode: Position absolute for toolbar in InApp variant
178
+ // Using higher specificity to override default position: relative
179
+ &.html-editor--library-mode {
180
+ .html-editor__header {
181
+ .editor-toolbar,
182
+ .editor-toolbar.editor-toolbar,
183
+ .ant-layout-header.editor-toolbar {
184
+ position: absolute;
185
+ }
186
+ }
187
+ }
188
+ }
129
189
 
130
190
  // Responsive design
131
191
  @media (max-width: 768px) {
@@ -231,6 +291,23 @@
231
291
  }
232
292
  }
233
293
 
294
+ // Fullscreen modal library mode styles (outside .html-editor block since fullscreen is a separate element)
295
+ .html-editor-fullscreen--library-mode {
296
+ .preview-pane {
297
+ max-height: calc(95vh - 5%);
298
+
299
+ &__content {
300
+ height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
301
+ max-height: calc(95vh - 50%);
302
+ }
303
+
304
+ iframe {
305
+ height: calc(95vh - 5%); // Same as editor pane - maximize to full available height
306
+ max-height: calc(95vh - 50%);
307
+ }
308
+ }
309
+ }
310
+
234
311
  // Animation classes for smooth transitions
235
312
  .html-editor-transition {
236
313
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
@@ -15,6 +15,7 @@
15
15
  border-radius: 0.25rem 0.25rem 0 0;
16
16
  box-sizing: border-box;
17
17
  flex-shrink: 0;
18
+ position: relative;
18
19
 
19
20
  &__left {
20
21
  display: flex;
@@ -110,4 +111,12 @@
110
111
  display: none;
111
112
  }
112
113
  }
114
+ }
115
+
116
+ // Library mode override for InApp variant - Position absolute for toolbar
117
+ // These selectors target the toolbar when inside library mode InApp variant
118
+ .html-editor--inapp.html-editor--library-mode .html-editor__header .editor-toolbar,
119
+ .html-editor--inapp.html-editor--library-mode .html-editor__header .editor-toolbar.editor-toolbar,
120
+ .html-editor--inapp.html-editor--library-mode .html-editor__header .ant-layout-header.editor-toolbar {
121
+ position: absolute;
113
122
  }
@@ -55,4 +55,25 @@ body .ant-modal-mask+.ant-modal-wrap .ant-modal.html-editor-fullscreen-modal .an
55
55
  margin-left: auto;
56
56
  }
57
57
  }
58
+ }
59
+
60
+ // Library mode overrides for INAPP variant - Position absolute for toolbar
61
+ // These selectors match the high-specificity patterns above but include library-mode class
62
+ .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
63
+ .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
64
+ .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
65
+ .html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
66
+ .html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
67
+ .html-editor.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
68
+ .html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .editor-toolbar,
69
+ .html-editor__header.html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .editor-toolbar,
70
+ .html-editor__header.html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .editor-toolbar.editor-toolbar,
71
+ .html-editor__header.html-editor__header--fullscreen.html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .ant-layout-header.editor-toolbar,
72
+ .ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
73
+ .ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
74
+ .ant-modal .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar,
75
+ .ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar,
76
+ .ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .editor-toolbar.editor-toolbar,
77
+ .ant-modal-content .html-editor-fullscreen--inapp.html-editor-fullscreen--library-mode .html-editor__header--fullscreen .ant-layout-header.editor-toolbar {
78
+ position: absolute;
58
79
  }
@@ -48,33 +48,6 @@ describe('ValidationErrorDisplay', () => {
48
48
  expect(screen.getByText('Invalid attribute')).toBeInTheDocument();
49
49
  });
50
50
 
51
- it('renders liquid errors separately', () => {
52
- const mockValidation = {
53
- getAllIssues: () => [
54
- {
55
- message: 'Invalid liquid syntax', severity: 'error', source: 'liquid-validator', line: 3,
56
- },
57
- {
58
- message: 'HTML error', severity: 'error', source: 'htmlhint', line: 10,
59
- },
60
- ],
61
- isClean: () => false,
62
- isValidating: false,
63
- };
64
-
65
- const { container } = render(
66
- <TestWrapper>
67
- <ValidationErrorDisplay validation={mockValidation} />
68
- </TestWrapper>
69
- );
70
-
71
- // Check for validation-error-display wrapper
72
- expect(container.querySelector('.validation-error-display')).toBeInTheDocument();
73
- // Check that both errors are rendered
74
- expect(screen.getByText('Invalid liquid syntax')).toBeInTheDocument();
75
- expect(screen.getByText('HTML error')).toBeInTheDocument();
76
- });
77
-
78
51
  it('does not render when validation has no errors', () => {
79
52
  const mockValidation = {
80
53
  getAllIssues: () => [],
@@ -961,6 +961,7 @@ const EmailHTMLEditor = (props) => {
961
961
  moduleFilterEnabled={location?.query?.type !== EMBEDDED}
962
962
  onTagContextChange={handleOnTagsContextChange}
963
963
  isLiquidEnabled={isLiquidEnabled}
964
+ isFullMode={isFullMode}
964
965
  />
965
966
  </CapColumn>
966
967
  </CapRow>
@@ -1235,6 +1235,7 @@ export const InApp = (props) => {
1235
1235
  onContextChange={handleOnTagsContextChange}
1236
1236
  // Pass validation errors to HTMLEditor for display
1237
1237
  errors={errorMessage}
1238
+ isFullMode={isFullMode}
1238
1239
  data-test="inapp-html-editor"
1239
1240
  style={{ width: '138%' }}
1240
1241
  />
@@ -1,239 +0,0 @@
1
- import React from 'react';
2
- import { render, fireEvent, within, screen } from '@testing-library/react';
3
- import '@testing-library/jest-dom';
4
- import { IntlProvider } from 'react-intl';
5
- import { Provider } from 'react-redux';
6
- import { configureStore } from '@capillarytech/vulcan-react-sdk/utils'
7
- import { initialReducer } from '../../../initialReducer';
8
- import history from '../../../utils/history';
9
- import EmailWrapperView from '../components/EmailWrapperView';
10
- import { EmailWrapperViewMockProps } from '../mockdata/mockdata';
11
- import { EMAIL_CREATE_MODES } from '../constants';
12
-
13
- // Mock pathConfig before history import - must be a string, not undefined
14
- jest.mock('../../../config/path', () => ({
15
- publicPath: '/creatives/ui',
16
- engagePlusPublicPath: '/campaigns/ui',
17
- }));
18
-
19
- // Mock window.location for createBrowserHistory
20
- const originalLocation = window.location;
21
- beforeAll(() => {
22
- delete window.location;
23
- window.location = {
24
- ...originalLocation,
25
- pathname: '/',
26
- search: '',
27
- hash: '',
28
- href: 'http://localhost/',
29
- replace: jest.fn(),
30
- };
31
- });
32
-
33
- afterAll(() => {
34
- window.location = originalLocation;
35
- });
36
-
37
- // This mock needs to be before any imports
38
- jest.mock('../../../v2Containers/Email', () => ({
39
- __esModule: true,
40
- default: () => <div data-testid="email-create-container" />,
41
- }));
42
-
43
- // Mock react-router
44
- jest.mock('react-router-dom', () => ({
45
- ...jest.requireActual('react-router-dom'),
46
- useHistory: () => ({
47
- push: jest.fn(),
48
- replace: jest.fn(),
49
- go: jest.fn(),
50
- goBack: jest.fn(),
51
- goForward: jest.fn(),
52
- location: { search: '' }
53
- }),
54
- }));
55
-
56
- // Mock redux-auth-wrapper
57
- jest.mock('redux-auth-wrapper/history4/redirect', () => ({
58
- connectedRouterRedirect: () => (Component) => Component,
59
- }));
60
-
61
- // Mock react-router
62
- jest.mock('connected-react-router', () => ({
63
- push: jest.fn(),
64
- replace: jest.fn(),
65
- go: jest.fn(),
66
- goBack: jest.fn(),
67
- goForward: jest.fn(),
68
- }));
69
-
70
- // Mock the saga injector
71
- jest.mock('@capillarytech/vulcan-react-sdk/utils/injectSaga', () => ({
72
- __esModule: true,
73
- default: () => (Component) => Component,
74
- }));
75
-
76
-
77
- const initialState = {
78
- cap: { loading: false, error: null },
79
- router: { location: { pathname: '/' } }
80
- };
81
-
82
- describe('EmailWrapperView', () => {
83
- let store;
84
-
85
- beforeAll(() => {
86
- // Use the real history (pathConfig is mocked, so it should work)
87
- store = configureStore(initialState, initialReducer, history);
88
- });
89
-
90
- const renderWithProviders = (component) => {
91
- return render(
92
- <Provider store={store}>
93
- <IntlProvider locale="en" messages={{}}>
94
- {component}
95
- </IntlProvider>
96
- </Provider>
97
- );
98
- };
99
-
100
- it('renders mode selection UI when step is modeSelection', () => {
101
- const props = {
102
- ...EmailWrapperViewMockProps,
103
- step: 'modeSelection',
104
- };
105
-
106
- const { getByText } = renderWithProviders(<EmailWrapperView {...props} />);
107
- expect(getByText('Create using editor')).toBeInTheDocument();
108
- expect(getByText('Upload zip file')).toBeInTheDocument();
109
- });
110
-
111
- it('shows template name input in full mode', () => {
112
- const props = {
113
- ...EmailWrapperViewMockProps,
114
- isFullMode: true,
115
- step: 'modeSelection',
116
- };
117
-
118
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
119
- const input = container.querySelector('.ant-input');
120
- expect(input).toBeInTheDocument();
121
- expect(input.value).toBe(props.templateName);
122
- });
123
-
124
- it('handles template name changes', () => {
125
- const props = {
126
- ...EmailWrapperViewMockProps,
127
- isFullMode: true,
128
- step: 'modeSelection',
129
- onTemplateNameChange: jest.fn(),
130
- };
131
-
132
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
133
- const input = container.querySelector('.ant-input');
134
- expect(input).toBeInTheDocument();
135
-
136
- // Create a change event and fire it
137
- fireEvent.change(input, { target: { value: 'New Template Name' } });
138
-
139
- // Just verify that the function was called
140
- expect(props.onTemplateNameChange).toHaveBeenCalled();
141
- });
142
-
143
- it('shows upload button when in upload mode', () => {
144
- const props = {
145
- ...EmailWrapperViewMockProps,
146
- step: 'modeSelection',
147
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
148
- uploadButtonLabel: 'Upload',
149
- };
150
-
151
- const { getByText, container } = renderWithProviders(<EmailWrapperView {...props} />);
152
-
153
- // Find the "Upload zip file" section
154
- const modeSelectionSection = getByText('Upload zip file').closest('div');
155
-
156
- // Find any button in the upload section
157
- const uploadButton = container.querySelector('.ant-btn');
158
-
159
- // Verify that we found an upload button
160
- expect(uploadButton).toBeInTheDocument();
161
- expect(uploadButton.textContent).toContain('Upload');
162
- });
163
-
164
- it('shows loading spinner when uploading', () => {
165
- const props = {
166
- ...EmailWrapperViewMockProps,
167
- isUploading: true,
168
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
169
- step: 'modeSelection',
170
- };
171
-
172
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
173
- const spinner = container.querySelector('.ant-spin');
174
- expect(spinner).toBeInTheDocument();
175
- });
176
-
177
- it('shows content creation UI when not in mode selection', () => {
178
- const props = {
179
- ...EmailWrapperViewMockProps,
180
- step: 'contentCreation',
181
- isShowEmailCreate: true,
182
- };
183
-
184
- const { getByTestId } = renderWithProviders(<EmailWrapperView {...props} />);
185
- expect(getByTestId('email-create-container')).toBeInTheDocument();
186
- });
187
-
188
- it('disables upload button when template name is empty in full mode', () => {
189
- const props = {
190
- ...EmailWrapperViewMockProps,
191
- isFullMode: true,
192
- step: 'modeSelection',
193
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
194
- templateName: '',
195
- isTemplateNameEmpty: true,
196
- uploadButtonLabel: 'Upload',
197
- };
198
-
199
- const { getByText, container } = renderWithProviders(<EmailWrapperView {...props} />);
200
-
201
- // Find the Upload section
202
- getByText('Upload zip file');
203
-
204
- // Find button in the container
205
- const uploadButton = container.querySelector('.ant-btn');
206
-
207
- // Check if the button is disabled
208
- expect(uploadButton).toBeDisabled();
209
- });
210
-
211
- it('shows error message when template name is empty during upload', () => {
212
- const props = {
213
- ...EmailWrapperViewMockProps,
214
- isFullMode: true,
215
- step: 'modeSelection',
216
- emailCreateMode: EMAIL_CREATE_MODES.UPLOAD,
217
- templateName: '',
218
- isTemplateNameEmpty: true,
219
- // Add errorMessage directly to the props
220
- errorMessage: 'Please enter template name',
221
- intl: {
222
- formatMessage: () => 'Please enter template name',
223
- },
224
- };
225
-
226
- const { container } = renderWithProviders(<EmailWrapperView {...props} />);
227
-
228
- // Look for error feedback in the Ant Design form
229
- const errorElement = container.querySelector('.ant-form-item-explain');
230
-
231
- if (errorElement) {
232
- expect(errorElement.textContent).toContain('Please enter template name');
233
- } else {
234
- // If the standard error element is not found, look for any element containing the error text
235
- const allText = container.textContent;
236
- expect(allText).toContain('Please enter template name');
237
- }
238
- });
239
- });