From 88e280533c86298105de1578f8cf84987b0ef0b4 Mon Sep 17 00:00:00 2001 From: "SOUTHAMERICA\\bvalverde" Date: Fri, 6 Feb 2026 15:30:27 -0600 Subject: [PATCH 1/2] Filter temporary EOP elements in Word Online paste and add test pattern support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Skip elements with both 'Selected' and 'EOP' classes during WAC paste processing to remove temporary End of Paragraph markers - Add unit tests for EOP element filtering behavior (3 test cases) - Enhance test runner with --testPathPattern and --testNamePattern flags for faster targeted test execution 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- karma.fast.conf.js | 7 ++ .../processPastedContentWacComponents.ts | 8 ++- .../word/processPastedContentFromWacTest.ts | 65 +++++++++++++++++++ tools/karma.test.js | 18 +++-- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/karma.fast.conf.js b/karma.fast.conf.js index 8058715f3911..cb9f6157373b 100644 --- a/karma.fast.conf.js +++ b/karma.fast.conf.js @@ -1,5 +1,7 @@ const argv = require('minimist')(process.argv.slice(2)); const components = argv.components !== true && argv.components; +const testPathPattern = argv.testPathPattern !== true && argv.testPathPattern; +const testNamePattern = argv.testNamePattern !== true && argv.testNamePattern; const runCoverage = typeof argv.coverage !== 'undefined'; const runFirefox = typeof argv.firefox !== 'undefined'; const runChrome = typeof argv.chrome !== 'undefined'; @@ -69,8 +71,13 @@ module.exports = function (config) { plugins, client: { components: components, + testPathPattern: testPathPattern, + testNamePattern: testNamePattern, clearContext: false, captureConsole: true, + jasmine: { + grep: testNamePattern || null, + }, }, browsers: launcher, files: ['tools/karma.test.all.js'], diff --git a/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts b/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts index 22851c364174..ebb3a9c9841a 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts @@ -23,6 +23,8 @@ import type { const LIST_ELEMENT_TAGS = ['UL', 'OL', 'LI']; const LIST_ELEMENT_SELECTOR = LIST_ELEMENT_TAGS.join(','); +const END_OF_PARAGRAPH = 'EOP'; +const SELECTED_CLASS = 'Selected'; interface WacContext extends DomToModelListFormat { /** @@ -77,7 +79,11 @@ const wacElementProcessor: ElementProcessor = ( return; } - if (TEMP_ELEMENTS_CLASSES.some(className => element.classList.contains(className))) { + if ( + TEMP_ELEMENTS_CLASSES.some(className => element.classList.contains(className)) || + // This is needed to remove some temporary End of paragraph elements that WAC sometimes preserve + (element.classList.contains(SELECTED_CLASS) && element.classList.contains(END_OF_PARAGRAPH)) + ) { return; } else if (shouldClearListContext(elementTag, element, context)) { const { listFormat } = context; diff --git a/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts index 9174c55d92e5..6215c4db93c5 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts @@ -6112,4 +6112,69 @@ describe('wordOnlineHandler', () => { true ); }); + + it('Should skip elements with both Selected and EOP classes', () => { + runTest( + '
HelloWorld
', + '
HelloWorld
', + { + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + { segmentType: 'Text', text: 'Hello', format: {} }, + { segmentType: 'Text', text: 'World', format: {} }, + ], + format: {}, + }, + ], + }, + true + ); + }); + + it('Should not skip elements with only Selected class', () => { + runTest( + '
Hello!World
', + '
Hello!World
', + { + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + { segmentType: 'Text', text: 'Hello', format: {} }, + { segmentType: 'Text', text: '!', format: {} }, + { segmentType: 'Text', text: 'World', format: {} }, + ], + format: {}, + }, + ], + }, + true + ); + }); + + it('Should not skip elements with only EOP class', () => { + runTest( + '
Hello!World
', + '
Hello!World
', + { + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + { segmentType: 'Text', text: 'Hello', format: {} }, + { segmentType: 'Text', text: '!', format: {} }, + { segmentType: 'Text', text: 'World', format: {} }, + ], + format: {}, + }, + ], + }, + true + ); + }); }); diff --git a/tools/karma.test.js b/tools/karma.test.js index 22a93d2442f9..7e5be9f2ad81 100644 --- a/tools/karma.test.js +++ b/tools/karma.test.js @@ -1,14 +1,24 @@ module.exports = function (contexts) { - if (!!__karma__.config.components) { - const filenameWithoutTest = __karma__.config.components.replace('Test', ''); - const filterRegExpByFilename = new RegExp(filenameWithoutTest); + const components = __karma__.config.components; + const testPathPattern = __karma__.config.testPathPattern; + + if (!!components || !!testPathPattern) { + const pattern = testPathPattern || components.replace('Test', ''); + const filterRegExpByFilename = new RegExp(pattern); const specificFiles = []; contexts.forEach(context => { specificFiles.push(...context.keys().filter(path => filterRegExpByFilename.test(path))); }); - return specificFiles.map(context); + console.log( + '\n\nRunning test cases from ' + + specificFiles.length + + ' files matching pattern: ' + + pattern + ); + + return specificFiles.map(file => contexts[0](file)); } else { const specificFiles = []; From 32216efb1bf943d6b170072ffc2cb2c9ff2acf47 Mon Sep 17 00:00:00 2001 From: Bryan Valverde U Date: Fri, 6 Feb 2026 15:43:06 -0600 Subject: [PATCH 2/2] Update packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../paste/WacComponents/processPastedContentWacComponents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts b/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts index ebb3a9c9841a..a780e1beb109 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/WacComponents/processPastedContentWacComponents.ts @@ -81,7 +81,7 @@ const wacElementProcessor: ElementProcessor = ( if ( TEMP_ELEMENTS_CLASSES.some(className => element.classList.contains(className)) || - // This is needed to remove some temporary End of paragraph elements that WAC sometimes preserve + // This is needed to remove some temporary End of paragraph elements that WAC sometimes preserves (element.classList.contains(SELECTED_CLASS) && element.classList.contains(END_OF_PARAGRAPH)) ) { return;