diff --git a/packages/roosterjs-content-model-api/lib/publicApi/utils/formatSegmentWithContentModel.ts b/packages/roosterjs-content-model-api/lib/publicApi/utils/formatSegmentWithContentModel.ts index ab2c099f0bd..3a8a2811f3b 100644 --- a/packages/roosterjs-content-model-api/lib/publicApi/utils/formatSegmentWithContentModel.ts +++ b/packages/roosterjs-content-model-api/lib/publicApi/utils/formatSegmentWithContentModel.ts @@ -42,9 +42,9 @@ export function formatSegmentWithContentModel( false /*includingEntity*/, true /*mutate*/ ); - let isCollapsedSelection = segmentAndParagraphs.every( - x => x[0].segmentType == 'SelectionMarker' - ); + let isCollapsedSelection = + segmentAndParagraphs.length >= 1 && + segmentAndParagraphs.every(x => x[0].segmentType == 'SelectionMarker'); if (isCollapsedSelection) { const para = segmentAndParagraphs[0][1]; diff --git a/packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts b/packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts index 20ec9fc018d..e7723a17d27 100644 --- a/packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts +++ b/packages/roosterjs-content-model-core/lib/corePlugin/cache/CachePlugin.ts @@ -149,9 +149,7 @@ class CachePlugin implements PluginWithState { }; private invalidateCache() { - if (!this.editor?.isInShadowEdit() && this.state.cachedModel) { - console.error('Clear cache'); - + if (!this.editor?.isInShadowEdit()) { this.state.cachedModel = undefined; this.state.cachedSelection = undefined; } diff --git a/packages/roosterjs-content-model-core/test/coreApi/setContentModel/setContentModelTest.ts b/packages/roosterjs-content-model-core/test/coreApi/setContentModel/setContentModelTest.ts index 234717bcdb7..7b86cb7c2ab 100644 --- a/packages/roosterjs-content-model-core/test/coreApi/setContentModel/setContentModelTest.ts +++ b/packages/roosterjs-content-model-core/test/coreApi/setContentModel/setContentModelTest.ts @@ -18,6 +18,7 @@ describe('setContentModel', () => { let createModelToDomContextWithConfigSpy: jasmine.Spy; let setDOMSelectionSpy: jasmine.Spy; let getDOMSelectionSpy: jasmine.Spy; + let flushMutationsSpy: jasmine.Spy; beforeEach(() => { contentModelToDomSpy = spyOn(contentModelToDom, 'contentModelToDom'); @@ -34,8 +35,9 @@ describe('setContentModel', () => { ).and.returnValue(mockedContext); setDOMSelectionSpy = jasmine.createSpy('setDOMSelection'); getDOMSelectionSpy = jasmine.createSpy('getDOMSelection'); + flushMutationsSpy = jasmine.createSpy('flushMutations'); - core = ({ + core = { physicalRoot: mockedDiv, logicalRoot: mockedDiv, api: { @@ -44,13 +46,17 @@ describe('setContentModel', () => { getDOMSelection: getDOMSelectionSpy, }, lifecycle: {}, - cache: {}, + cache: { + textMutationObserver: { + flushMutations: flushMutationsSpy, + }, + }, environment: { modelToDomSettings: { calculated: mockedConfig, }, }, - } as any) as EditorCore; + } as any; }); it('no default option, no shadow edit', () => { @@ -75,7 +81,7 @@ describe('setContentModel', () => { ); expect(setDOMSelectionSpy).toHaveBeenCalledWith(core, mockedRange); expect(core.cache.cachedSelection).toBe(mockedRange); - expect(core.cache.cachedModel).toBe(mockedModel); + expect(flushMutationsSpy).toHaveBeenCalledWith(mockedModel); }); it('with default option, no shadow edit', () => { diff --git a/packages/roosterjs-content-model-core/test/corePlugin/cache/textMutationObserverTest.ts b/packages/roosterjs-content-model-core/test/corePlugin/cache/textMutationObserverTest.ts index 515ad8d418d..332142a38e3 100644 --- a/packages/roosterjs-content-model-core/test/corePlugin/cache/textMutationObserverTest.ts +++ b/packages/roosterjs-content-model-core/test/corePlugin/cache/textMutationObserverTest.ts @@ -1,10 +1,30 @@ -import * as TextMutationObserver from '../../../lib/corePlugin/cache/textMutationObserver'; +import * as textMutationObserver from '../../../lib/corePlugin/cache/textMutationObserver'; +import { DomIndexer, TextMutationObserver } from 'roosterjs-content-model-types'; +import { DomIndexerImpl } from '../../../lib/corePlugin/cache/domIndexerImpl'; describe('TextMutationObserverImpl', () => { + let domIndexer: DomIndexer; + let onSkipMutation: jasmine.Spy; + let observer: TextMutationObserver; + + beforeEach(() => { + domIndexer = new DomIndexerImpl(); + onSkipMutation = jasmine.createSpy('onSkipMutation'); + }); + + afterEach(() => { + observer?.stopObserving(); + }); + it('init', () => { const div = document.createElement('div'); const onMutation = jasmine.createSpy('onMutation'); - TextMutationObserver.createTextMutationObserver(div, onMutation); + textMutationObserver.createTextMutationObserver( + div, + domIndexer, + onMutation, + onSkipMutation + ); expect(onMutation).not.toHaveBeenCalled(); }); @@ -12,7 +32,13 @@ describe('TextMutationObserverImpl', () => { it('not text change', async () => { const div = document.createElement('div'); const onMutation = jasmine.createSpy('onMutation'); - const observer = TextMutationObserver.createTextMutationObserver(div, onMutation); + + observer = textMutationObserver.createTextMutationObserver( + div, + domIndexer, + onMutation, + onSkipMutation + ); observer.startObserving(); @@ -33,7 +59,12 @@ describe('TextMutationObserverImpl', () => { div.appendChild(text); const onMutation = jasmine.createSpy('onMutation'); - const observer = TextMutationObserver.createTextMutationObserver(div, onMutation); + const observer = textMutationObserver.createTextMutationObserver( + div, + domIndexer, + onMutation, + onSkipMutation + ); observer.startObserving(); @@ -56,7 +87,12 @@ describe('TextMutationObserverImpl', () => { div.appendChild(span); const onMutation = jasmine.createSpy('onMutation'); - const observer = TextMutationObserver.createTextMutationObserver(div, onMutation); + const observer = textMutationObserver.createTextMutationObserver( + div, + domIndexer, + onMutation, + onSkipMutation + ); observer.startObserving(); @@ -77,7 +113,12 @@ describe('TextMutationObserverImpl', () => { div.appendChild(text); const onMutation = jasmine.createSpy('onMutation'); - const observer = TextMutationObserver.createTextMutationObserver(div, onMutation); + const observer = textMutationObserver.createTextMutationObserver( + div, + domIndexer, + onMutation, + onSkipMutation + ); observer.startObserving(); @@ -88,8 +129,10 @@ describe('TextMutationObserverImpl', () => { window.setTimeout(resolve, 10); }); - expect(onMutation).toHaveBeenCalledTimes(1); + expect(onMutation).toHaveBeenCalledTimes(2); expect(onMutation).toHaveBeenCalledWith(false); + + observer.stopObserving(); }); it('flush mutation', async () => { @@ -99,7 +142,12 @@ describe('TextMutationObserverImpl', () => { div.appendChild(text); const onMutation = jasmine.createSpy('onMutation'); - const observer = TextMutationObserver.createTextMutationObserver(div, onMutation); + const observer = textMutationObserver.createTextMutationObserver( + div, + domIndexer, + onMutation, + onSkipMutation + ); observer.startObserving(); @@ -120,7 +168,12 @@ describe('TextMutationObserverImpl', () => { div.appendChild(text); const onMutation = jasmine.createSpy('onMutation'); - const observer = TextMutationObserver.createTextMutationObserver(div, onMutation); + const observer = textMutationObserver.createTextMutationObserver( + div, + domIndexer, + onMutation, + onSkipMutation + ); observer.startObserving(); observer.flushMutations(); diff --git a/packages/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts b/packages/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts index 52367fcc9ba..ef681426f59 100644 --- a/packages/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts +++ b/packages/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts @@ -74,6 +74,7 @@ describe('brProcessor', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; diff --git a/packages/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts b/packages/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts index 06284b767ef..0d6feb24ac9 100644 --- a/packages/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts +++ b/packages/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts @@ -259,6 +259,7 @@ describe('entityProcessor', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; diff --git a/packages/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts b/packages/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts index b7fa18ff970..872c1fde02a 100644 --- a/packages/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts +++ b/packages/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts @@ -394,6 +394,7 @@ describe('generalProcessor', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; diff --git a/packages/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts b/packages/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts index b3fda940b4d..3a2be47d866 100644 --- a/packages/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts +++ b/packages/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts @@ -323,6 +323,7 @@ describe('imageProcessor', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; diff --git a/packages/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts b/packages/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts index 8b3563d4c5a..08cceeab06a 100644 --- a/packages/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts +++ b/packages/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts @@ -290,6 +290,7 @@ describe('tableProcessor', () => { onSegment: null!, onTable: onTableSpy, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; diff --git a/packages/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts b/packages/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts index f264ce94157..89ef5687a5d 100644 --- a/packages/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts +++ b/packages/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts @@ -577,6 +577,7 @@ describe('textProcessor', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; @@ -611,6 +612,7 @@ describe('textProcessor', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; @@ -656,6 +658,7 @@ describe('textProcessor', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; diff --git a/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts b/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts index a05f99d3398..8fec57ab9aa 100644 --- a/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts +++ b/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts @@ -581,6 +581,7 @@ describe('handleParagraph', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; @@ -624,6 +625,7 @@ describe('handleParagraph', () => { onSegment: onSegmentSpy, onTable: null!, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer; diff --git a/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts b/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts index 4c437eaef13..a27afe8415e 100644 --- a/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts +++ b/packages/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts @@ -601,6 +601,7 @@ describe('handleTable', () => { onSegment: null!, onTable: onTableSpy, reconcileSelection: null!, + reconcileChildList: null!, }; context.domIndexer = domIndexer;