@capillarytech/creatives-library 8.0.208 → 8.0.210
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/package.json +16 -2
- package/v2Components/HtmlEditor/HTMLEditor.js +508 -0
- package/v2Components/HtmlEditor/__tests__/HTMLEditor.test.js +1809 -0
- package/v2Components/HtmlEditor/__tests__/index.lazy.test.js +532 -0
- package/v2Components/HtmlEditor/_htmlEditor.scss +304 -0
- package/v2Components/HtmlEditor/_index.lazy.scss +26 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/_codeEditorPane.scss +376 -0
- package/v2Components/HtmlEditor/components/CodeEditorPane/index.js +331 -0
- package/v2Components/HtmlEditor/components/DeviceToggle/__tests__/index.test.js +314 -0
- package/v2Components/HtmlEditor/components/DeviceToggle/_deviceToggle.scss +244 -0
- package/v2Components/HtmlEditor/components/DeviceToggle/index.js +111 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/PreviewModeGroup.js +72 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/__tests__/PreviewModeGroup.test.js +1594 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/_editorToolbar.scss +113 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/_previewModeGroup.scss +82 -0
- package/v2Components/HtmlEditor/components/EditorToolbar/index.js +115 -0
- package/v2Components/HtmlEditor/components/FullscreenModal/_fullscreenModal.scss +57 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/ContentOverlay.js +90 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/DeviceFrame.js +60 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/LayoutSelector.js +58 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/ContentOverlay.test.js +403 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/DeviceFrame.test.js +424 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/__tests__/LayoutSelector.test.js +248 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/_inAppPreviewPane.scss +253 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/constants.js +104 -0
- package/v2Components/HtmlEditor/components/InAppPreviewPane/index.js +179 -0
- package/v2Components/HtmlEditor/components/PreviewPane/_previewPane.scss +220 -0
- package/v2Components/HtmlEditor/components/PreviewPane/index.js +229 -0
- package/v2Components/HtmlEditor/components/SplitContainer/SplitContainer.js +276 -0
- package/v2Components/HtmlEditor/components/SplitContainer/__tests__/SplitContainer.test.js +295 -0
- package/v2Components/HtmlEditor/components/SplitContainer/_splitContainer.scss +257 -0
- package/v2Components/HtmlEditor/components/SplitContainer/index.js +7 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/__tests__/index.test.js +152 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/_validationErrorDisplay.scss +31 -0
- package/v2Components/HtmlEditor/components/ValidationErrorDisplay/index.js +70 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/__tests__/index.test.js +98 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/_validationPanel.scss +311 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/index.js +297 -0
- package/v2Components/HtmlEditor/components/ValidationPanel/messages.js +57 -0
- package/v2Components/HtmlEditor/components/common/EditorContext.js +84 -0
- package/v2Components/HtmlEditor/components/common/__tests__/EditorContext.test.js +660 -0
- package/v2Components/HtmlEditor/constants.js +241 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useEditorContent.test.js +450 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useInAppContent.test.js +785 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useLayoutState.test.js +580 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.enhanced.test.js +768 -0
- package/v2Components/HtmlEditor/hooks/__tests__/useValidation.test.js +590 -0
- package/v2Components/HtmlEditor/hooks/useEditorContent.js +274 -0
- package/v2Components/HtmlEditor/hooks/useInAppContent.js +407 -0
- package/v2Components/HtmlEditor/hooks/useLayoutState.js +247 -0
- package/v2Components/HtmlEditor/hooks/useValidation.js +325 -0
- package/v2Components/HtmlEditor/index.js +29 -0
- package/v2Components/HtmlEditor/index.lazy.js +114 -0
- package/v2Components/HtmlEditor/messages.js +389 -0
- package/v2Components/HtmlEditor/utils/__tests__/contentSanitizer.test.js +741 -0
- package/v2Components/HtmlEditor/utils/__tests__/htmlValidator.enhanced.test.js +1042 -0
- package/v2Components/HtmlEditor/utils/__tests__/liquidTemplateSupport.test.js +515 -0
- package/v2Components/HtmlEditor/utils/__tests__/properSyntaxHighlighting.test.js +473 -0
- package/v2Components/HtmlEditor/utils/__tests__/simplePerformance.test.js +1109 -0
- package/v2Components/HtmlEditor/utils/__tests__/validationAdapter.test.js +240 -0
- package/v2Components/HtmlEditor/utils/contentSanitizer.js +433 -0
- package/v2Components/HtmlEditor/utils/htmlValidator.js +508 -0
- package/v2Components/HtmlEditor/utils/liquidTemplateSupport.js +524 -0
- package/v2Components/HtmlEditor/utils/properSyntaxHighlighting.js +163 -0
- package/v2Components/HtmlEditor/utils/simplePerformance.js +145 -0
- package/v2Components/HtmlEditor/utils/validationAdapter.js +130 -0
- package/v2Containers/EmailWrapper/components/HTMLEditorTesting.js +200 -0
- package/v2Containers/EmailWrapper/components/__tests__/HTMLEditorTesting.test.js +545 -0
- package/v2Containers/EmailWrapper/index.js +8 -1
- package/v2Containers/Rcs/index.js +2 -0
- package/v2Containers/Templates/constants.js +8 -0
- package/v2Containers/Templates/index.js +56 -28
- package/v2Containers/Templates/tests/__snapshots__/index.test.js.snap +5 -14
- package/v2Containers/Whatsapp/index.js +1 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EditorContext Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for EditorContext, EditorProvider, useEditorContext, and withEditorContext
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { render, screen } from '@testing-library/react';
|
|
9
|
+
import '@testing-library/jest-dom';
|
|
10
|
+
import EditorContext, {
|
|
11
|
+
EditorProvider,
|
|
12
|
+
useEditorContext,
|
|
13
|
+
withEditorContext
|
|
14
|
+
} from '../EditorContext';
|
|
15
|
+
|
|
16
|
+
describe('EditorContext', () => {
|
|
17
|
+
// Mock editor context value
|
|
18
|
+
const createMockEditorContext = (overrides = {}) => ({
|
|
19
|
+
content: {
|
|
20
|
+
html: '<p>Test</p>',
|
|
21
|
+
updateContent: jest.fn(),
|
|
22
|
+
...overrides.content
|
|
23
|
+
},
|
|
24
|
+
layout: {
|
|
25
|
+
splitSize: 50,
|
|
26
|
+
viewMode: 'split',
|
|
27
|
+
...overrides.layout
|
|
28
|
+
},
|
|
29
|
+
validation: {
|
|
30
|
+
isValid: true,
|
|
31
|
+
errors: [],
|
|
32
|
+
...overrides.validation
|
|
33
|
+
},
|
|
34
|
+
preferences: {
|
|
35
|
+
theme: 'light',
|
|
36
|
+
autoSave: true,
|
|
37
|
+
...overrides.preferences
|
|
38
|
+
},
|
|
39
|
+
editorRef: { current: null },
|
|
40
|
+
handleLabelInsert: jest.fn(),
|
|
41
|
+
handleSave: jest.fn(),
|
|
42
|
+
readOnly: false,
|
|
43
|
+
...overrides
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('EditorContext', () => {
|
|
47
|
+
it('has correct display name', () => {
|
|
48
|
+
expect(EditorContext.displayName).toBe('EditorContext');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('is created as a context object', () => {
|
|
52
|
+
expect(EditorContext).toBeDefined();
|
|
53
|
+
expect(EditorContext.Provider).toBeDefined();
|
|
54
|
+
expect(EditorContext.Consumer).toBeDefined();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('EditorProvider', () => {
|
|
59
|
+
it('renders children correctly', () => {
|
|
60
|
+
const mockValue = createMockEditorContext();
|
|
61
|
+
|
|
62
|
+
render(
|
|
63
|
+
<EditorProvider value={mockValue}>
|
|
64
|
+
<div data-testid="child">Test Child</div>
|
|
65
|
+
</EditorProvider>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
expect(screen.getByTestId('child')).toBeInTheDocument();
|
|
69
|
+
expect(screen.getByTestId('child')).toHaveTextContent('Test Child');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('renders multiple children', () => {
|
|
73
|
+
const mockValue = createMockEditorContext();
|
|
74
|
+
|
|
75
|
+
render(
|
|
76
|
+
<EditorProvider value={mockValue}>
|
|
77
|
+
<div data-testid="child-1">Child 1</div>
|
|
78
|
+
<div data-testid="child-2">Child 2</div>
|
|
79
|
+
<div data-testid="child-3">Child 3</div>
|
|
80
|
+
</EditorProvider>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(screen.getByTestId('child-1')).toBeInTheDocument();
|
|
84
|
+
expect(screen.getByTestId('child-2')).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByTestId('child-3')).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('provides context value to children', () => {
|
|
89
|
+
const mockValue = createMockEditorContext();
|
|
90
|
+
|
|
91
|
+
const TestComponent = () => {
|
|
92
|
+
const context = useEditorContext();
|
|
93
|
+
return <div data-testid="context-content">{context.content.html}</div>;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
render(
|
|
97
|
+
<EditorProvider value={mockValue}>
|
|
98
|
+
<TestComponent />
|
|
99
|
+
</EditorProvider>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
expect(screen.getByTestId('context-content')).toHaveTextContent('<p>Test</p>');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('memoizes context value', () => {
|
|
106
|
+
const mockValue = createMockEditorContext();
|
|
107
|
+
let renderCount = 0;
|
|
108
|
+
|
|
109
|
+
const TestComponent = () => {
|
|
110
|
+
useEditorContext();
|
|
111
|
+
renderCount++;
|
|
112
|
+
return <div>Rendered {renderCount} times</div>;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const { rerender } = render(
|
|
116
|
+
<EditorProvider value={mockValue}>
|
|
117
|
+
<TestComponent />
|
|
118
|
+
</EditorProvider>
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
expect(renderCount).toBe(1);
|
|
122
|
+
|
|
123
|
+
// Rerender with same value
|
|
124
|
+
rerender(
|
|
125
|
+
<EditorProvider value={mockValue}>
|
|
126
|
+
<TestComponent />
|
|
127
|
+
</EditorProvider>
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Should not trigger re-render due to memoization
|
|
131
|
+
expect(renderCount).toBe(2); // Context provider itself re-renders
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('updates context when value changes', () => {
|
|
135
|
+
const mockValue1 = createMockEditorContext({ content: { html: '<p>Test 1</p>' } });
|
|
136
|
+
const mockValue2 = createMockEditorContext({ content: { html: '<p>Test 2</p>' } });
|
|
137
|
+
|
|
138
|
+
const TestComponent = () => {
|
|
139
|
+
const context = useEditorContext();
|
|
140
|
+
return <div data-testid="content">{context.content.html}</div>;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const { rerender } = render(
|
|
144
|
+
<EditorProvider value={mockValue1}>
|
|
145
|
+
<TestComponent />
|
|
146
|
+
</EditorProvider>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(screen.getByTestId('content')).toHaveTextContent('<p>Test 1</p>');
|
|
150
|
+
|
|
151
|
+
rerender(
|
|
152
|
+
<EditorProvider value={mockValue2}>
|
|
153
|
+
<TestComponent />
|
|
154
|
+
</EditorProvider>
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect(screen.getByTestId('content')).toHaveTextContent('<p>Test 2</p>');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('handles nested providers', () => {
|
|
161
|
+
const outerValue = createMockEditorContext({ content: { html: 'Outer' } });
|
|
162
|
+
const innerValue = createMockEditorContext({ content: { html: 'Inner' } });
|
|
163
|
+
|
|
164
|
+
const TestComponent = ({ testId }) => {
|
|
165
|
+
const context = useEditorContext();
|
|
166
|
+
return <div data-testid={testId}>{context.content.html}</div>;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
render(
|
|
170
|
+
<EditorProvider value={outerValue}>
|
|
171
|
+
<TestComponent testId="outer" />
|
|
172
|
+
<EditorProvider value={innerValue}>
|
|
173
|
+
<TestComponent testId="inner" />
|
|
174
|
+
</EditorProvider>
|
|
175
|
+
</EditorProvider>
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
expect(screen.getByTestId('outer')).toHaveTextContent('Outer');
|
|
179
|
+
expect(screen.getByTestId('inner')).toHaveTextContent('Inner');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('handles null children', () => {
|
|
183
|
+
const mockValue = createMockEditorContext();
|
|
184
|
+
|
|
185
|
+
expect(() => {
|
|
186
|
+
render(
|
|
187
|
+
<EditorProvider value={mockValue}>
|
|
188
|
+
{null}
|
|
189
|
+
</EditorProvider>
|
|
190
|
+
);
|
|
191
|
+
}).not.toThrow();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('handles fragment children', () => {
|
|
195
|
+
const mockValue = createMockEditorContext();
|
|
196
|
+
|
|
197
|
+
render(
|
|
198
|
+
<EditorProvider value={mockValue}>
|
|
199
|
+
<>
|
|
200
|
+
<div data-testid="fragment-1">Fragment 1</div>
|
|
201
|
+
<div data-testid="fragment-2">Fragment 2</div>
|
|
202
|
+
</>
|
|
203
|
+
</EditorProvider>
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
expect(screen.getByTestId('fragment-1')).toBeInTheDocument();
|
|
207
|
+
expect(screen.getByTestId('fragment-2')).toBeInTheDocument();
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
describe('useEditorContext', () => {
|
|
212
|
+
it('returns context value when used within EditorProvider', () => {
|
|
213
|
+
const mockValue = createMockEditorContext();
|
|
214
|
+
|
|
215
|
+
const TestComponent = () => {
|
|
216
|
+
const context = useEditorContext();
|
|
217
|
+
return <div data-testid="test">{JSON.stringify(context.content.html)}</div>;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
render(
|
|
221
|
+
<EditorProvider value={mockValue}>
|
|
222
|
+
<TestComponent />
|
|
223
|
+
</EditorProvider>
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
expect(screen.getByTestId('test')).toHaveTextContent('<p>Test</p>');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('throws error when used outside of EditorProvider', () => {
|
|
230
|
+
// Suppress console.error for this test
|
|
231
|
+
const originalError = console.error;
|
|
232
|
+
console.error = jest.fn();
|
|
233
|
+
|
|
234
|
+
const TestComponent = () => {
|
|
235
|
+
useEditorContext();
|
|
236
|
+
return <div>Should not render</div>;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
expect(() => {
|
|
240
|
+
render(<TestComponent />);
|
|
241
|
+
}).toThrow('useEditorContext must be used within an EditorProvider');
|
|
242
|
+
|
|
243
|
+
console.error = originalError;
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('provides access to all context properties', () => {
|
|
247
|
+
const mockValue = createMockEditorContext();
|
|
248
|
+
let capturedContext;
|
|
249
|
+
|
|
250
|
+
const TestComponent = () => {
|
|
251
|
+
capturedContext = useEditorContext();
|
|
252
|
+
return <div>Test</div>;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
render(
|
|
256
|
+
<EditorProvider value={mockValue}>
|
|
257
|
+
<TestComponent />
|
|
258
|
+
</EditorProvider>
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
expect(capturedContext).toHaveProperty('content');
|
|
262
|
+
expect(capturedContext).toHaveProperty('layout');
|
|
263
|
+
expect(capturedContext).toHaveProperty('validation');
|
|
264
|
+
expect(capturedContext).toHaveProperty('preferences');
|
|
265
|
+
expect(capturedContext).toHaveProperty('editorRef');
|
|
266
|
+
expect(capturedContext).toHaveProperty('handleLabelInsert');
|
|
267
|
+
expect(capturedContext).toHaveProperty('handleSave');
|
|
268
|
+
expect(capturedContext).toHaveProperty('readOnly');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('provides access to nested context properties', () => {
|
|
272
|
+
const mockValue = createMockEditorContext({
|
|
273
|
+
content: { html: '<p>Test</p>', updateContent: jest.fn() },
|
|
274
|
+
layout: { splitSize: 60, viewMode: 'code' }
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const TestComponent = () => {
|
|
278
|
+
const { content, layout } = useEditorContext();
|
|
279
|
+
return (
|
|
280
|
+
<div>
|
|
281
|
+
<div data-testid="html">{content.html}</div>
|
|
282
|
+
<div data-testid="split-size">{layout.splitSize}</div>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
render(
|
|
288
|
+
<EditorProvider value={mockValue}>
|
|
289
|
+
<TestComponent />
|
|
290
|
+
</EditorProvider>
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
expect(screen.getByTestId('html')).toHaveTextContent('<p>Test</p>');
|
|
294
|
+
expect(screen.getByTestId('split-size')).toHaveTextContent('60');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('can call context functions', () => {
|
|
298
|
+
const mockHandleSave = jest.fn();
|
|
299
|
+
const mockValue = createMockEditorContext({ handleSave: mockHandleSave });
|
|
300
|
+
|
|
301
|
+
const TestComponent = () => {
|
|
302
|
+
const { handleSave } = useEditorContext();
|
|
303
|
+
return <button onClick={handleSave} data-testid="save-btn">Save</button>;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
render(
|
|
307
|
+
<EditorProvider value={mockValue}>
|
|
308
|
+
<TestComponent />
|
|
309
|
+
</EditorProvider>
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const saveButton = screen.getByTestId('save-btn');
|
|
313
|
+
saveButton.click();
|
|
314
|
+
|
|
315
|
+
expect(mockHandleSave).toHaveBeenCalledTimes(1);
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe('withEditorContext HOC', () => {
|
|
320
|
+
it('wraps component and injects editor context', () => {
|
|
321
|
+
const mockValue = createMockEditorContext();
|
|
322
|
+
|
|
323
|
+
const TestComponent = ({ editorContext }) => (
|
|
324
|
+
<div data-testid="content">{editorContext.content.html}</div>
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const WrappedComponent = withEditorContext(TestComponent);
|
|
328
|
+
|
|
329
|
+
render(
|
|
330
|
+
<EditorProvider value={mockValue}>
|
|
331
|
+
<WrappedComponent />
|
|
332
|
+
</EditorProvider>
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
expect(screen.getByTestId('content')).toHaveTextContent('<p>Test</p>');
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('passes through original props', () => {
|
|
339
|
+
const mockValue = createMockEditorContext();
|
|
340
|
+
|
|
341
|
+
const TestComponent = ({ customProp, editorContext }) => (
|
|
342
|
+
<div>
|
|
343
|
+
<div data-testid="custom">{customProp}</div>
|
|
344
|
+
<div data-testid="context">{editorContext.content.html}</div>
|
|
345
|
+
</div>
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
const WrappedComponent = withEditorContext(TestComponent);
|
|
349
|
+
|
|
350
|
+
render(
|
|
351
|
+
<EditorProvider value={mockValue}>
|
|
352
|
+
<WrappedComponent customProp="Custom Value" />
|
|
353
|
+
</EditorProvider>
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
expect(screen.getByTestId('custom')).toHaveTextContent('Custom Value');
|
|
357
|
+
expect(screen.getByTestId('context')).toHaveTextContent('<p>Test</p>');
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('sets correct displayName with component name', () => {
|
|
361
|
+
const TestComponent = () => <div>Test</div>;
|
|
362
|
+
TestComponent.displayName = 'TestComponent';
|
|
363
|
+
|
|
364
|
+
const WrappedComponent = withEditorContext(TestComponent);
|
|
365
|
+
|
|
366
|
+
expect(WrappedComponent.displayName).toBe('withEditorContext(TestComponent)');
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('sets correct displayName with function name', () => {
|
|
370
|
+
function MyComponent() {
|
|
371
|
+
return <div>Test</div>;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const WrappedComponent = withEditorContext(MyComponent);
|
|
375
|
+
|
|
376
|
+
expect(WrappedComponent.displayName).toBe('withEditorContext(MyComponent)');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('handles components without displayName', () => {
|
|
380
|
+
const TestComponent = () => <div>Test</div>;
|
|
381
|
+
|
|
382
|
+
const WrappedComponent = withEditorContext(TestComponent);
|
|
383
|
+
|
|
384
|
+
expect(WrappedComponent.displayName).toContain('withEditorContext');
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('wrapped component can access all context properties', () => {
|
|
388
|
+
const mockValue = createMockEditorContext();
|
|
389
|
+
let capturedContext;
|
|
390
|
+
|
|
391
|
+
const TestComponent = ({ editorContext }) => {
|
|
392
|
+
capturedContext = editorContext;
|
|
393
|
+
return <div>Test</div>;
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const WrappedComponent = withEditorContext(TestComponent);
|
|
397
|
+
|
|
398
|
+
render(
|
|
399
|
+
<EditorProvider value={mockValue}>
|
|
400
|
+
<WrappedComponent />
|
|
401
|
+
</EditorProvider>
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
expect(capturedContext).toEqual(mockValue);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('wrapped component throws error outside provider', () => {
|
|
408
|
+
// Suppress console.error for this test
|
|
409
|
+
const originalError = console.error;
|
|
410
|
+
console.error = jest.fn();
|
|
411
|
+
|
|
412
|
+
const TestComponent = () => <div>Test</div>;
|
|
413
|
+
const WrappedComponent = withEditorContext(TestComponent);
|
|
414
|
+
|
|
415
|
+
expect(() => {
|
|
416
|
+
render(<WrappedComponent />);
|
|
417
|
+
}).toThrow('useEditorContext must be used within an EditorProvider');
|
|
418
|
+
|
|
419
|
+
console.error = originalError;
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('wrapped component updates when context changes', () => {
|
|
423
|
+
const mockValue1 = createMockEditorContext({ content: { html: 'Value 1' } });
|
|
424
|
+
const mockValue2 = createMockEditorContext({ content: { html: 'Value 2' } });
|
|
425
|
+
|
|
426
|
+
const TestComponent = ({ editorContext }) => (
|
|
427
|
+
<div data-testid="content">{editorContext.content.html}</div>
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const WrappedComponent = withEditorContext(TestComponent);
|
|
431
|
+
|
|
432
|
+
const { rerender } = render(
|
|
433
|
+
<EditorProvider value={mockValue1}>
|
|
434
|
+
<WrappedComponent />
|
|
435
|
+
</EditorProvider>
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
expect(screen.getByTestId('content')).toHaveTextContent('Value 1');
|
|
439
|
+
|
|
440
|
+
rerender(
|
|
441
|
+
<EditorProvider value={mockValue2}>
|
|
442
|
+
<WrappedComponent />
|
|
443
|
+
</EditorProvider>
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
expect(screen.getByTestId('content')).toHaveTextContent('Value 2');
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
describe('Integration Tests', () => {
|
|
451
|
+
it('multiple components can access same context', () => {
|
|
452
|
+
const mockValue = createMockEditorContext();
|
|
453
|
+
|
|
454
|
+
const Component1 = () => {
|
|
455
|
+
const { content } = useEditorContext();
|
|
456
|
+
return <div data-testid="comp-1">{content.html}</div>;
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const Component2 = () => {
|
|
460
|
+
const { layout } = useEditorContext();
|
|
461
|
+
return <div data-testid="comp-2">{layout.splitSize}</div>;
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
render(
|
|
465
|
+
<EditorProvider value={mockValue}>
|
|
466
|
+
<Component1 />
|
|
467
|
+
<Component2 />
|
|
468
|
+
</EditorProvider>
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
expect(screen.getByTestId('comp-1')).toHaveTextContent('<p>Test</p>');
|
|
472
|
+
expect(screen.getByTestId('comp-2')).toHaveTextContent('50');
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('combines useEditorContext with other hooks', () => {
|
|
476
|
+
const mockValue = createMockEditorContext();
|
|
477
|
+
|
|
478
|
+
const TestComponent = () => {
|
|
479
|
+
const { content } = useEditorContext();
|
|
480
|
+
const [count, setCount] = React.useState(0);
|
|
481
|
+
|
|
482
|
+
return (
|
|
483
|
+
<div>
|
|
484
|
+
<div data-testid="content">{content.html}</div>
|
|
485
|
+
<div data-testid="count">{count}</div>
|
|
486
|
+
<button onClick={() => setCount(count + 1)}>Increment</button>
|
|
487
|
+
</div>
|
|
488
|
+
);
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
render(
|
|
492
|
+
<EditorProvider value={mockValue}>
|
|
493
|
+
<TestComponent />
|
|
494
|
+
</EditorProvider>
|
|
495
|
+
);
|
|
496
|
+
|
|
497
|
+
expect(screen.getByTestId('content')).toHaveTextContent('<p>Test</p>');
|
|
498
|
+
expect(screen.getByTestId('count')).toHaveTextContent('0');
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('context functions are callable from nested components', () => {
|
|
502
|
+
const mockHandleSave = jest.fn();
|
|
503
|
+
const mockHandleLabelInsert = jest.fn();
|
|
504
|
+
const mockValue = createMockEditorContext({
|
|
505
|
+
handleSave: mockHandleSave,
|
|
506
|
+
handleLabelInsert: mockHandleLabelInsert
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const NestedComponent = () => {
|
|
510
|
+
const { handleSave, handleLabelInsert } = useEditorContext();
|
|
511
|
+
return (
|
|
512
|
+
<div>
|
|
513
|
+
<button onClick={handleSave} data-testid="save">Save</button>
|
|
514
|
+
<button onClick={() => handleLabelInsert('label')} data-testid="insert">Insert</button>
|
|
515
|
+
</div>
|
|
516
|
+
);
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const ParentComponent = () => (
|
|
520
|
+
<div>
|
|
521
|
+
<NestedComponent />
|
|
522
|
+
</div>
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
render(
|
|
526
|
+
<EditorProvider value={mockValue}>
|
|
527
|
+
<ParentComponent />
|
|
528
|
+
</EditorProvider>
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
screen.getByTestId('save').click();
|
|
532
|
+
screen.getByTestId('insert').click();
|
|
533
|
+
|
|
534
|
+
expect(mockHandleSave).toHaveBeenCalledTimes(1);
|
|
535
|
+
expect(mockHandleLabelInsert).toHaveBeenCalledWith('label');
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
describe('Edge Cases', () => {
|
|
540
|
+
it('handles undefined values in context gracefully', () => {
|
|
541
|
+
const mockValue = createMockEditorContext({
|
|
542
|
+
editorRef: undefined,
|
|
543
|
+
handleLabelInsert: undefined
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
const TestComponent = () => {
|
|
547
|
+
const context = useEditorContext();
|
|
548
|
+
return (
|
|
549
|
+
<div>
|
|
550
|
+
<div data-testid="ref">{String(context.editorRef)}</div>
|
|
551
|
+
<div data-testid="insert">{String(context.handleLabelInsert)}</div>
|
|
552
|
+
</div>
|
|
553
|
+
);
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
render(
|
|
557
|
+
<EditorProvider value={mockValue}>
|
|
558
|
+
<TestComponent />
|
|
559
|
+
</EditorProvider>
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
expect(screen.getByTestId('ref')).toHaveTextContent('undefined');
|
|
563
|
+
expect(screen.getByTestId('insert')).toHaveTextContent('undefined');
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('handles readOnly flag correctly', () => {
|
|
567
|
+
const mockValue = createMockEditorContext({ readOnly: true });
|
|
568
|
+
|
|
569
|
+
const TestComponent = () => {
|
|
570
|
+
const { readOnly } = useEditorContext();
|
|
571
|
+
return <div data-testid="readonly">{String(readOnly)}</div>;
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
render(
|
|
575
|
+
<EditorProvider value={mockValue}>
|
|
576
|
+
<TestComponent />
|
|
577
|
+
</EditorProvider>
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
expect(screen.getByTestId('readonly')).toHaveTextContent('true');
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
it('handles complex nested objects in context', () => {
|
|
584
|
+
const mockValue = createMockEditorContext({
|
|
585
|
+
validation: {
|
|
586
|
+
isValid: false,
|
|
587
|
+
errors: [
|
|
588
|
+
{ line: 1, message: 'Error 1' },
|
|
589
|
+
{ line: 2, message: 'Error 2' }
|
|
590
|
+
],
|
|
591
|
+
warnings: []
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
const TestComponent = () => {
|
|
596
|
+
const { validation } = useEditorContext();
|
|
597
|
+
return (
|
|
598
|
+
<div>
|
|
599
|
+
<div data-testid="valid">{String(validation.isValid)}</div>
|
|
600
|
+
<div data-testid="error-count">{validation.errors.length}</div>
|
|
601
|
+
</div>
|
|
602
|
+
);
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
render(
|
|
606
|
+
<EditorProvider value={mockValue}>
|
|
607
|
+
<TestComponent />
|
|
608
|
+
</EditorProvider>
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
expect(screen.getByTestId('valid')).toHaveTextContent('false');
|
|
612
|
+
expect(screen.getByTestId('error-count')).toHaveTextContent('2');
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
describe('PropTypes Validation', () => {
|
|
617
|
+
// Suppress console errors for these tests
|
|
618
|
+
const originalError = console.error;
|
|
619
|
+
beforeAll(() => {
|
|
620
|
+
console.error = jest.fn();
|
|
621
|
+
});
|
|
622
|
+
afterAll(() => {
|
|
623
|
+
console.error = originalError;
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
it('accepts valid context value shape', () => {
|
|
627
|
+
const mockValue = createMockEditorContext();
|
|
628
|
+
|
|
629
|
+
expect(() => {
|
|
630
|
+
render(
|
|
631
|
+
<EditorProvider value={mockValue}>
|
|
632
|
+
<div>Test</div>
|
|
633
|
+
</EditorProvider>
|
|
634
|
+
);
|
|
635
|
+
}).not.toThrow();
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
it('requires children prop', () => {
|
|
639
|
+
const mockValue = createMockEditorContext();
|
|
640
|
+
|
|
641
|
+
// This should work
|
|
642
|
+
expect(() => {
|
|
643
|
+
render(
|
|
644
|
+
<EditorProvider value={mockValue}>
|
|
645
|
+
<div>Child</div>
|
|
646
|
+
</EditorProvider>
|
|
647
|
+
);
|
|
648
|
+
}).not.toThrow();
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('requires value prop', () => {
|
|
652
|
+
// EditorProvider requires value prop with specific shape
|
|
653
|
+
// React will handle the missing prop validation
|
|
654
|
+
expect(EditorProvider.propTypes.value).toBeDefined();
|
|
655
|
+
expect(EditorProvider.propTypes.children).toBeDefined();
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
|