@dotcms/angular 0.0.1-alpha.38 → 0.0.1-alpha.39

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.
Files changed (78) hide show
  1. package/.eslintrc.json +18 -0
  2. package/jest.config.ts +22 -0
  3. package/ng-package.json +7 -0
  4. package/package.json +4 -21
  5. package/project.json +33 -0
  6. package/{index.d.ts → src/index.ts} +0 -1
  7. package/src/lib/components/dot-editable-text/dot-editable-text.component.css +4 -0
  8. package/src/lib/components/dot-editable-text/dot-editable-text.component.html +8 -0
  9. package/src/lib/components/dot-editable-text/dot-editable-text.component.spec.ts +424 -0
  10. package/src/lib/components/dot-editable-text/dot-editable-text.component.ts +269 -0
  11. package/src/lib/components/dot-editable-text/utils.ts +51 -0
  12. package/src/lib/components/no-component/no-component.component.css +3 -0
  13. package/src/lib/components/no-component/no-component.component.spec.ts +24 -0
  14. package/src/lib/components/no-component/no-component.component.ts +31 -0
  15. package/src/lib/layout/column/column.component.css +99 -0
  16. package/src/lib/layout/column/column.component.spec.ts +33 -0
  17. package/src/lib/layout/column/column.component.ts +49 -0
  18. package/src/lib/layout/container/container.component.css +9 -0
  19. package/src/lib/layout/container/container.component.html +26 -0
  20. package/src/lib/layout/container/container.component.spec.ts +205 -0
  21. package/src/lib/layout/container/container.component.ts +140 -0
  22. package/src/lib/layout/contentlet/contentlet.component.spec.ts +22 -0
  23. package/{lib/layout/contentlet/contentlet.component.d.ts → src/lib/layout/contentlet/contentlet.component.ts} +32 -17
  24. package/src/lib/layout/dotcms-layout/dotcms-layout.component.css +3 -0
  25. package/src/lib/layout/dotcms-layout/dotcms-layout.component.spec.ts +195 -0
  26. package/src/lib/layout/dotcms-layout/dotcms-layout.component.ts +150 -0
  27. package/src/lib/layout/row/row.component.css +6 -0
  28. package/src/lib/layout/row/row.component.spec.ts +28 -0
  29. package/src/lib/layout/row/row.component.ts +32 -0
  30. package/{lib/models/dotcms.model.d.ts → src/lib/models/dotcms.model.ts} +21 -3
  31. package/{lib/models/index.d.ts → src/lib/models/index.ts} +8 -1
  32. package/{lib/services/dotcms-context/page-context.service.d.ts → src/lib/services/dotcms-context/page-context.service.ts} +41 -12
  33. package/src/lib/services/dotcms-context/page-context.spec.ts +80 -0
  34. package/src/lib/utils/index.ts +92 -0
  35. package/src/lib/utils/testing.utils.ts +1019 -0
  36. package/src/test-setup.ts +8 -0
  37. package/tsconfig.json +29 -0
  38. package/tsconfig.lib.json +12 -0
  39. package/tsconfig.lib.prod.json +9 -0
  40. package/tsconfig.spec.json +11 -0
  41. package/dotcms-angular.d.ts.map +0 -1
  42. package/esm2022/dotcms-angular.mjs +0 -5
  43. package/esm2022/index.mjs +0 -5
  44. package/esm2022/lib/components/dot-editable-text/dot-editable-text.component.mjs +0 -225
  45. package/esm2022/lib/components/dot-editable-text/utils.mjs +0 -43
  46. package/esm2022/lib/components/no-component/no-component.component.mjs +0 -32
  47. package/esm2022/lib/layout/column/column.component.mjs +0 -45
  48. package/esm2022/lib/layout/container/container.component.mjs +0 -126
  49. package/esm2022/lib/layout/contentlet/contentlet.component.mjs +0 -120
  50. package/esm2022/lib/layout/dotcms-layout/dotcms-layout.component.mjs +0 -100
  51. package/esm2022/lib/layout/row/row.component.mjs +0 -29
  52. package/esm2022/lib/models/dotcms.model.mjs +0 -3
  53. package/esm2022/lib/models/index.mjs +0 -3
  54. package/esm2022/lib/services/dotcms-context/page-context.service.mjs +0 -75
  55. package/esm2022/lib/utils/index.mjs +0 -79
  56. package/fesm2022/dotcms-angular.mjs +0 -858
  57. package/fesm2022/dotcms-angular.mjs.map +0 -1
  58. package/index.d.ts.map +0 -1
  59. package/lib/components/dot-editable-text/dot-editable-text.component.d.ts +0 -129
  60. package/lib/components/dot-editable-text/dot-editable-text.component.d.ts.map +0 -1
  61. package/lib/components/dot-editable-text/utils.d.ts +0 -7
  62. package/lib/components/dot-editable-text/utils.d.ts.map +0 -1
  63. package/lib/components/no-component/no-component.component.d.ts +0 -22
  64. package/lib/components/no-component/no-component.component.d.ts.map +0 -1
  65. package/lib/layout/column/column.component.d.ts +0 -29
  66. package/lib/layout/column/column.component.d.ts.map +0 -1
  67. package/lib/layout/container/container.component.d.ts +0 -88
  68. package/lib/layout/container/container.component.d.ts.map +0 -1
  69. package/lib/layout/contentlet/contentlet.component.d.ts.map +0 -1
  70. package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts +0 -67
  71. package/lib/layout/dotcms-layout/dotcms-layout.component.d.ts.map +0 -1
  72. package/lib/layout/row/row.component.d.ts +0 -20
  73. package/lib/layout/row/row.component.d.ts.map +0 -1
  74. package/lib/models/dotcms.model.d.ts.map +0 -1
  75. package/lib/models/index.d.ts.map +0 -1
  76. package/lib/services/dotcms-context/page-context.service.d.ts.map +0 -1
  77. package/lib/utils/index.d.ts +0 -63
  78. package/lib/utils/index.d.ts.map +0 -1
package/.eslintrc.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": ["../../../.eslintrc.base.json"],
3
+ "ignorePatterns": ["!**/*"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
+ "rules": {}
8
+ },
9
+ {
10
+ "files": ["*.ts", "*.tsx"],
11
+ "rules": {}
12
+ },
13
+ {
14
+ "files": ["*.js", "*.jsx"],
15
+ "rules": {}
16
+ }
17
+ ]
18
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,22 @@
1
+ /* eslint-disable */
2
+ export default {
3
+ displayName: 'sdk-angular',
4
+ preset: '../../../jest.preset.js',
5
+ setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
6
+ coverageDirectory: '../../../coverage/libs/sdk/angular',
7
+ transform: {
8
+ '^.+\\.(ts|mjs|js|html)$': [
9
+ 'jest-preset-angular',
10
+ {
11
+ tsconfig: '<rootDir>/tsconfig.spec.json',
12
+ stringifyContentPathRegex: '\\.(html|svg)$'
13
+ }
14
+ ]
15
+ },
16
+ transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
17
+ snapshotSerializers: [
18
+ 'jest-preset-angular/build/serializers/no-ng-attributes',
19
+ 'jest-preset-angular/build/serializers/ng-snapshot',
20
+ 'jest-preset-angular/build/serializers/html-comment'
21
+ ]
22
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../../dist/libs/sdk/angular",
4
+ "lib": {
5
+ "entryFile": "src/index.ts"
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dotcms/angular",
3
- "version": "0.0.1-alpha.38",
3
+ "version": "0.0.1-alpha.39",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^17.1.0",
6
6
  "@angular/core": "^17.1.0",
7
7
  "@angular/router": "^17.1.0",
8
- "@dotcms/client": "0.0.1-alpha.38",
8
+ "@dotcms/client": "0.0.1-alpha.39",
9
9
  "@tinymce/tinymce-angular": "^8.0.0",
10
10
  "rxjs": "^7.8.0"
11
11
  },
@@ -28,22 +28,5 @@
28
28
  "bugs": {
29
29
  "url": "https://github.com/dotCMS/core/issues"
30
30
  },
31
- "homepage": "https://github.com/dotCMS/core/tree/master/core-web/libs/sdk/angular/README.md",
32
- "module": "fesm2022/dotcms-angular.mjs",
33
- "typings": "index.d.ts",
34
- "exports": {
35
- "./package.json": {
36
- "default": "./package.json"
37
- },
38
- ".": {
39
- "types": "./index.d.ts",
40
- "esm2022": "./esm2022/dotcms-angular.mjs",
41
- "esm": "./esm2022/dotcms-angular.mjs",
42
- "default": "./fesm2022/dotcms-angular.mjs"
43
- }
44
- },
45
- "sideEffects": false,
46
- "dependencies": {
47
- "tslib": "^2.3.0"
48
- }
49
- }
31
+ "homepage": "https://github.com/dotCMS/core/tree/master/core-web/libs/sdk/angular/README.md"
32
+ }
package/project.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "sdk-angular",
3
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "libs/sdk/angular/src",
5
+ "prefix": "lib",
6
+ "tags": [],
7
+ "projectType": "library",
8
+ "targets": {
9
+ "build": {
10
+ "executor": "@nx/angular:package",
11
+ "outputs": ["{workspaceRoot}/dist/{projectRoot}"],
12
+ "options": {
13
+ "project": "libs/sdk/angular/ng-package.json"
14
+ },
15
+ "configurations": {
16
+ "production": {
17
+ "tsConfig": "libs/sdk/angular/tsconfig.lib.prod.json"
18
+ },
19
+ "development": {
20
+ "tsConfig": "libs/sdk/angular/tsconfig.lib.json"
21
+ }
22
+ },
23
+ "defaultConfiguration": "production"
24
+ },
25
+ "test": {
26
+ "executor": "@nx/jest:jest",
27
+ "outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
28
+ "options": {
29
+ "jestConfig": "libs/sdk/angular/jest.config.ts"
30
+ }
31
+ }
32
+ }
33
+ }
@@ -2,4 +2,3 @@ export * from './lib/components/dot-editable-text/dot-editable-text.component';
2
2
  export * from './lib/layout/dotcms-layout/dotcms-layout.component';
3
3
  export * from './lib/services/dotcms-context/page-context.service';
4
4
  export * from './lib/models';
5
- //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,4 @@
1
+ :host ::ng-deep .mce-content-body:not(.mce-edit-focus):hover {
2
+ outline: 2px solid #006ce7;
3
+ border-radius: 4px;
4
+ }
@@ -0,0 +1,8 @@
1
+ @if (isInsideEditor) {
2
+ <editor
3
+ #tinyEditor
4
+ [init]="init"
5
+ [initialValue]="content"
6
+ (onMouseDown)="onMouseDown($event)"
7
+ (onFocusOut)="onFocusOut()" />
8
+ }
@@ -0,0 +1,424 @@
1
+ import { SpyObject } from '@ngneat/spectator';
2
+ import { Spectator, createComponentFactory } from '@ngneat/spectator/jest';
3
+ import { EditorComponent, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
4
+ import { MockComponent } from 'ng-mocks';
5
+ import { Editor } from 'tinymce';
6
+
7
+ import { DebugElement, ElementRef, Renderer2, SecurityContext } from '@angular/core';
8
+ import { By, DomSanitizer } from '@angular/platform-browser';
9
+
10
+ import * as dotcmsClient from '@dotcms/client';
11
+
12
+ import { DotEditableTextComponent } from './dot-editable-text.component';
13
+ import { TINYMCE_CONFIG } from './utils';
14
+
15
+ import { dotcmsContentletMock } from '../../utils/testing.utils';
16
+
17
+ const { CUSTOMER_ACTIONS, postMessageToEditor } = dotcmsClient;
18
+
19
+ // Mock @dotcms/client module
20
+ jest.mock('@dotcms/client', () => ({
21
+ ...jest.requireActual('@dotcms/client'),
22
+ isInsideEditor: jest.fn().mockImplementation(() => true),
23
+ postMessageToEditor: jest.fn(),
24
+ DotCmsClient: {
25
+ dotcmsUrl: 'http://localhost:8080'
26
+ }
27
+ }));
28
+
29
+ const mockedDotcmsClient = dotcmsClient as jest.Mocked<typeof dotcmsClient>;
30
+
31
+ const TINYMCE_EDITOR_MOCK: unknown = {
32
+ focus: jest.fn(),
33
+ getContent: (_data: unknown) => '',
34
+ isDirty: () => false,
35
+ hasFocus: () => false,
36
+ setContent: jest.fn()
37
+ };
38
+
39
+ const TINYMCE_EDITOR_PROPERTY_MOCK = {
40
+ get: jest.fn(() => TINYMCE_EDITOR_MOCK as Editor)
41
+ };
42
+
43
+ const mockEditorFn = (spectator: Spectator<DotEditableTextComponent>) => {
44
+ // Mock the editor property of the EditorComponent
45
+ // We need to test that the methods of the editor are called
46
+ // We do not care about how the editor handles the calls under the hood
47
+ Object.defineProperty(
48
+ spectator.component.editorComponent,
49
+ 'editor',
50
+ TINYMCE_EDITOR_PROPERTY_MOCK
51
+ );
52
+ };
53
+
54
+ describe('DotEditableTextComponent', () => {
55
+ let spectator: Spectator<DotEditableTextComponent>;
56
+
57
+ const createComponent = createComponentFactory({
58
+ component: DotEditableTextComponent,
59
+ declarations: [MockComponent(EditorComponent)],
60
+ providers: [
61
+ Renderer2,
62
+ {
63
+ provide: DomSanitizer,
64
+ useValue: {
65
+ bypassSecurityTrustHtml: () => '',
66
+ sanitize: () => ''
67
+ }
68
+ },
69
+ {
70
+ provide: ElementRef,
71
+ useValue: {
72
+ nativeElement: document.createElement('div')
73
+ }
74
+ },
75
+ {
76
+ provide: TINYMCE_SCRIPT_SRC,
77
+ useValue: 'tinymce/tinymce.min.js'
78
+ }
79
+ ]
80
+ });
81
+
82
+ beforeEach(() => {
83
+ spectator = createComponent({
84
+ props: {
85
+ contentlet: dotcmsContentletMock,
86
+ fieldName: 'title'
87
+ },
88
+ detectChanges: false
89
+ });
90
+ });
91
+
92
+ describe('Outside Editor', () => {
93
+ let renderer2: SpyObject<Renderer2>;
94
+ let elementRef: SpyObject<ElementRef>;
95
+ let sanitizer: SpyObject<DomSanitizer>;
96
+
97
+ beforeEach(() => {
98
+ jest.spyOn(mockedDotcmsClient, 'isInsideEditor').mockReturnValue(false);
99
+ renderer2 = spectator.inject(Renderer2, true);
100
+ elementRef = spectator.inject(ElementRef, true);
101
+ sanitizer = spectator.inject(DomSanitizer, true);
102
+ });
103
+
104
+ describe('Template', () => {
105
+ it('Should insert safe HTML content using innerHTML', () => {
106
+ const safeHtml = dotcmsContentletMock.title + 'Safe';
107
+ const spyRender2 = jest.spyOn(renderer2, 'setProperty');
108
+ const spybypassSecurityTrustHtml = jest
109
+ .spyOn(sanitizer, 'bypassSecurityTrustHtml')
110
+ .mockReturnValue(safeHtml);
111
+ const spySanitze = jest.spyOn(sanitizer, 'sanitize').mockReturnValue(safeHtml);
112
+
113
+ spectator.detectChanges();
114
+
115
+ const nativeElement = elementRef.nativeElement;
116
+ const componentHTML = spectator.debugElement.nativeElement.innerHTML;
117
+
118
+ expect(componentHTML).toBe(safeHtml);
119
+ expect(spybypassSecurityTrustHtml).toHaveBeenCalledWith(dotcmsContentletMock.title);
120
+ expect(spySanitze).toHaveBeenCalledWith(SecurityContext.HTML, safeHtml);
121
+ expect(spyRender2).toHaveBeenCalledWith(nativeElement, 'innerHTML', safeHtml);
122
+ });
123
+
124
+ it('Should not initialize the editor', () => {
125
+ spectator.detectChanges();
126
+
127
+ const editorComponent = spectator.query(EditorComponent);
128
+ expect(editorComponent).toBeNull();
129
+ });
130
+ });
131
+ });
132
+
133
+ describe('Inside Editor', () => {
134
+ beforeEach(() => {
135
+ jest.spyOn(mockedDotcmsClient, 'isInsideEditor').mockReturnValue(true);
136
+ });
137
+
138
+ it('should set content with the right format when the contentlet changes', () => {
139
+ spectator.detectChanges();
140
+ mockEditorFn(spectator);
141
+
142
+ const editorComponent = spectator.query(EditorComponent) as EditorComponent;
143
+ const spySetContent = jest.spyOn(editorComponent.editor, 'setContent');
144
+
145
+ spectator.setInput('contentlet', {
146
+ ...dotcmsContentletMock,
147
+ title: 'New title'
148
+ });
149
+ spectator.detectChanges();
150
+ expect(spySetContent).toHaveBeenCalledWith('New title', { format: 'text' });
151
+ });
152
+
153
+ describe('Configuration', () => {
154
+ describe('Editor Configuration', () => {
155
+ it('should set a plain mode by default', () => {
156
+ spectator.setInput('mode', 'plain');
157
+ spectator.detectChanges();
158
+ const editorComponent = spectator.query(EditorComponent);
159
+
160
+ spectator.detectChanges();
161
+ expect(spectator.component.mode).toBe('plain');
162
+ expect(editorComponent?.init).toEqual({
163
+ ...TINYMCE_CONFIG['plain'],
164
+ base_url: 'http://localhost:8080/ext/tinymcev7'
165
+ });
166
+ });
167
+
168
+ it('should set a minimal mode when the mode is set to minimal', () => {
169
+ spectator.setInput('mode', 'minimal');
170
+ spectator.detectChanges();
171
+
172
+ const editorComponent = spectator.query(EditorComponent);
173
+
174
+ expect(spectator.component.mode).toBe('minimal');
175
+ expect(editorComponent?.init).toEqual({
176
+ ...TINYMCE_CONFIG['minimal'],
177
+ base_url: 'http://localhost:8080/ext/tinymcev7'
178
+ });
179
+ });
180
+
181
+ it('should set a full mode when the mode is set to full', () => {
182
+ spectator.setInput('mode', 'full');
183
+ spectator.detectChanges();
184
+
185
+ const editorComponent = spectator.query(EditorComponent);
186
+
187
+ expect(spectator.component.mode).toBe('full');
188
+ expect(editorComponent?.init).toEqual({
189
+ ...TINYMCE_CONFIG['full'],
190
+ base_url: 'http://localhost:8080/ext/tinymcev7'
191
+ });
192
+ });
193
+ });
194
+
195
+ describe('format', () => {
196
+ let getContentSpy: jest.SpyInstance;
197
+ let editorDebugElement: DebugElement;
198
+ let customEvent: {
199
+ event: FocusEvent;
200
+ editor: unknown;
201
+ };
202
+
203
+ beforeEach(() => {
204
+ spectator.detectChanges();
205
+ mockEditorFn(spectator);
206
+
207
+ const editor = spectator.component.editorComponent.editor;
208
+ getContentSpy = jest.spyOn(editor, 'getContent');
209
+ customEvent = {
210
+ event: new FocusEvent('focusout'),
211
+ editor: TINYMCE_EDITOR_MOCK
212
+ };
213
+ editorDebugElement = spectator.debugElement.query(
214
+ By.directive(EditorComponent)
215
+ );
216
+ });
217
+
218
+ it('should get the content as text by default', () => {
219
+ spectator.triggerEventHandler(editorDebugElement, 'onFocusOut', customEvent);
220
+ expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
221
+ });
222
+
223
+ it('should get the content as text when format is set to text', () => {
224
+ spectator.setInput('format', 'text');
225
+ spectator.detectChanges();
226
+ spectator.triggerEventHandler(editorDebugElement, 'onFocusOut', customEvent);
227
+ expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
228
+ });
229
+
230
+ it('should get the content as html when format is set to html', () => {
231
+ spectator.setInput('format', 'html');
232
+ spectator.detectChanges();
233
+ spectator.triggerEventHandler(editorDebugElement, 'onFocusOut', customEvent);
234
+ expect(getContentSpy).toHaveBeenCalledWith({ format: 'html' });
235
+ });
236
+ });
237
+ });
238
+
239
+ describe('events', () => {
240
+ beforeEach(() => {
241
+ spectator.detectChanges();
242
+ mockEditorFn(spectator);
243
+ });
244
+
245
+ describe('Window Message', () => {
246
+ let focusSpy: jest.SpyInstance;
247
+ beforeEach(() => {
248
+ focusSpy = jest.spyOn(spectator.component.editorComponent.editor, 'focus');
249
+ });
250
+
251
+ it("should focus on the editor when the message is 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS'", () => {
252
+ window.dispatchEvent(
253
+ new MessageEvent('message', {
254
+ data: {
255
+ name: 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS',
256
+ payload: {
257
+ oldInode: dotcmsContentletMock.inode,
258
+ inode: dotcmsContentletMock.inode
259
+ }
260
+ }
261
+ })
262
+ );
263
+ spectator.detectChanges();
264
+ expect(focusSpy).toHaveBeenCalled();
265
+ });
266
+
267
+ it("should not focus on the editor when the message is not 'COPY_CONTENTLET_INLINE_EDITING_SUCCESS'", () => {
268
+ window.dispatchEvent(
269
+ new MessageEvent('message', {
270
+ data: { name: 'ANOTHER_EVENT' }
271
+ })
272
+ );
273
+ spectator.detectChanges();
274
+ expect(focusSpy).not.toHaveBeenCalled();
275
+ });
276
+ });
277
+
278
+ describe('mousedown', () => {
279
+ let event: MouseEvent;
280
+ let editorDebugElement: DebugElement;
281
+ let customEvent: {
282
+ event: MouseEvent;
283
+ editor: unknown;
284
+ };
285
+
286
+ beforeEach(() => {
287
+ spectator.setInput('contentlet', {
288
+ ...dotcmsContentletMock,
289
+ onNumberOfPages: 2 // This will need to be overridden in tests where a different value is required
290
+ });
291
+ spectator.detectChanges();
292
+
293
+ event = new MouseEvent('mousedown');
294
+ customEvent = { event, editor: TINYMCE_EDITOR_MOCK };
295
+ editorDebugElement = spectator.debugElement.query(
296
+ By.directive(EditorComponent)
297
+ );
298
+
299
+ jest.spyOn(event, 'stopPropagation');
300
+ jest.spyOn(event, 'preventDefault');
301
+ });
302
+
303
+ it('should postMessage the UVE if the content is in multiple pages', () => {
304
+ spectator.triggerEventHandler(editorDebugElement, 'onMouseDown', customEvent);
305
+
306
+ const payload = {
307
+ dataset: {
308
+ fieldName: 'title',
309
+ inode: dotcmsContentletMock.inode,
310
+ language: dotcmsContentletMock.languageId
311
+ }
312
+ };
313
+
314
+ expect(postMessageToEditor).toHaveBeenCalledWith({
315
+ action: CUSTOMER_ACTIONS.COPY_CONTENTLET_INLINE_EDITING,
316
+ payload
317
+ });
318
+ expect(event.stopPropagation).toHaveBeenCalled();
319
+ expect(event.preventDefault).toHaveBeenCalled();
320
+ });
321
+
322
+ it('should not postMessage the UVE if the content is in a single page', () => {
323
+ spectator.setInput('contentlet', {
324
+ ...dotcmsContentletMock,
325
+ onNumberOfPages: 1
326
+ });
327
+ spectator.detectChanges();
328
+ spectator.triggerEventHandler(editorDebugElement, 'onMouseDown', customEvent);
329
+ expect(postMessageToEditor).not.toHaveBeenCalled();
330
+ expect(event.stopPropagation).not.toHaveBeenCalled();
331
+ expect(event.preventDefault).not.toHaveBeenCalled();
332
+ });
333
+
334
+ it('should not postMessage the UVE if the editor is already focus', () => {
335
+ const hasFocusSpy = jest
336
+ .spyOn(spectator.component.editorComponent.editor, 'hasFocus')
337
+ .mockReturnValue(true);
338
+
339
+ spectator.detectChanges();
340
+ spectator.triggerEventHandler(editorDebugElement, 'onMouseDown', customEvent);
341
+ expect(postMessageToEditor).not.toHaveBeenCalled();
342
+ expect(event.stopPropagation).not.toHaveBeenCalled();
343
+ expect(event.preventDefault).not.toHaveBeenCalled();
344
+ expect(hasFocusSpy).toHaveBeenCalled();
345
+ });
346
+ });
347
+
348
+ describe('focusout', () => {
349
+ let isDirtySpy: jest.SpyInstance;
350
+ let getContentSpy: jest.SpyInstance;
351
+ let event: FocusEvent;
352
+ let editorDebugElement: DebugElement;
353
+ let customEvent: {
354
+ event: FocusEvent;
355
+ editor: unknown;
356
+ };
357
+
358
+ beforeEach(() => {
359
+ const editor = spectator.component.editorComponent.editor;
360
+ isDirtySpy = jest.spyOn(editor, 'isDirty');
361
+ getContentSpy = jest.spyOn(editor, 'getContent');
362
+
363
+ event = new FocusEvent('focusout');
364
+ customEvent = { event, editor: TINYMCE_EDITOR_MOCK };
365
+
366
+ editorDebugElement = spectator.debugElement.query(
367
+ By.directive(EditorComponent)
368
+ );
369
+ });
370
+
371
+ it('should not postMessage the UVE if the editor is not dirty', () => {
372
+ isDirtySpy.mockReturnValue(false);
373
+ getContentSpy.mockReturnValue("I'm not dirty");
374
+
375
+ spectator.detectChanges();
376
+
377
+ spectator.triggerEventHandler(editorDebugElement, 'onFocusOut', customEvent);
378
+
379
+ expect(isDirtySpy).toHaveBeenCalled();
380
+ expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
381
+ expect(postMessageToEditor).not.toHaveBeenCalled();
382
+ });
383
+
384
+ it('should not postMessage the UVE if the content did not change', () => {
385
+ isDirtySpy.mockReturnValue(true);
386
+ getContentSpy.mockReturnValue(dotcmsContentletMock.title);
387
+
388
+ spectator.detectChanges();
389
+ spectator.triggerEventHandler(editorDebugElement, 'onFocusOut', customEvent);
390
+
391
+ expect(isDirtySpy).toHaveBeenCalled();
392
+ expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
393
+ expect(postMessageToEditor).not.toHaveBeenCalled();
394
+ });
395
+
396
+ it('should postMessage the UVE if the content changed', () => {
397
+ isDirtySpy.mockReturnValue(true);
398
+ getContentSpy.mockReturnValue('New content');
399
+
400
+ spectator.detectChanges();
401
+ spectator.triggerEventHandler(editorDebugElement, 'onFocusOut', customEvent);
402
+
403
+ const postMessageData = {
404
+ action: CUSTOMER_ACTIONS.UPDATE_CONTENTLET_INLINE_EDITING,
405
+ payload: {
406
+ content: 'New content',
407
+ dataset: {
408
+ inode: dotcmsContentletMock.inode,
409
+ langId: dotcmsContentletMock.languageId,
410
+ fieldName: 'title'
411
+ }
412
+ }
413
+ };
414
+
415
+ expect(isDirtySpy).toHaveBeenCalled();
416
+ expect(getContentSpy).toHaveBeenCalledWith({ format: 'text' });
417
+ expect(postMessageToEditor).toHaveBeenCalledWith(postMessageData);
418
+ });
419
+ });
420
+ });
421
+ });
422
+
423
+ afterEach(() => jest.clearAllMocks()); // Clear all mocks to avoid side effects from other tests
424
+ });