ckeditor5-blazor 1.9.1 → 1.10.0
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/dist/elements/editable.d.ts +3 -11
- package/dist/elements/editable.d.ts.map +1 -1
- package/dist/elements/editor/editor.d.ts.map +1 -1
- package/dist/elements/editor/typings.d.ts +2 -1
- package/dist/elements/editor/typings.d.ts.map +1 -1
- package/dist/elements/editor/utils/cleanup-orphan-editor-elements.d.ts +8 -0
- package/dist/elements/editor/utils/cleanup-orphan-editor-elements.d.ts.map +1 -0
- package/dist/elements/editor/utils/create-editor-in-context.d.ts +6 -1
- package/dist/elements/editor/utils/create-editor-in-context.d.ts.map +1 -1
- package/dist/elements/editor/utils/index.d.ts +1 -0
- package/dist/elements/editor/utils/index.d.ts.map +1 -1
- package/dist/elements/editor/utils/wrap-with-watchdog.d.ts +7 -16
- package/dist/elements/editor/utils/wrap-with-watchdog.d.ts.map +1 -1
- package/dist/elements/ui-part.d.ts +3 -3
- package/dist/elements/ui-part.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +459 -394
- package/dist/index.mjs.map +1 -1
- package/dist/interop/create-editable-blazor-interop.d.ts.map +1 -1
- package/dist/interop/create-editor-blazor-interop.d.ts.map +1 -1
- package/dist/shared/are-maps-equal.d.ts +11 -0
- package/dist/shared/are-maps-equal.d.ts.map +1 -0
- package/dist/shared/async-registry.d.ts +44 -16
- package/dist/shared/async-registry.d.ts.map +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/elements/editable.ts +38 -58
- package/src/elements/editor/editor.ts +122 -101
- package/src/elements/editor/typings.ts +3 -1
- package/src/elements/editor/utils/cleanup-orphan-editor-elements.test.ts +285 -0
- package/src/elements/editor/utils/cleanup-orphan-editor-elements.ts +60 -0
- package/src/elements/editor/utils/create-editor-in-context.ts +8 -2
- package/src/elements/editor/utils/index.ts +1 -0
- package/src/elements/editor/utils/wrap-with-watchdog.test.ts +34 -14
- package/src/elements/editor/utils/wrap-with-watchdog.ts +15 -25
- package/src/elements/ui-part.test.ts +1 -1
- package/src/elements/ui-part.ts +12 -11
- package/src/interop/create-editable-blazor-interop.ts +19 -16
- package/src/interop/create-editor-blazor-interop.ts +15 -18
- package/src/shared/are-maps-equal.test.ts +56 -0
- package/src/shared/are-maps-equal.ts +22 -0
- package/src/shared/async-registry.test.ts +190 -88
- package/src/shared/async-registry.ts +179 -107
- package/src/shared/index.ts +1 -0
|
@@ -20,7 +20,7 @@ export function createEditorBlazorInterop(element: HTMLElement, interop: DotNetI
|
|
|
20
20
|
const editorId = element.getAttribute('data-cke-editor-id');
|
|
21
21
|
|
|
22
22
|
let unmounted = false;
|
|
23
|
-
let
|
|
23
|
+
let stopEffect: VoidFunction | null = null;
|
|
24
24
|
|
|
25
25
|
let sync = createNoopSync<Record<string, string>>();
|
|
26
26
|
let syncRootAttributes: RootAttributesUpdater | null = null;
|
|
@@ -39,12 +39,7 @@ export function createEditorBlazorInterop(element: HTMLElement, interop: DotNetI
|
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
* Initializes the focus tracker and model listeners for the editor.
|
|
44
|
-
*/
|
|
45
|
-
const initializeSynchronization = async () => {
|
|
46
|
-
const editor = await EditorsRegistry.the.waitFor(editorId);
|
|
47
|
-
|
|
42
|
+
stopEffect = EditorsRegistry.the.mountEffect(editorId, (editor) => {
|
|
48
43
|
editorRef = globalThis.DotNet.createJSObjectReference(editor);
|
|
49
44
|
sync = createEditorValueSync(editor, {
|
|
50
45
|
getCurrentValue: () => getEditorRootsValues(editor),
|
|
@@ -70,13 +65,20 @@ export function createEditorBlazorInterop(element: HTMLElement, interop: DotNetI
|
|
|
70
65
|
// that already exist on the .NET side.
|
|
71
66
|
void interop.invokeMethodAsync('OnEditorReady', editorRef);
|
|
72
67
|
|
|
73
|
-
|
|
74
|
-
unmountCKEditorListeners = () => {
|
|
68
|
+
return () => {
|
|
75
69
|
editor.ui.focusTracker.off('change:isFocused', onFocusChange);
|
|
70
|
+
sync.unmount();
|
|
71
|
+
|
|
72
|
+
/* v8 ignore else -- @preserve */
|
|
73
|
+
if (editorRef) {
|
|
74
|
+
globalThis.DotNet?.disposeJSObjectReference(editorRef);
|
|
75
|
+
editorRef = null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
syncRootAttributes = null;
|
|
76
79
|
};
|
|
77
|
-
};
|
|
80
|
+
});
|
|
78
81
|
|
|
79
|
-
void initializeSynchronization();
|
|
80
82
|
document.body.addEventListener(CKEditor5ChangeDataEvent.EVENT_NAME, onDataChange);
|
|
81
83
|
|
|
82
84
|
ensureEditorElementsRegistered();
|
|
@@ -117,15 +119,10 @@ export function createEditorBlazorInterop(element: HTMLElement, interop: DotNetI
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
document.body.removeEventListener(CKEditor5ChangeDataEvent.EVENT_NAME, onDataChange);
|
|
120
|
-
sync.unmount();
|
|
121
|
-
unmountCKEditorListeners?.();
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
editorRef = null;
|
|
126
|
-
}
|
|
123
|
+
stopEffect?.();
|
|
124
|
+
stopEffect = null;
|
|
127
125
|
|
|
128
|
-
syncRootAttributes = null;
|
|
129
126
|
unmounted = true;
|
|
130
127
|
},
|
|
131
128
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { areMapsEqual } from './are-maps-equal';
|
|
4
|
+
|
|
5
|
+
describe('areMapsEqual', () => {
|
|
6
|
+
it('should return true for two empty maps', () => {
|
|
7
|
+
expect(areMapsEqual(new Map(), new Map())).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return true for maps with identical keys and primitive values', () => {
|
|
11
|
+
const map1 = new Map([['a', 1], ['b', 2]]);
|
|
12
|
+
const map2 = new Map([['a', 1], ['b', 2]]);
|
|
13
|
+
|
|
14
|
+
expect(areMapsEqual(map1, map2)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return false if map sizes are different', () => {
|
|
18
|
+
const map1 = new Map([['a', 1]]);
|
|
19
|
+
const map2 = new Map([['a', 1], ['b', 2]]);
|
|
20
|
+
|
|
21
|
+
expect(areMapsEqual(map1, map2)).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should return false if the keys are different', () => {
|
|
25
|
+
const map1 = new Map([['a', 1], ['c', 2]]);
|
|
26
|
+
const map2 = new Map([['a', 1], ['b', 2]]);
|
|
27
|
+
|
|
28
|
+
expect(areMapsEqual(map1, map2)).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should return false if the values for the same keys are different', () => {
|
|
32
|
+
const map1 = new Map([['a', 1], ['b', 3]]);
|
|
33
|
+
const map2 = new Map([['a', 1], ['b', 2]]);
|
|
34
|
+
|
|
35
|
+
expect(areMapsEqual(map1, map2)).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return false if the first map (map1) is null', () => {
|
|
39
|
+
const map2 = new Map([['a', 1]]);
|
|
40
|
+
|
|
41
|
+
expect(areMapsEqual(null, map2)).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should correctly handle objects as values using shallow comparison (reference equality)', () => {
|
|
45
|
+
const obj = { id: 1 };
|
|
46
|
+
|
|
47
|
+
const map1 = new Map([['key', obj]]);
|
|
48
|
+
const map2 = new Map([['key', obj]]);
|
|
49
|
+
|
|
50
|
+
expect(areMapsEqual(map1, map2)).toBe(true);
|
|
51
|
+
|
|
52
|
+
const map3 = new Map([['key', { id: 1 }]]);
|
|
53
|
+
|
|
54
|
+
expect(areMapsEqual(map1, map3)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compares two Map structures for equality based on their contents.
|
|
3
|
+
* The function checks if the maps have the same size, contain the exact same keys,
|
|
4
|
+
* and have strictly equal values (using shallow comparison).
|
|
5
|
+
*
|
|
6
|
+
* @param map1 - The first map to compare (can be null).
|
|
7
|
+
* @param map2 - The second map to compare.
|
|
8
|
+
* @returns Returns `true` if the maps are identical in terms of keys and values, otherwise `false`.
|
|
9
|
+
*/
|
|
10
|
+
export function areMapsEqual(map1: Map<any, any> | null, map2: Map<any, any>): boolean {
|
|
11
|
+
if (!map1 || map1.size !== map2.size) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
for (const [key, value] of map1) {
|
|
16
|
+
if (!map2.has(key) || map2.get(key) !== value) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
@@ -96,30 +96,6 @@ describe('async registry', () => {
|
|
|
96
96
|
registry.unregister('item1');
|
|
97
97
|
expect(registry.getItems()).not.toContain(item);
|
|
98
98
|
});
|
|
99
|
-
|
|
100
|
-
it('should throw an error if trying to unregister an item that is not registered', () => {
|
|
101
|
-
expect(() => registry.unregister('nonexistent')).toThrow(
|
|
102
|
-
'Item with ID "nonexistent" is not registered.',
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should also unregister the default item if the unregistered item was the default one', async () => {
|
|
107
|
-
const item1 = createMockItem('item1');
|
|
108
|
-
|
|
109
|
-
registry.register('item1', item1); // This also registers it as default
|
|
110
|
-
|
|
111
|
-
// Check it is the default
|
|
112
|
-
const promise = registry.execute(null, item => item);
|
|
113
|
-
|
|
114
|
-
await expect(promise).resolves.toBe(item1);
|
|
115
|
-
|
|
116
|
-
registry.unregister('item1');
|
|
117
|
-
|
|
118
|
-
// Now check that the default is also gone
|
|
119
|
-
expect(() => registry.unregister(null)).toThrow(
|
|
120
|
-
'Item with ID "null" is not registered.',
|
|
121
|
-
);
|
|
122
|
-
});
|
|
123
99
|
});
|
|
124
100
|
|
|
125
101
|
describe('execute', () => {
|
|
@@ -234,7 +210,7 @@ describe('async registry', () => {
|
|
|
234
210
|
});
|
|
235
211
|
});
|
|
236
212
|
|
|
237
|
-
describe('
|
|
213
|
+
describe('getItems', () => {
|
|
238
214
|
it('should return all registered items', () => {
|
|
239
215
|
const item1 = createMockItem('item1');
|
|
240
216
|
const item2 = createMockItem('item2');
|
|
@@ -244,7 +220,7 @@ describe('async registry', () => {
|
|
|
244
220
|
|
|
245
221
|
const items = registry.getItems();
|
|
246
222
|
|
|
247
|
-
expect(items).toHaveLength(3);
|
|
223
|
+
expect(items).toHaveLength(3);
|
|
248
224
|
expect(items).toContain(item1);
|
|
249
225
|
expect(items).toContain(item2);
|
|
250
226
|
});
|
|
@@ -252,16 +228,30 @@ describe('async registry', () => {
|
|
|
252
228
|
it('should return unique items if some point to the same instance', () => {
|
|
253
229
|
const item1 = createMockItem('item1');
|
|
254
230
|
|
|
255
|
-
registry.register('item1', item1);
|
|
231
|
+
registry.register('item1', item1);
|
|
256
232
|
|
|
257
233
|
const items = registry.getItems();
|
|
258
234
|
|
|
259
|
-
expect(items).toHaveLength(2);
|
|
235
|
+
expect(items).toHaveLength(2);
|
|
260
236
|
expect(items.filter(e => e === item1)).toHaveLength(2);
|
|
261
237
|
});
|
|
262
238
|
});
|
|
263
239
|
|
|
264
|
-
describe('
|
|
240
|
+
describe('getItem', () => {
|
|
241
|
+
it('should return registered item', () => {
|
|
242
|
+
const item = createMockItem('item1');
|
|
243
|
+
|
|
244
|
+
registry.register('item1', item);
|
|
245
|
+
|
|
246
|
+
expect(registry.getItem('item1')).toBe(item);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should return undefined if item doesn\'t exist', () => {
|
|
250
|
+
expect(registry.getItem('item1')).toBeUndefined();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('hasItem', () => {
|
|
265
255
|
it('should return true if an item with the given ID is registered', () => {
|
|
266
256
|
const item = createMockItem('item1');
|
|
267
257
|
|
|
@@ -402,110 +392,222 @@ describe('async registry', () => {
|
|
|
402
392
|
});
|
|
403
393
|
});
|
|
404
394
|
|
|
405
|
-
describe('
|
|
406
|
-
it('should
|
|
395
|
+
describe('reset', () => {
|
|
396
|
+
it('should destroy all registered items', async () => {
|
|
407
397
|
const item1 = createMockItem('item1');
|
|
398
|
+
const item2 = createMockItem('item2');
|
|
399
|
+
|
|
408
400
|
registry.register('item1', item1);
|
|
401
|
+
registry.register('item2', item2);
|
|
409
402
|
|
|
410
|
-
|
|
403
|
+
await registry.reset();
|
|
411
404
|
|
|
412
|
-
expect(
|
|
405
|
+
expect(registry.getItems()).toHaveLength(0);
|
|
413
406
|
});
|
|
414
407
|
|
|
415
|
-
it('should
|
|
416
|
-
const
|
|
417
|
-
const
|
|
408
|
+
it('should call destroy on each unique item', async () => {
|
|
409
|
+
const destroyMock1 = vi.fn().mockResolvedValue(undefined);
|
|
410
|
+
const destroyMock2 = vi.fn().mockResolvedValue(undefined);
|
|
411
|
+
|
|
412
|
+
const item1 = { name: 'item1', destroy: destroyMock1 } as unknown as Mockitem;
|
|
413
|
+
const item2 = { name: 'item2', destroy: destroyMock2 } as unknown as Mockitem;
|
|
418
414
|
|
|
419
415
|
registry.register('item1', item1);
|
|
416
|
+
registry.register('item2', item2);
|
|
420
417
|
|
|
421
|
-
|
|
418
|
+
await registry.reset();
|
|
419
|
+
|
|
420
|
+
expect(destroyMock1).toHaveBeenCalledOnce();
|
|
421
|
+
expect(destroyMock2).toHaveBeenCalledOnce();
|
|
422
422
|
});
|
|
423
423
|
|
|
424
|
-
it('should
|
|
425
|
-
const
|
|
424
|
+
it('should clear watchers so they are no longer called after reset', async () => {
|
|
425
|
+
const watcher = vi.fn();
|
|
426
|
+
registry.watch(watcher);
|
|
426
427
|
|
|
427
|
-
registry.
|
|
428
|
+
await registry.reset();
|
|
429
|
+
watcher.mockClear();
|
|
428
430
|
|
|
429
|
-
|
|
431
|
+
const item = createMockItem('item1');
|
|
432
|
+
registry.register('item1', item);
|
|
433
|
+
|
|
434
|
+
expect(watcher).not.toHaveBeenCalled();
|
|
430
435
|
});
|
|
436
|
+
});
|
|
431
437
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const
|
|
438
|
+
describe('mountEffect', () => {
|
|
439
|
+
it('should call onMount immediately when item is already registered', () => {
|
|
440
|
+
const item = createMockItem('item1');
|
|
441
|
+
registry.register('item1', item);
|
|
435
442
|
|
|
436
|
-
vi.
|
|
443
|
+
const onMount = vi.fn();
|
|
444
|
+
registry.mountEffect('item1', onMount);
|
|
437
445
|
|
|
438
|
-
|
|
439
|
-
|
|
446
|
+
expect(onMount).toHaveBeenCalledOnce();
|
|
447
|
+
expect(onMount).toHaveBeenCalledWith(item);
|
|
440
448
|
});
|
|
441
449
|
|
|
442
|
-
it('should
|
|
443
|
-
vi.
|
|
444
|
-
|
|
445
|
-
|
|
450
|
+
it('should call onMount when item registers later', () => {
|
|
451
|
+
const onMount = vi.fn();
|
|
452
|
+
registry.mountEffect('item1', onMount);
|
|
453
|
+
|
|
454
|
+
expect(onMount).not.toHaveBeenCalled();
|
|
446
455
|
|
|
447
456
|
const item = createMockItem('item1');
|
|
448
457
|
registry.register('item1', item);
|
|
449
458
|
|
|
450
|
-
|
|
459
|
+
expect(onMount).toHaveBeenCalledOnce();
|
|
460
|
+
expect(onMount).toHaveBeenCalledWith(item);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('should call cleanup returned by onMount when item is unregistered', () => {
|
|
464
|
+
const item = createMockItem('item1');
|
|
465
|
+
registry.register('item1', item);
|
|
466
|
+
|
|
467
|
+
const cleanup = vi.fn();
|
|
468
|
+
registry.mountEffect('item1', () => cleanup);
|
|
469
|
+
|
|
470
|
+
registry.unregister('item1');
|
|
471
|
+
|
|
472
|
+
expect(cleanup).toHaveBeenCalledOnce();
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should call cleanup and re-call onMount on re-registration', () => {
|
|
476
|
+
const item1a = createMockItem('item1');
|
|
477
|
+
registry.register('item1', item1a);
|
|
478
|
+
|
|
479
|
+
const cleanup = vi.fn();
|
|
480
|
+
const onMount = vi.fn(() => cleanup);
|
|
481
|
+
registry.mountEffect('item1', onMount);
|
|
482
|
+
|
|
483
|
+
expect(onMount).toHaveBeenCalledOnce();
|
|
484
|
+
|
|
485
|
+
registry.unregister('item1');
|
|
486
|
+
expect(cleanup).toHaveBeenCalledOnce();
|
|
487
|
+
|
|
488
|
+
const item1b = createMockItem('item1');
|
|
489
|
+
registry.register('item1', item1b);
|
|
490
|
+
|
|
491
|
+
expect(onMount).toHaveBeenCalledTimes(2);
|
|
492
|
+
expect(onMount).toHaveBeenLastCalledWith(item1b);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('should not call onMount if item never registers', () => {
|
|
496
|
+
const onMount = vi.fn();
|
|
497
|
+
const stop = registry.mountEffect('item1', onMount);
|
|
498
|
+
|
|
499
|
+
stop();
|
|
451
500
|
|
|
452
|
-
expect(
|
|
453
|
-
vi.useRealTimers();
|
|
501
|
+
expect(onMount).not.toHaveBeenCalled();
|
|
454
502
|
});
|
|
455
503
|
|
|
456
|
-
it('should cleanup
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
504
|
+
it('should run cleanup and stop watching when stop is called with item mounted', () => {
|
|
505
|
+
const item = createMockItem('item1');
|
|
506
|
+
registry.register('item1', item);
|
|
507
|
+
|
|
508
|
+
const cleanup = vi.fn();
|
|
509
|
+
const stop = registry.mountEffect('item1', () => cleanup);
|
|
510
|
+
|
|
511
|
+
stop();
|
|
512
|
+
|
|
513
|
+
expect(cleanup).toHaveBeenCalledOnce();
|
|
514
|
+
|
|
515
|
+
// Watcher should be gone — unregistering should not trigger cleanup again.
|
|
516
|
+
registry.unregister('item1');
|
|
517
|
+
|
|
518
|
+
expect(cleanup).toHaveBeenCalledOnce();
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it('should not call cleanup when stop is called before item registers', () => {
|
|
522
|
+
const cleanup = vi.fn();
|
|
523
|
+
const onMount = vi.fn(() => cleanup);
|
|
524
|
+
const stop = registry.mountEffect('item1', onMount);
|
|
525
|
+
|
|
526
|
+
stop();
|
|
527
|
+
|
|
528
|
+
expect(onMount).not.toHaveBeenCalled();
|
|
529
|
+
expect(cleanup).not.toHaveBeenCalled();
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
it('should call onMount and immediately run cleanup when item registers after stop', () => {
|
|
533
|
+
const cleanup = vi.fn();
|
|
534
|
+
const onMount = vi.fn(() => cleanup);
|
|
535
|
+
const stop = registry.mountEffect('item1', onMount);
|
|
460
536
|
|
|
461
|
-
|
|
537
|
+
stop();
|
|
462
538
|
|
|
463
|
-
|
|
539
|
+
const item = createMockItem('item1');
|
|
540
|
+
registry.register('item1', item);
|
|
464
541
|
|
|
465
|
-
expect(
|
|
466
|
-
|
|
542
|
+
expect(onMount).toHaveBeenCalledOnce();
|
|
543
|
+
expect(onMount).toHaveBeenCalledWith(item);
|
|
544
|
+
expect(cleanup).toHaveBeenCalledOnce();
|
|
467
545
|
});
|
|
468
546
|
|
|
469
|
-
it('should
|
|
470
|
-
vi.
|
|
471
|
-
const
|
|
472
|
-
const promise = registry.waitFor('item1', 100);
|
|
547
|
+
it('should stop watching after late cleanup fires', () => {
|
|
548
|
+
const onMount = vi.fn();
|
|
549
|
+
const stop = registry.mountEffect('item1', onMount);
|
|
473
550
|
|
|
474
|
-
|
|
475
|
-
vi.advanceTimersByTime(100);
|
|
551
|
+
stop();
|
|
476
552
|
|
|
477
|
-
|
|
553
|
+
const item1 = createMockItem('item1');
|
|
554
|
+
registry.register('item1', item1);
|
|
555
|
+
|
|
556
|
+
expect(onMount).toHaveBeenCalledOnce();
|
|
557
|
+
|
|
558
|
+
// Watcher should have been removed after late cleanup — further changes are ignored.
|
|
559
|
+
registry.unregister('item1');
|
|
560
|
+
registry.register('item1', createMockItem('item1'));
|
|
478
561
|
|
|
479
|
-
|
|
480
|
-
|
|
562
|
+
expect(onMount).toHaveBeenCalledOnce();
|
|
563
|
+
});
|
|
481
564
|
|
|
482
|
-
|
|
565
|
+
it('should not throw when onMount returns void and stop is called', () => {
|
|
483
566
|
const item = createMockItem('item1');
|
|
484
567
|
registry.register('item1', item);
|
|
485
568
|
|
|
486
|
-
|
|
569
|
+
const stop = registry.mountEffect('item1', () => {});
|
|
570
|
+
|
|
571
|
+
expect(() => stop()).not.toThrow();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should work with the default null ID', () => {
|
|
575
|
+
const item = createMockItem('item1');
|
|
576
|
+
registry.register('item1', item); // also registered as default
|
|
577
|
+
|
|
578
|
+
const onMount = vi.fn();
|
|
579
|
+
registry.mountEffect(null, onMount);
|
|
487
580
|
|
|
488
|
-
|
|
581
|
+
expect(onMount).toHaveBeenCalledOnce();
|
|
582
|
+
expect(onMount).toHaveBeenCalledWith(item);
|
|
489
583
|
});
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
describe('waitFor', () => {
|
|
587
|
+
it('should return a promise that resolves with the item instance', async () => {
|
|
588
|
+
const item1 = createMockItem('item1');
|
|
589
|
+
registry.register('item1', item1);
|
|
590
|
+
|
|
591
|
+
const result = await registry.waitFor('item1');
|
|
490
592
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const clearTimeoutSpy = vi.spyOn(globalThis, 'clearTimeout');
|
|
494
|
-
const promise = registry.waitFor('item1', 100);
|
|
593
|
+
expect(result).toBe(item1);
|
|
594
|
+
});
|
|
495
595
|
|
|
496
|
-
|
|
497
|
-
|
|
596
|
+
it('should wait for the item to be registered before resolving', async () => {
|
|
597
|
+
const promise = registry.waitFor('item1');
|
|
598
|
+
const item1 = createMockItem('item1');
|
|
498
599
|
|
|
499
|
-
|
|
600
|
+
registry.register('item1', item1);
|
|
500
601
|
|
|
501
|
-
|
|
602
|
+
expect(await promise).toBe(item1);
|
|
603
|
+
});
|
|
502
604
|
|
|
503
|
-
|
|
504
|
-
registry.
|
|
605
|
+
it('should reject if error is registered after waitFor call', async () => {
|
|
606
|
+
const promise = registry.waitFor('item1');
|
|
505
607
|
|
|
506
|
-
|
|
608
|
+
registry.error('item1', 'Failed to initialize');
|
|
507
609
|
|
|
508
|
-
|
|
610
|
+
await expect(promise).rejects.toThrow('Failed to initialize');
|
|
509
611
|
});
|
|
510
612
|
});
|
|
511
613
|
|
|
@@ -597,7 +699,7 @@ describe('async registry', () => {
|
|
|
597
699
|
const item1 = createMockItem('item1');
|
|
598
700
|
registry.register('item1', item1);
|
|
599
701
|
|
|
600
|
-
expect(watcher).toHaveBeenCalledTimes(
|
|
702
|
+
expect(watcher).toHaveBeenCalledTimes(2);
|
|
601
703
|
});
|
|
602
704
|
|
|
603
705
|
it('should call watcher when item is unregistered', () => {
|
|
@@ -610,7 +712,7 @@ describe('async registry', () => {
|
|
|
610
712
|
watcher.mockClear();
|
|
611
713
|
registry.unregister('item1');
|
|
612
714
|
|
|
613
|
-
expect(watcher).toHaveBeenCalledTimes(
|
|
715
|
+
expect(watcher).toHaveBeenCalledTimes(1);
|
|
614
716
|
});
|
|
615
717
|
|
|
616
718
|
it('should call watcher when all items are destroyed', async () => {
|