浏览代码

Added priority and document fragment handling to view test utils.

Szymon Kupś 9 年之前
父节点
当前提交
af4f254eea
共有 2 个文件被更改,包括 158 次插入31 次删除
  1. 22 1
      packages/ckeditor5-engine/tests/_utils-tests/view.js
  2. 136 30
      packages/ckeditor5-engine/tests/_utils/view.js

+ 22 - 1
packages/ckeditor5-engine/tests/_utils-tests/view.js

@@ -6,6 +6,7 @@
 'use strict';
 
 import { getData } from '/tests/engine/_utils/view.js';
+import DocumentFragment from '/ckeditor5/engine/treeview/documentfragment.js';
 import Element from '/ckeditor5/engine/treeview/element.js';
 import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
@@ -87,8 +88,28 @@ describe( 'view test utils', () => {
 			const b = new AttributeElement( 'b', null, text );
 			const p = new ContainerElement( 'p', null, b );
 
-			expect( getData( p, null, { showTypes: true } ) )
+			expect( getData( p, null, { showType: true } ) )
 				.to.equal( '<container:p><attribute:b>foobar</attribute:b></container:p>' );
 		} );
+
+		it( 'should write elements priorities when needed', () => {
+			const text = new Text( 'foobar' );
+			const b = new AttributeElement( 'b', null, text );
+			const p = new ContainerElement( 'p', null, b );
+
+			expect( getData( p, null, { showType: true, showPriority: true } ) )
+				.to.equal( '<container:p><attribute:b priority=10>foobar</attribute:b></container:p>' );
+		} );
+
+		it( 'should parse DocumentFragment as root', () => {
+			const text1 = new Text( 'foobar' );
+			const text2 = new Text( 'bazqux' );
+			const b1 = new Element( 'b', null, text1 );
+			const b2 = new Element( 'b', null, text2 );
+			const fragment = new DocumentFragment( [ b1, b2 ] );
+			expect( getData( fragment, null ) ).to.equal( '<b>foobar</b><b>bazqux</b>' );
+		} );
+
+		// TODO: test when range is outside element/text
 	} );
 } );

+ 136 - 30
packages/ckeditor5-engine/tests/_utils/view.js

@@ -7,6 +7,7 @@
 
 /* jshint latedef:false */
 
+import DocumentFragment from '/ckeditor5/engine/treeview/documentfragment.js';
 import Element from '/ckeditor5/engine/treeview/element.js';
 import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
@@ -23,19 +24,32 @@ export function getData( root, selection, options ) {
 	return viewStringify.stringify();
 }
 
+/**
+ * Private helper class used for converting view tree to string.
+ *
+ * @private
+ */
 class ViewStringify {
-	constructor( root, selection, options = {} ) {
+	/**
+	 * Creates ViewStringify instance.
+	 * @param root
+	 * @param {engine.treeView.Selection} [selection=null] Selection which ranges should be also converted to string.
+	 * @param {Object} [options] Options object.
+	 * @param {Boolean} [options.showType=false] When set to `true` type of elements will be printed ( `<container:p>`
+	 * instead of `<p>` and `<attribute:b>` instead of `<b>`.
+	 * @param {Boolean} [options.showPriority=false] When set to `true` AttributeElement's priority will be printed.
+	 */
+	constructor( root, selection = null, options = {} ) {
 		this.root = root;
-		this.selection = null;
+		this.selection = selection;
 		this.ranges = [];
 
-		if ( selection ) {
-			this.selection = selection;
+		if ( this.selection ) {
 			this.ranges = [ ...selection.getRanges() ];
 		}
 
-		this.showTypes = !!options.showTypes;
-		this.showPriorities = !!options.showPriorities;
+		this.showType = !!options.showType;
+		this.showPriority = !!options.showPriority;
 	}
 
 	/**
@@ -52,24 +66,38 @@ class ViewStringify {
 		return result;
 	}
 
-	_walkView( root, cb ) {
-		if ( root instanceof Element ) {
-			cb( this._stringifyElementOpen( root ) );
+	/**
+	 * Executes simple walker that iterates over all elements in the view tree starting from root element.
+	 * Calls `callback` with parsed chunks of string data.
+	 *
+	 * @private
+	 * @param root
+	 * @param {Function} callback
+	 */
+	_walkView( root, callback ) {
+		const isElement = root instanceof Element;
+
+		if ( isElement || root instanceof DocumentFragment ) {
+			if ( isElement ) {
+				callback( this._stringifyElementOpen( root ) );
+			}
 
 			let offset = 0;
-			this._checkElementRanges( root, offset, cb );
+			this._checkElementRanges( root, offset, callback );
 
 			for ( let child of root.getChildren() ) {
-				this._walkView( child, cb );
+				this._walkView( child, callback );
 				offset++;
-				this._checkElementRanges( root, offset, cb );
+				this._checkElementRanges( root, offset, callback );
 			}
 
-			cb( this._stringifyElementClose( root ) );
+			if ( isElement ) {
+				callback( this._stringifyElementClose( root ) );
+			}
 		}
 
 		if ( root instanceof Text ) {
-			cb( this._checkTextRanges( root ) );
+			callback( this._checkTextRanges( root ) );
 		}
 	}
 
@@ -122,39 +150,117 @@ class ViewStringify {
 		return result;
 	}
 
+	/**
+	 * Converts passed {@link engine.treeView.Element Element} to opening tag.
+	 * Depending on current configuration opening tag can be simple (`<a>`), contain type prefix (`<container:p>` or
+	 * `<attribute:a>`), contain priority information ( `<attribute:a priority=20>` ). Element's attributes also
+	 * will be included (`<a href="http://ckeditor.com" name="foobar">`).
+	 *
+	 * @private
+	 * @param {engine.treeView.Element} element
+	 * @returns {string}
+	 */
 	_stringifyElementOpen( element ) {
-		let attributes = [];
-		const namespace = this._stringifyElementType( element );
-
-		// TODO: Maybe attributes should be put in alphabetical order, it might be easier to write expected string.
-		for ( let attribute of element.getAttributeKeys() ) {
-			attributes.push( `${ attribute }="${ element.getAttribute( attribute ) }"` );
-		}
+		const name = this._stringifyElementName( element );
+		const priority = this._stringifyElementPriority( element );
+		const attributes = this._stringifyElementAttributes( element );
+		const parts = [ name, priority, attributes ];
 
-		return `<${ namespace + element.name }${ attributes.length > 0 ? ' ' + attributes.join( ' ' ) : '' }>`;
+		return `<${ parts.filter( i => i !== '' ).join( ' ' ) }>`;
 	}
 
+	/**
+	 * Converts passed {@link engine.treeView.Element Element} to closing tag.
+	 * Depending on current configuration opening tag can be simple (`</a>`) or contain type prefix (`</container:p>` or
+	 * `</attribute:a>`).
+	 *
+	 * @private
+	 * @param {engine.treeView.Element} element
+	 * @returns {string}
+	 */
 	_stringifyElementClose( element ) {
-		const namespace = this._stringifyElementType( element );
+		const name = this._stringifyElementName( element );
 
-		return `</${ namespace + element.name }>`;
+		return `</${ name }>`;
 	}
 
-	_stringifyElementType( element ) {
-		if ( this.showTypes ) {
+	/**
+	 * Converts passed {@link engine.treeView.Element Element} its name representation.
+	 * Depending on current configuration name can be simple (`a`) or contain type prefix (`container:p` or
+	 * `attribute:a`).
+	 *
+	 * @private
+	 * @param {engine.treeView.Element} element
+	 * @returns {string}
+	 */
+	_stringifyElementName( element ) {
+		let name = element.name;
+
+		if ( this.showType ) {
 			if ( element instanceof AttributeElement ) {
-				return 'attribute:';
+				return 'attribute:' + name;
 			}
 
 			if ( element instanceof ContainerElement ) {
-				return 'container:';
+				return 'container:' + name;
 			}
 		}
 
+		return name;
+	}
+
+	/**
+	 * Converts passed {@link engine.treeView.Element Element} to its priority representation.
+	 * Priority string representation will be returned when passed element is an instance of
+	 * {@link engine.treeView.AttributeElement AttributeElement} and current configuration allow to show priority.
+	 * Otherwise returns empty string.
+	 *
+	 * @private
+	 * @param {engine.treeView.Element} element
+	 * @returns {string}
+	 */
+	_stringifyElementPriority( element ) {
+		if ( this.showPriority && element instanceof AttributeElement ) {
+			return `priority=${ element.priority }`;
+		}
+
 		return '';
 	}
 
-	static _insertToString( source, index, insert ) {
-		return source.substr( 0, index ) + insert + source.substr( index );
+	/**
+	 * Converts passed {@link engine.treeView.Element Element} attributes to their string representation.
+	 * If element has no attributes - empty string is returned.
+	 *
+	 * @private
+	 * @param {engine.treeView.Element} element
+	 * @returns {string}
+	 */
+	_stringifyElementAttributes( element ) {
+		const attributes = [];
+
+		// TODO: Maybe attributes should be put in alphabetical order, it might be easier to write expected string.
+		for ( let attribute of element.getAttributeKeys() ) {
+			attributes.push( `${ attribute }="${ element.getAttribute( attribute ) }"` );
+		}
+
+		return attributes.join( ' ' );
+	}
+
+	/**
+	 * Inserts given text at specified index in input text.
+	 * If index is outside input text boundaries - returns same, unmodified string.
+	 *
+	 * @private
+	 * @param {String} input Input string.
+	 * @param {Number} index Index where to insert inside input string.
+	 * @param {String} insert Text to insert.
+	 * @returns {string}
+	 */
+	static _insertToString( input, index, insert ) {
+		if ( index < 0 || index > input.length ) {
+			return input;
+		}
+
+		return input.substr( 0, index ) + insert + input.substr( index );
 	}
 }