浏览代码

Merge branch 't/5' into t/8

Krzysztof Krztoń 7 年之前
父节点
当前提交
e9bd4caae0

+ 121 - 110
packages/ckeditor5-paste-from-office/src/filters/list.js

@@ -4,13 +4,12 @@
  */
 
 /**
- * @module pastefromoffice/filters/list
+ * @module paste-from-office/filters/list
  */
 
 import Element from '@ckeditor/ckeditor5-engine/src/view/element';
 import Matcher from '@ckeditor/ckeditor5-engine/src/view/matcher';
-import Position from '@ckeditor/ckeditor5-engine/src/view/position';
-import TreeWalker from '@ckeditor/ckeditor5-engine/src/view/treewalker';
+import Range from '@ckeditor/ckeditor5-engine/src/view/range';
 import UpcastWriter from '@ckeditor/ckeditor5-engine/src/view/upcastwriter';
 
 /**
@@ -21,60 +20,62 @@ import UpcastWriter from '@ckeditor/ckeditor5-engine/src/view/upcastwriter';
  *		<p class=MsoListParagraphCxSpFirst style='mso-list:l1 level1 lfo1'>...</p> // Paragraph based list.
  *		<h1 style='mso-list:l0 level1 lfo1'>...</h1> // Heading 1 based list.
  *
- * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} bodyView The view
- * structure which to transform.
- * @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} The view
- * structure instance with list-like elements transformed into semantic lists.
+ * @param {module:engine/view/documentfragment~DocumentFragment} documentFragment The view structure which to transform.
+ * @param {String} stylesString Styles from which list-like elements styling will be extracted.
  */
-export function paragraphsToLists( bodyView, stylesString ) {
-	const firstChild = bodyView.getChild( 0 );
-
-	if ( firstChild ) {
-		const listNodes = findAllListNodes( Position.createBefore( firstChild ) );
-		createLists( listNodes, stylesString );
+export function transformListItemLikeElementsIntoLists( documentFragment, stylesString ) {
+	if ( !documentFragment.childCount ) {
+		return;
 	}
 
-	return bodyView;
-}
+	const listLikeItems = findAllListItemLikeElements( documentFragment );
 
-// Writer used for View elements manipulation.
-const writer = new UpcastWriter();
+	if ( listLikeItems.length ) {
+		const writer = new UpcastWriter();
 
-// Matcher for finding list-like elements.
-const listMatcher = new Matcher( {
-	name: /^p|h\d+$/,
-	styles: {
-		'mso-list': /.*/
-	}
-} );
+		let currentList = null;
 
-// Matcher for finding `span` elements holding lists numbering/bullets.
-const listBulletMatcher = new Matcher( {
-	name: 'span',
-	styles: {
-		'mso-list': 'Ignore'
+		for ( let i = 0; i < listLikeItems.length; i++ ) {
+			if ( !currentList || listLikeItems[ i - 1 ].id !== listLikeItems[ i ].id ) {
+				const listStyle = findListStyle( listLikeItems[ i ], stylesString );
+				currentList = insertEmptyList( listStyle, listLikeItems[ i ].element, writer );
+			}
+
+			const listItem = transformElementIntoListItem( listLikeItems[ i ].element, writer );
+
+			writer.appendChild( listItem, currentList );
+		}
 	}
-} );
+}
 
-// Finds all list-like nodes starting from a given position.
+// Finds all list-like elements in a given document fragment.
 //
-// @param {module:engine/src/view/position~Position} startPosition Position from which to start looking.
-// @returns {Array.<Object>} Array of found list items. Each item is an object containing:
+// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment
+// in which to look for list-like nodes.
+// @returns {Array.<Object>} Array of found list-like items. Each item is an object containing:
 //
 //		* {module:engine/src/view/element~Element} element List-like element.
 //		* {Number} id List item id parsed from `mso-list` style (see `getListItemData()` function).
 //		* {Number} order List item creation order parsed from `mso-list` style (see `getListItemData()` function).
 //		* {Number} indent List item indentation level parsed from `mso-list` style (see `getListItemData()` function).
-function findAllListNodes( startPosition ) {
-	const treeWalker = new TreeWalker( { startPosition, ignoreElementEnd: true } );
+function findAllListItemLikeElements( documentFragment ) {
+	const range = Range.createIn( documentFragment );
+
+	// Matcher for finding list-like elements.
+	const listItemLikeElementsMatcher = new Matcher( {
+		name: /^p|h\d+$/,
+		styles: {
+			'mso-list': /.*/
+		}
+	} );
+
+	const listLikeItems = [];
 
-	// Find all list nodes.
-	const listNodes = [];
-	for ( const value of treeWalker ) {
-		if ( value.type === 'elementStart' && listMatcher.match( value.item ) ) {
+	for ( const value of range ) {
+		if ( value.type === 'elementStart' && listItemLikeElementsMatcher.match( value.item ) ) {
 			const itemData = getListItemData( value.item );
 
-			listNodes.push( {
+			listLikeItems.push( {
 				element: value.item,
 				id: itemData.id,
 				order: itemData.order,
@@ -83,39 +84,79 @@ function findAllListNodes( startPosition ) {
 		}
 	}
 
-	return listNodes;
+	return listLikeItems;
 }
 
-// Transforms given list-like nodes into semantic lists. As the function operates on a provided
-// {module:engine/src/view/element~Element elements}, it will modify the view structure to which those list elements belongs.
+// Extracts list item style from the provided CSS.
 //
-// @param {Array.<Object>} listItems Array containing list items data. Usually it is the output of `findAllListNodes()` function.
-// @param {String} styles CSS styles which may contain additional data about lists format.
-function createLists( listItems, styles ) {
-	if ( listItems.length ) {
-		let currentList = null;
-		let previousListItem = null;
-
-		for ( const listItem of listItems ) {
-			const listNode = listItem.element;
-
-			if ( !previousListItem || previousListItem.id !== listItem.id ) {
-				const listStyle = findListType( listItem, styles );
-				currentList = new Element( listStyle.type );
-				writer.insertChild( listNode.parent.getChildIndex( listNode ), currentList, listNode.parent );
-			}
+// List item style is extracted from CSS stylesheet. Each list with its specific style attribute
+// value (`mso-list:l1 level1 lfo1`) has its dedicated properties in a CSS stylesheet defined with a selector like:
+//
+// 		@list l1:level1 { ... }
+//
+// It contains `mso-level-number-format` property which defines list numbering/bullet style. If this property
+// is not defined it means default `decimal` numbering.
+//
+// Here CSS string representation is used as `mso-level-number-format` property is an invalid CSS property
+// and will be removed during CSS parsing.
+//
+// @param {Object} listLikeItem List-like item for which list style will be searched for. Usually
+// a result of `findAllListItemLikeElements()` function.
+// @param {String} stylesString CSS stylesheet.
+// @returns {Object} result
+// @returns {String} result.type List type, could be `ul` or `ol`.
+// @returns {String} result.style List style, for example: `decimal`, `lower-roman`, etc. It is extracted
+// directly from Word stylesheet without further processing and may be not compatible
+// with CSS `list-style-type` property accepted values.
+function findListStyle( listLikeItem, stylesString ) {
+	const listStyleRegexp = new RegExp( `@list l${ listLikeItem.id }:level${ listLikeItem.indent }\\s*({[^}]*)`, 'gi' );
+	const listStyleTypeRegex = /mso-level-number-format:([^;]*);/gi;
 
-			removeBulletElement( listNode );
+	const listStyleMatch = listStyleRegexp.exec( stylesString );
 
-			writer.appendChild( listNode, currentList );
-			writer.rename( 'li', listNode );
+	let listStyleType = 'decimal'; // Decimal is default one.
+	if ( listStyleMatch && listStyleMatch[ 1 ] ) {
+		const listStyleTypeMatch = listStyleTypeRegex.exec( listStyleMatch[ 1 ] );
 
-			previousListItem = listItem;
+		if ( listStyleTypeMatch && listStyleTypeMatch[ 1 ] ) {
+			listStyleType = listStyleTypeMatch[ 1 ].trim();
 		}
 	}
+
+	return {
+		type: listStyleType !== 'bullet' && listStyleType !== 'image' ? 'ol' : 'ul',
+		style: listStyleType
+	};
 }
 
-// Extracts list information from Word specific list style like:
+// Creates empty list of a given type and inserts it after a specified element.
+//
+// @param {Object} listStyle List style object which determines the type of newly created list.
+// Usually a result of `findListStyle()` function.
+// @param {module:engine/view/element~Element} element Element before which list is inserted.
+// @param {module:engine/view/upcastwriter~UpcastWriter} writer
+// @returns {module:engine/view/element~Element} Newly created list element.
+function insertEmptyList( listStyle, element, writer ) {
+	const list = new Element( listStyle.type );
+	const position = element.parent.getChildIndex( element );
+
+	writer.insertChild( position, list, element.parent );
+
+	return list;
+}
+
+// Transforms given element into a semantic list item. As the function operates on a provided
+// {module:engine/src/view/element~Element element} it will modify the view structure to which this element belongs.
+//
+// @param {module:engine/view/element~Element} element Element which will be transformed into list item.
+// @param {module:engine/view/upcastwriter~UpcastWriter} writer
+function transformElementIntoListItem( element, writer ) {
+	removeBulletElement( element, writer );
+
+	return writer.rename( 'li', element );
+}
+
+// Extracts list item information from Word specific list-like element style:
 //
 //		`style="mso-list:l1 level1 lfo1"`
 //
@@ -125,11 +166,11 @@ function createLists( listItems, styles ) {
 //		* `level1` is a list item indentation level,
 //		* `lfo1` is a list insertion order in a document.
 //
-// @param {module:engine/view/element~Element} element List-like element from which data is extracted.
+// @param {module:engine/view/element~Element} element Element from which style data is extracted.
 // @returns {Object} result
-// @returns {Number} result.id List id.
-// @returns {Number} result.order List creation order.
-// @returns {Number} result.indent List indentation level.
+// @returns {Number} result.id Parent list id.
+// @returns {Number} result.order List item creation order.
+// @returns {Number} result.indent List item indentation level.
 function getListItemData( element ) {
 	const data = {};
 	const listStyle = element.getStyle( 'mso-list' );
@@ -143,53 +184,23 @@ function getListItemData( element ) {
 	return data;
 }
 
-// Checks list item style based on a provided CSS.
-//
-// List item style is extracted from CSS stylesheet. Each list with its specific style attribute value (`mso-list:l1 level1 lfo1`)
-// has its dedicated properties in a CSS stylesheet defined with a selector like:
+// Removes span with a numbering/bullet from a given element.
 //
-// 		@list l1:level1 { ... }
-//
-// It contains `mso-level-number-format` property which defines list numbering/bullet style. If this property
-// is not defined it means default `decimal` numbering.
-//
-// Here CSS string representation is used as `mso-level-number-format` is invalid CSS property which gets removed during parsing.
-//
-// @param {Object} listItem List item for which list style will be searched for.
-// @param {String} styles CSS stylesheet.
-// @returns {Object} result
-// @returns {String} result.type Type of the list, could be `ul` or `ol`.
-// @returns {String} result.style List style like `decimal`, `lower-roman`, etc. It is passed directly from Word stylesheet
-// so may be not compatible with CSS `list-style-type` accepted values.
-function findListType( listItem, styles ) {
-	const listStyleRegexp = new RegExp( `@list l${ listItem.id }:level${ listItem.indent }\\s*({[^}]*)`, 'gi' );
-	const listStyleTypeRegex = /mso-level-number-format:([^;]*);/gi;
-
-	const listStyleMatch = listStyleRegexp.exec( styles );
-
-	let listStyleType = 'decimal'; // Decimal is default one.
-	if ( listStyleMatch && listStyleMatch[ 1 ] ) {
-		const listStyleTypeMatch = listStyleTypeRegex.exec( listStyleMatch[ 1 ] );
-
-		if ( listStyleTypeMatch && listStyleTypeMatch[ 1 ] ) {
-			listStyleType = listStyleTypeMatch[ 1 ].trim();
+// @param {module:engine/view/element~Element} element
+// @param {module:engine/view/upcastwriter~UpcastWriter} writer
+function removeBulletElement( element, writer ) {
+	// Matcher for finding `span` elements holding lists numbering/bullets.
+	const bulletMatcher = new Matcher( {
+		name: 'span',
+		styles: {
+			'mso-list': 'Ignore'
 		}
-	}
-
-	return {
-		type: listStyleType !== 'bullet' && listStyleType !== 'image' ? 'ol' : 'ul',
-		style: listStyleType
-	};
-}
+	} );
 
-// Removes span with a numbering/bullet from the given list element.
-//
-// @param {module:engine/view/element~Element} listElement
-function removeBulletElement( listElement ) {
-	const treeWalker = new TreeWalker( { startPosition: Position.createBefore( listElement.getChild( 0 ) ), ignoreElementEnd: true } );
+	const range = Range.createIn( element );
 
-	for ( const value of treeWalker ) {
-		if ( value.type === 'elementStart' && listBulletMatcher.match( value.item ) ) {
+	for ( const value of range ) {
+		if ( value.type === 'elementStart' && bulletMatcher.match( value.item ) ) {
 			writer.remove( value.item );
 		}
 	}

+ 10 - 9
packages/ckeditor5-paste-from-office/src/filters/utils.js

@@ -4,7 +4,7 @@
  */
 
 /**
- * @module pastefromoffice/filters/utils
+ * @module paste-from-office/filters/utils
  */
 
 /* globals DOMParser */
@@ -12,11 +12,8 @@
 import DomConverter from '@ckeditor/ckeditor5-engine/src/view/domconverter';
 import { NBSP_FILLER } from '@ckeditor/ckeditor5-engine/src/view/filler';
 
-const domParser = new DOMParser();
-const domConverter = new DomConverter( { blockFiller: NBSP_FILLER } );
-
 /**
- * Parses provided HTML extracting contents of `body` and `style` tags.
+ * Parses provided HTML extracting contents of `<body>` and `<style>` tags.
  *
  * @param {String} htmlString HTML string to be parsed.
  * @returns {Object} result
@@ -28,6 +25,8 @@ const domConverter = new DomConverter( { blockFiller: NBSP_FILLER } );
  * @returns {String} result.stylesString All `style` tags contents combined in the order of occurrence into one string.
  */
 export function parseHtml( htmlString ) {
+	const domParser = new DOMParser();
+
 	// Parse htmlString as native Document object.
 	const htmlDocument = domParser.parseFromString( normalizeEndTagsPrecedingSpace( htmlString ), 'text/html' );
 
@@ -55,6 +54,7 @@ export function parseHtml( htmlString ) {
 // @param {Document} htmlDocument Native `Document` object to be transformed.
 // @returns {module:engine/view/documentfragment~DocumentFragment}
 function documentToView( htmlDocument ) {
+	const domConverter = new DomConverter( { blockFiller: NBSP_FILLER } );
 	const fragment = htmlDocument.createDocumentFragment();
 	const nodes = htmlDocument.body.childNodes;
 
@@ -75,11 +75,12 @@ function documentToView( htmlDocument ) {
 function extractStyles( htmlDocument ) {
 	const styles = [];
 	const stylesString = [];
+	const styleTags = Array.from( htmlDocument.getElementsByTagName( 'style' ) );
 
-	for ( const el of htmlDocument.all ) {
-		if ( el.tagName.toLowerCase() === 'style' && el.sheet && el.sheet.rules && el.sheet.rules.length ) {
-			styles.push( el.sheet );
-			stylesString.push( el.innerHTML );
+	for ( const style of styleTags ) {
+		if ( style.sheet && style.sheet.cssRules && style.sheet.cssRules.length ) {
+			styles.push( style.sheet );
+			stylesString.push( style.innerHTML );
 		}
 	}
 

+ 15 - 15
packages/ckeditor5-paste-from-office/src/pastefromoffice.js

@@ -4,17 +4,22 @@
  */
 
 /**
- * @module pastefromoffice/pastefromoffice
+ * @module paste-from-office/pastefromoffice
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 
 import { parseHtml } from './filters/utils';
-import { paragraphsToLists } from './filters/list';
+import { transformListItemLikeElementsIntoLists } from './filters/list';
 
 /**
- * This plugin handles content pasted from Word and transforms it (if necessary)
- * to format suitable for editor {@link module:engine/model/model~Model}.
+ * The Paste from Office plugin.
+ *
+ * This plugin handles content pasted from Office apps (for now only Word) and transforms it (if necessary)
+ * to a valid structure which can then be understood by the editor features.
+ *
+ * For more information about this feature check the {@glink api/paste-from-office package page}.
  *
  * @extends module:core/plugin~Plugin
  */
@@ -31,25 +36,20 @@ export default class PasteFromOffice extends Plugin {
 	 */
 	init() {
 		const editor = this.editor;
-		const document = editor.editing.view.document;
 
-		this.listenTo( document, 'clipboardInput', ( evt, data ) => {
+		this.listenTo( editor.plugins.get( Clipboard ), 'inputTransformation', ( evt, data ) => {
 			const html = data.dataTransfer.getData( 'text/html' );
 
 			if ( isWordInput( html ) ) {
-				evt.stop();
-
-				editor.plugins.get( 'Clipboard' ).fire( 'inputTransformation', {
-					content: this._normalizeWordInput( html )
-				} );
+				data.content = this._normalizeWordInput( html );
 			}
-		} );
+		}, { priority: 'high' } );
 	}
 
 	/**
 	 * Normalizes input pasted from Word to format suitable for editor {@link module:engine/model/model~Model}.
 	 *
-	 * **Notice**: this function was exposed mainly for testing purposes and should not be called directly.
+	 * **Note**: this function was exposed mainly for testing purposes and should not be called directly.
 	 *
 	 * @protected
 	 * @param {String} input Word input.
@@ -57,9 +57,9 @@ export default class PasteFromOffice extends Plugin {
 	 */
 	_normalizeWordInput( input ) {
 		const { body, stylesString } = parseHtml( input );
-		const normalizedInput = paragraphsToLists( body, stylesString );
+		transformListItemLikeElementsIntoLists( body, stylesString );
 
-		return normalizedInput;
+		return body;
 	}
 }
 

+ 4 - 4
packages/ckeditor5-paste-from-office/tests/data/integration/basic-styles.js

@@ -241,7 +241,7 @@ describe( 'Basic Styles – integration', () => {
 	//		<p><s>Third</s> line <b>styling, <i>space on e</i>nd </b></p>
 	// Input same for: Chrome, Firefox and Edge.
 	describe( 'mulitple styles multiline', () => {
-		it( 'pastes in the empty editor', () => {
+		it.skip( 'pastes in the empty editor', () => {
 			expectPaste( editor, multipleStylesMultiline, '<paragraph>Line ' +
 				'<$text bold="true">bold</$text> and <$text italic="true">italics</$text></paragraph>' +
 				'<paragraph>Line <$text bold="true" underline="true">foo</$text><$text underline="true"> bar</$text></paragraph>' +
@@ -250,7 +250,7 @@ describe( 'Basic Styles – integration', () => {
 				'<$text bold="true">nd []</$text></paragraph>' ); // The last space '...nd </b></p>' goes missing.
 		} );
 
-		it( 'pastes in the paragraph', () => {
+		it.skip( 'pastes in the paragraph', () => {
 			setData( editor.model, '<paragraph>More [] text</paragraph>' );
 
 			expectPaste( editor, multipleStylesMultiline, '<paragraph>More Line ' +
@@ -261,7 +261,7 @@ describe( 'Basic Styles – integration', () => {
 				'<$text bold="true">nd []</$text> text</paragraph>' ); // The last space '...nd </b></p>' goes missing.
 		} );
 
-		it( 'pastes in the different block context', () => {
+		it.skip( 'pastes in the different block context', () => {
 			setData( editor.model, '<heading1>More [] text</heading1>' );
 
 			expectPaste( editor, multipleStylesMultiline, '<heading1>More Line ' +
@@ -272,7 +272,7 @@ describe( 'Basic Styles – integration', () => {
 				'<$text bold="true">nd []</$text> text</paragraph>' ); // The last space '...nd </b></p>' goes missing.
 		} );
 
-		it( 'pastes in the inline styling context', () => {
+		it.skip( 'pastes in the inline styling context', () => {
 			setData( editor.model, '<paragraph><$text bold="true">Bol[]d</$text></paragraph>' );
 
 			expectPaste( editor, multipleStylesMultiline, '<paragraph><$text bold="true">Bol</$text>Line ' +

+ 20 - 20
packages/ckeditor5-paste-from-office/tests/filters/list.js

@@ -6,62 +6,62 @@
 import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
 import { stringify } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
-import { paragraphsToLists } from '../../src/filters/list';
+import { transformListItemLikeElementsIntoLists } from '../../src/filters/list';
 
 describe( 'Filters – list', () => {
 	const htmlDataProcessor = new HtmlDataProcessor();
 
-	describe( 'paragraphsToLists', () => {
+	describe( 'transformListItemLikeElementsIntoLists', () => {
 		it( 'replaces list-like elements with semantic lists', () => {
 			const html = '<p style="mso-list:l0 level1 lfo0"><span style="mso-list:Ignore">1.</span>Item 1</p>';
 			const view = htmlDataProcessor.toView( html );
 
-			const result = paragraphsToLists( view, '' );
+			transformListItemLikeElementsIntoLists( view, '' );
 
-			expect( result.childCount ).to.equal( 1 );
-			expect( result.getChild( 0 ).name ).to.equal( 'ol' );
-			expect( stringify( result ) ).to.equal( '<ol><li style="mso-list:l0 level1 lfo0">Item 1</li></ol>' );
+			expect( view.childCount ).to.equal( 1 );
+			expect( view.getChild( 0 ).name ).to.equal( 'ol' );
+			expect( stringify( view ) ).to.equal( '<ol><li style="mso-list:l0 level1 lfo0">Item 1</li></ol>' );
 		} );
 
 		it( 'replaces list-like elements with semantic lists with proper bullet type based on styles', () => {
 			const html = '<p style="mso-list:l0 level1 lfo0"><span style="mso-list:Ignore">1.</span>Item 1</p>';
 			const view = htmlDataProcessor.toView( html );
 
-			const result = paragraphsToLists( view, '@list l0:level1 { mso-level-number-format: bullet; }' );
+			transformListItemLikeElementsIntoLists( view, '@list l0:level1 { mso-level-number-format: bullet; }' );
 
-			expect( result.childCount ).to.equal( 1 );
-			expect( result.getChild( 0 ).name ).to.equal( 'ul' );
-			expect( stringify( result ) ).to.equal( '<ul><li style="mso-list:l0 level1 lfo0">Item 1</li></ul>' );
+			expect( view.childCount ).to.equal( 1 );
+			expect( view.getChild( 0 ).name ).to.equal( 'ul' );
+			expect( stringify( view ) ).to.equal( '<ul><li style="mso-list:l0 level1 lfo0">Item 1</li></ul>' );
 		} );
 
 		it( 'does not modify the view if there are no list-like elements', () => {
 			const html = '<h1>H1</h1><p>Foo Bar</p>';
 			const view = htmlDataProcessor.toView( html );
 
-			const result = paragraphsToLists( view, '' );
+			transformListItemLikeElementsIntoLists( view, '' );
 
-			expect( result.childCount ).to.equal( 2 );
-			expect( stringify( result ) ).to.equal( html );
+			expect( view.childCount ).to.equal( 2 );
+			expect( stringify( view ) ).to.equal( html );
 		} );
 
 		it( 'handles empty `mso-list` style correctly', () => {
 			const html = '<p style="mso-list:"><span style="mso-list:Ignore">1.</span>Item 1</p>';
 			const view = htmlDataProcessor.toView( html );
 
-			const result = paragraphsToLists( view, '' );
+			transformListItemLikeElementsIntoLists( view, '' );
 
-			expect( result.childCount ).to.equal( 1 );
-			expect( result.getChild( 0 ).name ).to.equal( 'ol' );
-			expect( stringify( result ) ).to.equal( '<ol><li style="mso-list:">Item 1</li></ol>' );
+			expect( view.childCount ).to.equal( 1 );
+			expect( view.getChild( 0 ).name ).to.equal( 'ol' );
+			expect( stringify( view ) ).to.equal( '<ol><li style="mso-list:">Item 1</li></ol>' );
 		} );
 
 		it( 'handles empty body correctly', () => {
 			const view = htmlDataProcessor.toView( '' );
 
-			const result = paragraphsToLists( view, '' );
+			transformListItemLikeElementsIntoLists( view, '' );
 
-			expect( result.childCount ).to.equal( 0 );
-			expect( stringify( result ) ).to.equal( '' );
+			expect( view.childCount ).to.equal( 0 );
+			expect( stringify( view ) ).to.equal( '' );
 		} );
 	} );
 } );

+ 19 - 5
packages/ckeditor5-paste-from-office/tests/filters/utils.js

@@ -12,21 +12,21 @@ import { parseHtml } from '../../src/filters/utils';
 describe( 'Filters – utils', () => {
 	describe( 'parseHtml', () => {
 		it( 'correctly parses HTML with body and one style tag', () => {
-			const html = '<head><style>p { color: red; } a { border: none; }</style></head><body><p>Foo Bar</p></body>';
+			const html = '<head><style>p { color: red; } a { font-size: 12px; }</style></head><body><p>Foo Bar</p></body>';
 			const { body, bodyString, styles, stylesString } = parseHtml( html );
 
 			expect( body ).to.instanceof( DocumentFragment );
-			expect( body.childCount ).to.equal( 1 );
+			expect( body.childCount ).to.equal( 1, 'body.childCount' );
 
 			expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
 
-			expect( styles.length ).to.equal( 1 );
+			expect( styles.length ).to.equal( 1, 'styles.length' );
 			expect( styles[ 0 ] ).to.instanceof( CSSStyleSheet );
 			expect( styles[ 0 ].cssRules.length ).to.equal( 2 );
 			expect( styles[ 0 ].cssRules[ 0 ].style.color ).to.equal( 'red' );
-			expect( styles[ 0 ].cssRules[ 1 ].style.border ).to.equal( 'none' );
+			expect( styles[ 0 ].cssRules[ 1 ].style[ 'font-size' ] ).to.equal( '12px' );
 
-			expect( stylesString ).to.equal( 'p { color: red; } a { border: none; }' );
+			expect( stylesString ).to.equal( 'p { color: red; } a { font-size: 12px; }' );
 		} );
 
 		it( 'correctly parses HTML with body contents only', () => {
@@ -76,5 +76,19 @@ describe( 'Filters – utils', () => {
 
 			expect( stylesString ).to.equal( '' );
 		} );
+
+		it( 'correctly parses HTML with body contents and empty style tag', () => {
+			const html = '<head><style></style></head><body><p>Foo Bar</p></body>';
+			const { body, bodyString, styles, stylesString } = parseHtml( html );
+
+			expect( body ).to.instanceof( DocumentFragment );
+			expect( body.childCount ).to.equal( 1 );
+
+			expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
+
+			expect( styles.length ).to.equal( 0 );
+
+			expect( stylesString ).to.equal( '' );
+		} );
 	} );
 } );

+ 9 - 4
packages/ckeditor5-paste-from-office/tests/pastefromoffice.js

@@ -5,16 +5,21 @@
 
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import DocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 import PasteFromOffice from '../src/pastefromoffice';
 import { createDataTransfer } from './_utils/utils';
 
 describe( 'Paste from Office plugin', () => {
-	let editor, normalizeSpy;
+	let editor, content, normalizeSpy;
 
 	testUtils.createSinonSandbox();
 
+	before( () => {
+		content = new DocumentFragment();
+	} );
+
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
@@ -31,7 +36,7 @@ describe( 'Paste from Office plugin', () => {
 			'text/html': '<meta name=Generator content="Microsoft Word 15">'
 		} );
 
-		editor.editing.view.document.fire( 'clipboardInput', { dataTransfer } );
+		editor.plugins.get( 'Clipboard' ).fire( 'inputTransformation', { content, dataTransfer } );
 
 		expect( normalizeSpy.calledOnce ).to.true;
 	} );
@@ -41,7 +46,7 @@ describe( 'Paste from Office plugin', () => {
 			'text/html': '<html><head><meta name="Generator"  content=Microsoft Word 15></head></html>'
 		} );
 
-		editor.editing.view.document.fire( 'clipboardInput', { dataTransfer } );
+		editor.plugins.get( 'Clipboard' ).fire( 'inputTransformation', { content, dataTransfer } );
 
 		expect( normalizeSpy.calledOnce ).to.true;
 	} );
@@ -51,7 +56,7 @@ describe( 'Paste from Office plugin', () => {
 			'text/html': '<meta name=Generator content="Other">'
 		} );
 
-		editor.editing.view.document.fire( 'clipboardInput', { dataTransfer } );
+		editor.plugins.get( 'Clipboard' ).fire( 'inputTransformation', { content, dataTransfer } );
 
 		expect( normalizeSpy.called ).to.false;
 	} );