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/processPastedContentFromExcel.ts b/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts index 08a6e61a69f..330525ecf60 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts @@ -12,23 +12,29 @@ 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'; +const DEFAULT_BORDER_STYLE = 'solid 1px #d4d4d4'; /** * @internal * Convert pasted content from Excel, add borders when source doc doesn't have a border * @param event The BeforePaste event + * @param domCreator The DOM creator + * @param allowExcelNoBorderTable Allow table copied from Excel without border + * @param isNativeEvent Whether the event is native event */ - export function processPastedContentFromExcel( event: BeforePasteEvent, domCreator: DOMCreator, - allowExcelNoBorderTable?: boolean + allowExcelNoBorderTable: boolean, + isNativeEvent: boolean ) { const { fragment, htmlBefore, htmlAfter, clipboardData } = event; - validateExcelFragment(fragment, domCreator, htmlBefore, clipboardData, htmlAfter); + // For non native event we already validated that the content contains a table + if (isNativeEvent) { + validateExcelFragment(fragment, domCreator, htmlBefore, clipboardData, htmlAfter); + } // For Excel Online const firstChild = fragment.firstChild; @@ -54,40 +60,13 @@ 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, + isNativeEvent /* 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 @@ -148,3 +127,50 @@ export function excelHandler(html: string, htmlBefore: string): string { return html; } } + +/** + * @internal + * Exported only for unit test + */ +export function setupExcelTableHandlers( + event: BeforePasteEvent, + allowExcelNoBorderTable: boolean | undefined, + isNativeEvent: boolean +) { + addParser(event.domToModelOption, 'tableCell', (format, element) => { + if ( + !allowExcelNoBorderTable && + (element.style.borderStyle === 'none' || + (!isNativeEvent && 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..a68a3c4a26a 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts @@ -106,12 +106,14 @@ export class PastePlugin implements EditorPlugin { break; case 'excelOnline': case 'excelDesktop': + case 'excelNonNativeEvent': if (pasteType === 'normal' || pasteType === 'mergeFormat') { // Handle HTML copied from Excel processPastedContentFromExcel( event, this.editor.getDOMCreator(), - this.allowExcelNoBorderTable + !!this.allowExcelNoBorderTable, + pasteSource != 'excelNonNativeEvent' /* isNativeEvent */ ); } break; 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..d6357495297 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts @@ -0,0 +1,25 @@ +import type { BeforePasteEvent, ClipboardData } from 'roosterjs-content-model-types'; + +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/e2e/cmPasteFromExcelNonNative.ts b/packages/roosterjs-content-model-plugins/test/paste/e2e/cmPasteFromExcelNonNative.ts new file mode 100644 index 00000000000..f865fa6a1e9 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/e2e/cmPasteFromExcelNonNative.ts @@ -0,0 +1,119 @@ +import * as processPastedContentFromExcel from '../../../lib/paste/Excel/processPastedContentFromExcel'; +import { excelContentFromNonNativeTemplate } from './htmlTemplates/htmlFromExcelNonNative'; +import { expectEqual, initEditor } from './testUtils'; +import { paste } from 'roosterjs-content-model-core'; +import type { ClipboardData, IEditor } from 'roosterjs-content-model-types'; + +describe('Paste from Excel non native', () => { + let editor: IEditor = undefined!; + + beforeEach(() => { + editor = initEditor('Paste_from_Excel_non_native'); + }); + + afterEach(() => { + document.getElementById('Paste_from_Excel_non_native')?.remove(); + }); + + it('E2E', () => { + const clipboardData = { + types: ['web data/shadow-workbook', 'image/png', 'text/plain', 'text/html'], + text: '\r\n', + image: {}, + files: [{}], + rawHtml: excelContentFromNonNativeTemplate, + customValues: {}, + pasteNativeEvent: true, + imageDataUri: null, + } as ClipboardData; + spyOn(processPastedContentFromExcel, 'processPastedContentFromExcel').and.callThrough(); + + paste(editor, clipboardData); + const model = editor.getContentModelCopy('connected'); + + expect(processPastedContentFromExcel.processPastedContentFromExcel).toHaveBeenCalled(); + + expectEqual(model, { + blockGroupType: 'Document', + blocks: [ + { + widths: jasmine.anything() as any, + rows: [ + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [], + format: { + borderTop: '1px solid rgb(212, 212, 212)', + borderRight: '1px solid rgb(212, 212, 212)', + borderBottom: '1px solid rgb(212, 212, 212)', + borderLeft: '1px solid rgb(212, 212, 212)', + width: '50pt', + height: '14.4pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [], + format: { + borderTop: '1px solid rgb(212, 212, 212)', + borderRight: '1px solid rgb(212, 212, 212)', + borderBottom: '1px solid rgb(212, 212, 212)', + borderLeft: '1px solid rgb(212, 212, 212)', + width: '50pt', + }, + dataset: {}, + }, + ], + format: {}, + }, + ], + blockType: 'Table', + format: { + width: '100pt', + useBorderBox: true, + borderCollapse: true, + }, + dataset: {}, + }, + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: { + backgroundColor: '', + fontFamily: '', + fontSize: '', + fontWeight: '', + italic: false, + letterSpacing: '', + lineHeight: '', + strikethrough: false, + superOrSubScriptSequence: '', + textColor: '', + underline: false, + }, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + blockType: 'Paragraph', + format: {}, + }, + ], + format: {}, + }); + }); +}); diff --git a/packages/roosterjs-content-model-plugins/test/paste/e2e/htmlTemplates/htmlFromExcelNonNative.ts b/packages/roosterjs-content-model-plugins/test/paste/e2e/htmlTemplates/htmlFromExcelNonNative.ts new file mode 100644 index 00000000000..1336f064f07 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/e2e/htmlTemplates/htmlFromExcelNonNative.ts @@ -0,0 +1,5 @@ +export const excelContentFromNonNativeTemplate = ` + + +
`; 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 96% 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..3e8bebfdb51 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,7 @@ -import * as PastePluginFile from '../../lib/paste/Excel/processPastedContentFromExcel'; +import * as PastePluginFile from '../../../lib/paste/Excel/processPastedContentFromExcel'; 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, @@ -29,7 +29,12 @@ describe('processPastedContentFromExcelTest', () => { const event = createBeforePasteEventMock(fragment); event.clipboardData.html = source; - processPastedContentFromExcel(event, domCreator); + processPastedContentFromExcel( + event, + domCreator, + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ + ); const model = domToContentModel( fragment, @@ -352,7 +357,12 @@ describe('Do not run scenarios', () => { if (excelHandler) { spyOn(PastePluginFile, 'excelHandler').and.returnValue(excelHandler); } - processPastedContentFromExcel(event, domCreator); + processPastedContentFromExcel( + event, + domCreator, + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ + ); // Assert while (div.firstChild) { 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..b41b21d605d 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,12 @@ -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 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 +38,7 @@ describe('Content Model Paste Plugin Test', () => { eventType: 'beforePaste', clipboardData: { html: '', + types: [], }, fragment: document.createDocumentFragment(), htmlBefore: '', @@ -77,7 +78,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /*allowExcelNoBorderTable*/, + true ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 3); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); @@ -94,7 +96,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).not.toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(0); @@ -110,7 +113,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); @@ -126,7 +130,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); @@ -173,6 +178,23 @@ describe('Content Model Paste Plugin Test', () => { expect(Object.keys(event.domToModelOption.styleSanitizers).length).toEqual(4); }); + it('excelNonNativeEvent', () => { + spyOn(getPasteSource, 'getPasteSource').and.returnValue('excelNonNativeEvent'); + spyOn(ExcelFile, 'processPastedContentFromExcel').and.callThrough(); + + plugin.initialize(editor); + plugin.onPluginEvent(event); + + expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( + event, + domCreator, + false /* allowExcelNoBorderTable */, + false /* isNativeEvent */ + ); + expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); + expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); + }); + 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,