From c5337afd9206aff60e503c1353ee223faaf5e841 Mon Sep 17 00:00:00 2001 From: "SOUTHAMERICA\\bvalverde" Date: Tue, 18 Feb 2025 11:08:14 -0600 Subject: [PATCH] Implement Excel non-native event handling and related utilities --- .../controlsV2/demoButtons/pasteButton.ts | 15 ++---- .../handleExcelContentFromNotNativeEvent.ts | 12 +++++ .../Excel/processPastedContentFromExcel.ts | 44 ++-------------- .../paste/Excel/setupExcelTableHandlers.ts | 51 +++++++++++++++++++ .../lib/paste/PastePlugin.ts | 4 ++ .../pasteSourceValidations/getPasteSource.ts | 5 +- .../isExcelNonNativeEvent.ts | 18 +++++++ .../test/paste/createBeforePasteEventMock.ts | 25 +++++++++ ...andleExcelContentFromNotNativeEventTest.ts | 38 ++++++++++++++ .../processPastedContentFromExcelTest.ts | 11 ++-- .../{ => excel}/validateExcelFragmentTest.ts | 2 +- .../getPasteSourceTest.ts | 24 ++++++++- .../ContentModelPastePluginTest.ts | 39 +++++++++++--- .../processPastedContentFromPowerPointTest.ts | 2 +- .../{ => utils}/deprecatedColorParserTest.ts | 2 +- .../paste/{ => utils}/getStyleMetadataTest.ts | 2 +- .../test/paste/{ => utils}/linkParserTest.ts | 4 +- .../processPastedContentFromWacTest.ts | 6 +-- ...processPastedContentFromWordDesktopTest.ts | 34 ++----------- 19 files changed, 233 insertions(+), 105 deletions(-) create mode 100644 packages/roosterjs-content-model-plugins/lib/paste/Excel/handleExcelContentFromNotNativeEvent.ts create mode 100644 packages/roosterjs-content-model-plugins/lib/paste/Excel/setupExcelTableHandlers.ts create mode 100644 packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts create mode 100644 packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts create mode 100644 packages/roosterjs-content-model-plugins/test/paste/excel/handleExcelContentFromNotNativeEventTest.ts rename packages/roosterjs-content-model-plugins/test/paste/{ => excel}/processPastedContentFromExcelTest.ts (97%) rename packages/roosterjs-content-model-plugins/test/paste/{ => excel}/validateExcelFragmentTest.ts (94%) rename packages/roosterjs-content-model-plugins/test/paste/{ => plugin}/ContentModelPastePluginTest.ts (83%) rename packages/roosterjs-content-model-plugins/test/paste/{ => powerPoint}/processPastedContentFromPowerPointTest.ts (97%) rename packages/roosterjs-content-model-plugins/test/paste/{ => utils}/deprecatedColorParserTest.ts (90%) rename packages/roosterjs-content-model-plugins/test/paste/{ => utils}/getStyleMetadataTest.ts (98%) rename packages/roosterjs-content-model-plugins/test/paste/{ => utils}/linkParserTest.ts (97%) rename packages/roosterjs-content-model-plugins/test/paste/{ => word}/processPastedContentFromWacTest.ts (99%) rename packages/roosterjs-content-model-plugins/test/paste/{ => word}/processPastedContentFromWordDesktopTest.ts (99%) diff --git a/demo/scripts/controlsV2/demoButtons/pasteButton.ts b/demo/scripts/controlsV2/demoButtons/pasteButton.ts index dc507f5ac79..071f5bf824d 100644 --- a/demo/scripts/controlsV2/demoButtons/pasteButton.ts +++ b/demo/scripts/controlsV2/demoButtons/pasteButton.ts @@ -45,19 +45,14 @@ const createDataTransfer = ( const createDataTransferItems = (data: ClipboardItems) => { const isTEXT = (type: string) => type.startsWith('text/'); - const isIMAGE = (type: string) => type.startsWith('image/'); const dataTransferItems: Promise[] = []; data.forEach(item => { item.types.forEach(type => { - if (isTEXT(type) || isIMAGE(type)) { - dataTransferItems.push( - item - .getType(type) - .then(blob => - createDataTransfer(isTEXT(type) ? 'string' : 'file', type, blob) - ) - ); - } + dataTransferItems.push( + item + .getType(type) + .then(blob => createDataTransfer(isTEXT(type) ? 'string' : 'file', type, blob)) + ); }); }); return dataTransferItems; diff --git a/packages/roosterjs-content-model-plugins/lib/paste/Excel/handleExcelContentFromNotNativeEvent.ts b/packages/roosterjs-content-model-plugins/lib/paste/Excel/handleExcelContentFromNotNativeEvent.ts new file mode 100644 index 00000000000..074da97b193 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/lib/paste/Excel/handleExcelContentFromNotNativeEvent.ts @@ -0,0 +1,12 @@ +import { setupExcelTableHandlers } from './setupExcelTableHandlers'; +import type { BeforePasteEvent } from 'roosterjs-content-model-types'; + +/** + * @internal + */ +export function handleExcelContentFromNotNativeEvent( + event: BeforePasteEvent, + allowExcelNoBorderTable: boolean +) { + setupExcelTableHandlers(event, allowExcelNoBorderTable, false /* handleForNativeEvent */); +} diff --git a/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts b/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts index 08a6e61a69f..1778f9ec841 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts @@ -1,18 +1,11 @@ -import { addParser } from '../utils/addParser'; import { isNodeOfType, moveChildNodes } from 'roosterjs-content-model-dom'; -import { setProcessor } from '../utils/setProcessor'; -import type { - BeforePasteEvent, - ClipboardData, - DOMCreator, - ElementProcessor, -} from 'roosterjs-content-model-types'; +import { setupExcelTableHandlers } from './setupExcelTableHandlers'; +import type { BeforePasteEvent, ClipboardData, DOMCreator } from 'roosterjs-content-model-types'; const LAST_TD_END_REGEX = /<\/\s*td\s*>((?!<\/\s*tr\s*>)[\s\S])*$/i; const LAST_TR_END_REGEX = /<\/\s*tr\s*>((?!<\/\s*table\s*>)[\s\S])*$/i; const LAST_TR_REGEX = /]*>[^<]*/i; const LAST_TABLE_REGEX = /]*>[^<]*/i; -const DEFAULT_BORDER_STYLE = 'solid 1px #d4d4d4'; const TABLE_SELECTOR = 'table'; /** @@ -54,40 +47,9 @@ export function processPastedContentFromExcel( } } - addParser(event.domToModelOption, 'tableCell', (format, element) => { - if (!allowExcelNoBorderTable && element.style.borderStyle === 'none') { - format.borderBottom = DEFAULT_BORDER_STYLE; - format.borderLeft = DEFAULT_BORDER_STYLE; - format.borderRight = DEFAULT_BORDER_STYLE; - format.borderTop = DEFAULT_BORDER_STYLE; - } - }); - - setProcessor(event.domToModelOption, 'child', childProcessor); + setupExcelTableHandlers(event, allowExcelNoBorderTable, true /* handleForNativeEvent */); } -/** - * @internal - * Exported only for unit test - */ -export const childProcessor: ElementProcessor = (group, element, context) => { - const segmentFormat = { ...context.segmentFormat }; - if ( - group.blockGroupType === 'TableCell' && - group.format.textColor && - !context.segmentFormat.textColor - ) { - context.segmentFormat.textColor = group.format.textColor; - } - - context.defaultElementProcessors.child(group, element, context); - - if (group.blockGroupType === 'TableCell' && group.format.textColor) { - context.segmentFormat = segmentFormat; - delete group.format.textColor; - } -}; - /** * @internal * Exported only for unit test diff --git a/packages/roosterjs-content-model-plugins/lib/paste/Excel/setupExcelTableHandlers.ts b/packages/roosterjs-content-model-plugins/lib/paste/Excel/setupExcelTableHandlers.ts new file mode 100644 index 00000000000..d6cc486864e --- /dev/null +++ b/packages/roosterjs-content-model-plugins/lib/paste/Excel/setupExcelTableHandlers.ts @@ -0,0 +1,51 @@ +import { addParser } from '../utils/addParser'; +import { setProcessor } from '../utils/setProcessor'; +import type { BeforePasteEvent, ElementProcessor } from 'roosterjs-content-model-types'; + +const DEFAULT_BORDER_STYLE = 'solid 1px #d4d4d4'; + +/** + * @internal + */ +export function setupExcelTableHandlers( + event: BeforePasteEvent, + allowExcelNoBorderTable: boolean | undefined, + handleForNativeEvent: boolean +) { + addParser(event.domToModelOption, 'tableCell', (format, element) => { + if ( + !allowExcelNoBorderTable && + (element.style.borderStyle === 'none' || + (!handleForNativeEvent && element.style.borderStyle == '')) + ) { + format.borderBottom = DEFAULT_BORDER_STYLE; + format.borderLeft = DEFAULT_BORDER_STYLE; + format.borderRight = DEFAULT_BORDER_STYLE; + format.borderTop = DEFAULT_BORDER_STYLE; + } + }); + + setProcessor(event.domToModelOption, 'child', childProcessor); +} + +/** + * @internal + * Exported only for unit test + */ +export const childProcessor: ElementProcessor = (group, element, context) => { + const segmentFormat = { ...context.segmentFormat }; + if ( + group.blockGroupType === 'TableCell' && + group.format.textColor && + !context.segmentFormat.textColor + ) { + context.segmentFormat.textColor = group.format.textColor; + } + + context.defaultElementProcessors.child(group, element, context); + + if (group.blockGroupType === 'TableCell' && group.format.textColor) { + context.segmentFormat = segmentFormat; + delete group.format.textColor; + } +}; diff --git a/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts b/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts index 5fea7005843..1de361eb6c0 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts @@ -4,6 +4,7 @@ import { chainSanitizerCallback } from './utils/chainSanitizerCallback'; import { DefaultSanitizers } from './DefaultSanitizers'; import { deprecatedBorderColorParser } from './utils/deprecatedColorParser'; import { getPasteSource } from './pasteSourceValidations/getPasteSource'; +import { handleExcelContentFromNotNativeEvent } from './Excel/handleExcelContentFromNotNativeEvent'; import { parseLink } from './utils/linkParser'; import { PastePropertyNames } from './pasteSourceValidations/constants'; import { processPastedContentFromExcel } from './Excel/processPastedContentFromExcel'; @@ -123,6 +124,9 @@ export class PastePlugin implements EditorPlugin { case 'powerPointDesktop': processPastedContentFromPowerPoint(event, this.editor.getDOMCreator()); break; + case 'excelNonNativeEvent': + handleExcelContentFromNotNativeEvent(event, !!this.allowExcelNoBorderTable); + break; } addParser(event.domToModelOption, 'link', parseLink); diff --git a/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts index 031a57f338e..5d7cf90ce1f 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts @@ -1,5 +1,6 @@ import { documentContainWacElements } from './documentContainWacElements'; import { isExcelDesktopDocument } from './isExcelDesktopDocument'; +import { isExcelNotNativeEvent } from './isExcelNonNativeEvent'; import { isExcelOnlineDocument } from './isExcelOnlineDocument'; import { isGoogleSheetDocument } from './isGoogleSheetDocument'; import { isPowerPointDesktopDocument } from './isPowerPointDesktopDocument'; @@ -29,7 +30,8 @@ export type KnownPasteSourceType = | 'googleSheets' | 'wacComponents' | 'default' - | 'singleImage'; + | 'singleImage' + | 'excelNonNativeEvent'; /** * @internal @@ -44,6 +46,7 @@ const getSourceFunctions = new Map([ ['wacComponents', documentContainWacElements], ['googleSheets', isGoogleSheetDocument], ['singleImage', shouldConvertToSingleImage], + ['excelNonNativeEvent', isExcelNotNativeEvent], ]); /** diff --git a/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts new file mode 100644 index 00000000000..684362196d8 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts @@ -0,0 +1,18 @@ +import type { GetSourceFunction, GetSourceInputParams } from './getPasteSource'; + +const ShadowWorkbookClipboardType = 'web data/shadow-workbook'; + +/** + * @internal + * When the clipboard content is retrieved programatically, the clipboard html does not contain the usual + * attributes we use to determine if the content is from Excel. This function is used to handle that case. + */ +export const isExcelNotNativeEvent: GetSourceFunction = (props: GetSourceInputParams) => { + const { clipboardData } = props; + + return ( + clipboardData.types.includes(ShadowWorkbookClipboardType) && + clipboardData.htmlFirstLevelChildTags?.length == 1 && + clipboardData.htmlFirstLevelChildTags[0] == 'TABLE' + ); +}; diff --git a/packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts b/packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts new file mode 100644 index 00000000000..85abcc11432 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts @@ -0,0 +1,25 @@ +import { BeforePasteEvent, ClipboardData } from 'roosterjs'; + +export function createBeforePasteEventMock( + fragment: DocumentFragment, + htmlBefore: string = '' +): BeforePasteEvent { + return { + eventType: 'beforePaste', + clipboardData: {}, + fragment: fragment, + htmlBefore, + htmlAfter: '', + htmlAttributes: {}, + pasteType: 'normal', + domToModelOption: { + additionalAllowedTags: [], + additionalDisallowedTags: [], + additionalFormatParsers: {}, + attributeSanitizers: {}, + formatParserOverride: {}, + processorOverride: {}, + styleSanitizers: {}, + }, + }; +} diff --git a/packages/roosterjs-content-model-plugins/test/paste/excel/handleExcelContentFromNotNativeEventTest.ts b/packages/roosterjs-content-model-plugins/test/paste/excel/handleExcelContentFromNotNativeEventTest.ts new file mode 100644 index 00000000000..0055d7ee885 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/excel/handleExcelContentFromNotNativeEventTest.ts @@ -0,0 +1,38 @@ +import * as addParserFile from '../../../lib/paste/utils/addParser'; +import * as setProcessorFile from '../../../lib/paste/utils/setProcessor'; +import * as setupExcelTableHandlersFile from '../../../lib/paste/Excel/setupExcelTableHandlers'; +import { BeforePasteEvent } from 'roosterjs-content-model-types'; +import { handleExcelContentFromNotNativeEvent } from '../../../lib/paste/Excel/handleExcelContentFromNotNativeEvent'; + +describe('handleExcelContentFromNotNativeEvent', () => { + beforeEach(() => { + spyOn(setupExcelTableHandlersFile, 'setupExcelTableHandlers').and.callThrough(); + spyOn(addParserFile, 'addParser').and.callFake(() => {}); + spyOn(setProcessorFile, 'setProcessor').and.callFake(() => {}); + }); + + it('should handle Excel content from non-native event', () => { + const fragment = document.createDocumentFragment(); + const table = document.createElement('table'); + fragment.appendChild(table); + + const event: BeforePasteEvent = { + fragment, + clipboardData: { + types: ['web data/shadow-workbook'], + } as any, + } as any; + + const allowExcelNoBorderTable = true; + + handleExcelContentFromNotNativeEvent(event, allowExcelNoBorderTable); + + expect(setupExcelTableHandlersFile.setupExcelTableHandlers).toHaveBeenCalledWith( + event, + allowExcelNoBorderTable, + false /* handleForNativeEvemt */ + ); + expect(setProcessorFile.setProcessor).toHaveBeenCalledTimes(1); + expect(addParserFile.addParser).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromExcelTest.ts b/packages/roosterjs-content-model-plugins/test/paste/excel/processPastedContentFromExcelTest.ts similarity index 97% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromExcelTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/excel/processPastedContentFromExcelTest.ts index 42cd593c605..f09258e0f40 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromExcelTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/excel/processPastedContentFromExcelTest.ts @@ -1,7 +1,8 @@ -import * as PastePluginFile from '../../lib/paste/Excel/processPastedContentFromExcel'; +import * as PastePluginFile from '../../../lib/paste/Excel/processPastedContentFromExcel'; +import * as setupExcelTableHandlersFile from '../../../lib/paste/Excel/setupExcelTableHandlers'; import { ContentModelDocument, DOMCreator } from 'roosterjs-content-model-types'; -import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; -import { processPastedContentFromExcel } from '../../lib/paste/Excel/processPastedContentFromExcel'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { processPastedContentFromExcel } from '../../../lib/paste/Excel/processPastedContentFromExcel'; import { contentModelToDom, createDomToModelContext, @@ -397,7 +398,7 @@ describe('childProcessorTest', () => { const tableCell = createTableCell(); tableCell.format.textColor = 'black'; - PastePluginFile.childProcessor(tableCell, element, context); + setupExcelTableHandlersFile.childProcessor(tableCell, element, context); expect(tableCell.format.textColor).toBeUndefined(); expect(context.segmentFormat.textColor).toBeUndefined(); @@ -417,7 +418,7 @@ describe('childProcessorTest', () => { const tableCell = createTableCell(); tableCell.format.textColor = 'black'; - PastePluginFile.childProcessor(tableCell, element, context); + setupExcelTableHandlersFile.childProcessor(tableCell, element, context); expect(tableCell.format.textColor).toBeUndefined(); expect(context.segmentFormat.textColor).toEqual('blue'); diff --git a/packages/roosterjs-content-model-plugins/test/paste/validateExcelFragmentTest.ts b/packages/roosterjs-content-model-plugins/test/paste/excel/validateExcelFragmentTest.ts similarity index 94% rename from packages/roosterjs-content-model-plugins/test/paste/validateExcelFragmentTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/excel/validateExcelFragmentTest.ts index 8bb38d34982..c18b56039d2 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/validateExcelFragmentTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/excel/validateExcelFragmentTest.ts @@ -1,5 +1,5 @@ import { ClipboardData, DOMCreator } from 'roosterjs-content-model-types'; -import { validateExcelFragment } from '../../lib/paste/Excel/processPastedContentFromExcel'; +import { validateExcelFragment } from '../../../lib/paste/Excel/processPastedContentFromExcel'; describe('validateExcelFragment', () => { let domCreator: DOMCreator; diff --git a/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts b/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts index 136f4f36eb3..1aa7c39c160 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts @@ -51,6 +51,13 @@ describe('getPasteSourceTest | ', () => { const result = getPasteSource(defaultParam(), false /* shouldConvertSingleImage */); expect(result).toBe('default'); }); + it('Is Excel Non-Native Event', () => { + const result = getPasteSource( + excelNonNativeEventParam(), + false /* shouldConvertSingleImage */ + ); + expect(result).toBe('excelNonNativeEvent'); + }); }); function wacParam(): BeforePasteEvent { @@ -78,8 +85,9 @@ function googleSheetParam(): BeforePasteEvent { function converSingleImageParam(): BeforePasteEvent { const fragment = document.createDocumentFragment(); - const clipboardData = { + const clipboardData = { htmlFirstLevelChildTags: ['IMG'], + types: [], }; return { @@ -111,5 +119,17 @@ function wordParam(): BeforePasteEvent { function defaultParam(): BeforePasteEvent { const fragment = document.createDocumentFragment(); - return { htmlAttributes: {}, fragment, clipboardData: {} }; + return { htmlAttributes: {}, fragment, clipboardData: { types: [] } }; +} + +function excelNonNativeEventParam(): BeforePasteEvent { + const fragment = document.createDocumentFragment(); + + const clipboardData: ClipboardData = { + types: ['web data/shadow-workbook'], + rawHtml: '', + htmlFirstLevelChildTags: ['TABLE'], + } as any; + + return { fragment, clipboardData, htmlAttributes: {} }; } diff --git a/packages/roosterjs-content-model-plugins/test/paste/ContentModelPastePluginTest.ts b/packages/roosterjs-content-model-plugins/test/paste/plugin/ContentModelPastePluginTest.ts similarity index 83% rename from packages/roosterjs-content-model-plugins/test/paste/ContentModelPastePluginTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/plugin/ContentModelPastePluginTest.ts index 56f955ebafb..2e824c93492 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/ContentModelPastePluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/plugin/ContentModelPastePluginTest.ts @@ -1,12 +1,13 @@ -import * as addParser from '../../lib/paste/utils/addParser'; -import * as ExcelFile from '../../lib/paste/Excel/processPastedContentFromExcel'; -import * as getPasteSource from '../../lib/paste/pasteSourceValidations/getPasteSource'; -import * as PowerPointFile from '../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; -import * as setProcessor from '../../lib/paste/utils/setProcessor'; -import * as WacFile from '../../lib/paste/WacComponents/processPastedContentWacComponents'; +import * as addParser from '../../../lib/paste/utils/addParser'; +import * as ExcelFile from '../../../lib/paste/Excel/processPastedContentFromExcel'; +import * as getPasteSource from '../../../lib/paste/pasteSourceValidations/getPasteSource'; +import * as handleExcelContentFromNotNativeEventFile from '../../../lib/paste/Excel/handleExcelContentFromNotNativeEvent'; +import * as PowerPointFile from '../../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; +import * as setProcessor from '../../../lib/paste/utils/setProcessor'; +import * as WacFile from '../../../lib/paste/WacComponents/processPastedContentWacComponents'; import { BeforePasteEvent, DOMCreator, IEditor } from 'roosterjs-content-model-types'; -import { PastePlugin } from '../../lib/paste/PastePlugin'; -import { PastePropertyNames } from '../../lib/paste/pasteSourceValidations/constants'; +import { PastePlugin } from '../../../lib/paste/PastePlugin'; +import { PastePropertyNames } from '../../../lib/paste/pasteSourceValidations/constants'; const trustedHTMLHandler = (val: string) => val; const domCreator: DOMCreator = { @@ -38,6 +39,7 @@ describe('Content Model Paste Plugin Test', () => { eventType: 'beforePaste', clipboardData: { html: '', + types: [], }, fragment: document.createDocumentFragment(), htmlBefore: '', @@ -173,6 +175,27 @@ describe('Content Model Paste Plugin Test', () => { expect(Object.keys(event.domToModelOption.styleSanitizers).length).toEqual(4); }); + it('excelNonNativeEvent', () => { + spyOn(getPasteSource, 'getPasteSource').and.returnValue('excelNonNativeEvent'); + spyOn( + handleExcelContentFromNotNativeEventFile, + 'handleExcelContentFromNotNativeEvent' + ).and.callFake(() => {}); + + plugin.initialize(editor); + plugin.onPluginEvent(event); + + expect( + handleExcelContentFromNotNativeEventFile.handleExcelContentFromNotNativeEvent + ).toHaveBeenCalled(); + expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED); + expect(setProcessor.setProcessor).toHaveBeenCalledTimes(0); + expect(event.domToModelOption.additionalDisallowedTags.length).toEqual(0); + expect(event.domToModelOption.additionalAllowedTags.length).toEqual(0); + expect(Object.keys(event.domToModelOption.attributeSanitizers).length).toEqual(0); + expect(Object.keys(event.domToModelOption.styleSanitizers).length).toEqual(4); + }); + it('Default with customized Sanitizing option', () => { spyOn(getPasteSource, 'getPasteSource').and.returnValue('default'); diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromPowerPointTest.ts b/packages/roosterjs-content-model-plugins/test/paste/powerPoint/processPastedContentFromPowerPointTest.ts similarity index 97% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromPowerPointTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/powerPoint/processPastedContentFromPowerPointTest.ts index 73fc764fb6e..31f6240ed84 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromPowerPointTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/powerPoint/processPastedContentFromPowerPointTest.ts @@ -1,5 +1,5 @@ import * as moveChildNodes from 'roosterjs-content-model-dom/lib/domUtils/moveChildNodes'; -import { processPastedContentFromPowerPoint } from '../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; +import { processPastedContentFromPowerPoint } from '../../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; import type { BeforePasteEvent, ClipboardData, DOMCreator } from 'roosterjs-content-model-types'; const getPasteEvent = (): BeforePasteEvent => { diff --git a/packages/roosterjs-content-model-plugins/test/paste/deprecatedColorParserTest.ts b/packages/roosterjs-content-model-plugins/test/paste/utils/deprecatedColorParserTest.ts similarity index 90% rename from packages/roosterjs-content-model-plugins/test/paste/deprecatedColorParserTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/utils/deprecatedColorParserTest.ts index 55e8efa4263..3de671cd2f4 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/deprecatedColorParserTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/utils/deprecatedColorParserTest.ts @@ -1,4 +1,4 @@ -import { deprecatedBorderColorParser } from '../../lib/paste/utils/deprecatedColorParser'; +import { deprecatedBorderColorParser } from '../../../lib/paste/utils/deprecatedColorParser'; const DeprecatedColors: string[] = [ 'activeborder', diff --git a/packages/roosterjs-content-model-plugins/test/paste/getStyleMetadataTest.ts b/packages/roosterjs-content-model-plugins/test/paste/utils/getStyleMetadataTest.ts similarity index 98% rename from packages/roosterjs-content-model-plugins/test/paste/getStyleMetadataTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/utils/getStyleMetadataTest.ts index 0aae54bd19c..0250615ad1f 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/getStyleMetadataTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/utils/getStyleMetadataTest.ts @@ -1,5 +1,5 @@ import { BeforePasteEvent } from 'roosterjs-content-model-types'; -import { getStyleMetadata } from '../../lib/paste/WordDesktop/getStyleMetadata'; +import { getStyleMetadata } from '../../../lib/paste/WordDesktop/getStyleMetadata'; const domCreator = { htmlToDOM: (html: string) => new DOMParser().parseFromString(html, 'text/html'), diff --git a/packages/roosterjs-content-model-plugins/test/paste/linkParserTest.ts b/packages/roosterjs-content-model-plugins/test/paste/utils/linkParserTest.ts similarity index 97% rename from packages/roosterjs-content-model-plugins/test/paste/linkParserTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/utils/linkParserTest.ts index a1be1151bf5..deb45736613 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/linkParserTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/utils/linkParserTest.ts @@ -1,6 +1,6 @@ import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; -import { parseLink } from '../../lib/paste/utils/linkParser'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { parseLink } from '../../../lib/paste/utils/linkParser'; import { contentModelToDom, createDomToModelContext, diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWacTest.ts b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts similarity index 99% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWacTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts index 50ff7a50e80..76e099175e2 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWacTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts @@ -1,9 +1,9 @@ import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; -import { expectEqual } from './e2e/testUtils'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { expectEqual } from '../e2e/testUtils'; import { itChromeOnly } from 'roosterjs-content-model-dom/test/testUtils'; import { pasteDisplayFormatParser } from 'roosterjs-content-model-core/lib/override/pasteDisplayFormatParser'; -import { processPastedContentWacComponents } from '../../lib/paste/WacComponents/processPastedContentWacComponents'; +import { processPastedContentWacComponents } from '../../../lib/paste/WacComponents/processPastedContentWacComponents'; import { listItemMetadataApplier, listLevelMetadataApplier, diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWordDesktopTest.ts b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWordDesktopTest.ts similarity index 99% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWordDesktopTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWordDesktopTest.ts index 7b301c86fe8..f45d3510504 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWordDesktopTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWordDesktopTest.ts @@ -1,8 +1,8 @@ -import * as getStyleMetadata from '../../lib/paste/WordDesktop/getStyleMetadata'; -import { BeforePasteEvent, ClipboardData } from 'roosterjs-content-model-types'; -import { expectEqual } from './e2e/testUtils'; -import { processPastedContentFromWordDesktop } from '../../lib/paste/WordDesktop/processPastedContentFromWordDesktop'; -import { WordMetadata } from '../../lib/paste/WordDesktop/WordMetadata'; +import * as getStyleMetadata from '../../../lib/paste/WordDesktop/getStyleMetadata'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { expectEqual } from '../e2e/testUtils'; +import { processPastedContentFromWordDesktop } from '../../../lib/paste/WordDesktop/processPastedContentFromWordDesktop'; +import { WordMetadata } from '../../../lib/paste/WordDesktop/WordMetadata'; import { createDomToModelContext, domToContentModel, @@ -5223,30 +5223,6 @@ describe('processPastedContentFromWordDesktopTest', () => { }); }); -export function createBeforePasteEventMock( - fragment: DocumentFragment, - htmlBefore: string = '' -): BeforePasteEvent { - return { - eventType: 'beforePaste', - clipboardData: {}, - fragment: fragment, - htmlBefore, - htmlAfter: '', - htmlAttributes: {}, - pasteType: 'normal', - domToModelOption: { - additionalAllowedTags: [], - additionalDisallowedTags: [], - additionalFormatParsers: {}, - attributeSanitizers: {}, - formatParserOverride: {}, - processorOverride: {}, - styleSanitizers: {}, - }, - }; -} - function createListElementFromWord( tag: string, content: string,