Przeglądaj źródła

Merge pull request #334 from ckeditor/t/330

t/330: Introduce AttributeElement and ContainerElement.
Szymon Kupś 9 lat temu
rodzic
commit
620d53ce33

+ 74 - 0
packages/ckeditor5-engine/src/treeview/attributeelement.js

@@ -0,0 +1,74 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import Element from './element.js';
+
+/**
+ * Default attribute priority.
+ *
+ * @member {Number} engine.treeView.AttributeElement.DEFAULT_PRIORITY
+ */
+export const DEFAULT_PRIORITY = 10;
+
+/**
+ * Attributes are elements which define document presentation. They are mostly elements like `<b>` or `<span>`.
+ * Attributes can be broken and merged by the {@link engine.treeView.Writer}.
+ *
+ * Editing engine does not define fixed HTML DTD. This is why the type of the {@link engine.treeView.Element} need to
+ * be defined by the feature developer. Creating an element you should use {@link engine.treeView.ContainerElement}
+ * class or `AttributeElement`.
+ *
+ * @memberOf engine.treeView
+ * @extends engine.treeView.Element
+ */
+export default class AttributeElement extends Element {
+	/**
+	 * Creates a attribute element.
+	 *
+	 * @see engine.treeView.Element
+	 */
+	constructor( name, attrs, children ) {
+		super( name, attrs, children );
+
+		/**
+		 * Element priority. Attributes have to have the same priority to be
+		 * {@link engine.treeView.Element#isSimilar similar}. Setting different priorities on similar
+ 		 * nodes may prevent merging, eg. two `<abbr>` nodes next each other shouldn't be merged.
+		 *
+		 * @member {Number} engine.treeView.AttributeElement#priority
+		 */
+		this.priority = DEFAULT_PRIORITY;
+	}
+
+	/**
+	 * Clones provided element with priority.
+	 *
+	 * @param {Boolean} deep If set to `true` clones element and all its children recursively. When set to `false`,
+	 * element will be cloned without any children.
+	 * @returns {Element} Clone of this element.
+	 */
+	clone( deep ) {
+		const cloned = super.clone( deep );
+
+		// Clone priority too.
+		cloned.priority	= this.priority;
+
+		return cloned;
+	}
+
+	/**
+	 * Checks if this element is similar to other element.
+	 * Both elements should have the same name, attributes and priority to be considered as similar.
+	 * Two similar elements can contain different set of children nodes.
+	 *
+	 * @param {Element} otherElement
+	 * @returns {Boolean}
+	 */
+	isSimilar( otherElement ) {
+		return super.isSimilar( otherElement ) && this.priority == otherElement.priority;
+	}
+}

+ 54 - 0
packages/ckeditor5-engine/src/treeview/containerelement.js

@@ -0,0 +1,54 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import Element from './element.js';
+
+/**
+ * Containers are elements which define document structure. They define boundaries for
+ * {@link engine.treeView.AttributeElement attributes}. They are mostly use for block elements like `<p>` or `<div>`.
+ *
+ * Editing engine does not define fixed HTML DTD. This is why the type of the {@link engine.treeView.Element} need to
+ * be defined by the feature developer.
+ *
+ * Creating an element you should use `ContainerElement` class or {@link engine.treeView.AttributeElement}. This is
+ * important to define the type of the element because of two reasons:
+ *
+ * Firstly, {@link engine.treeView.DomConverter} needs the information what is an editable block to convert elements to
+ * DOM properly. {@link engine.treeView.DomConverter} will ensure that `ContainerElement` is editable and it is possible
+ * to put caret inside it, even if the container is empty.
+ *
+ * Secondly, {@link engine.treeView.Writer} use this information.
+ * {@link engine.treeView.Writer#breakAttributes Break} and {@link engine.treeView.Writer#mergeAttributes Merge}
+ * operations are performed only in a bounds of a container nodes.
+ *
+ * For instance if `<p>` is an container and `<b>` is attribute:
+ *
+ *		<p><b>fo^o</b></p>
+ *
+ * {@link engine.treeView.Writer#breakAttributes breakAttributes} will create:
+ *
+ *		<p><b>fo</b><b>o</b></p>
+ *
+ * There might be a need to mark `<span>` element as a container node, for example in situation when it will be a
+ * container of an inline widget:
+ *
+ * 		<span color="red">foobar</span>  // attribute
+ * 		<span data-widget>foobar</span>  // container
+ *
+ * @memberOf engine.treeView
+ * @extends engine.treeView.Element
+ */
+export default class ContainerElement extends Element {
+	/**
+	 * Creates a container element.
+	 *
+	 * @see engine.treeView.Element
+	 */
+	constructor( name, attrs, children ) {
+		super( name, attrs, children );
+	}
+}

+ 10 - 1
packages/ckeditor5-engine/src/treeview/element.js

@@ -12,6 +12,14 @@ import isPlainObject from '../../utils/lib/lodash/isPlainObject.js';
 /**
  * Tree view element.
  *
+ * Editing engine does not define fixed HTML DTD. This is why the type of the {@link engine.treeView.Element} need to
+ * be defined by the feature developer. Creating an element you should use {@link engine.treeView.ContainerElement}
+ * class or {@link engine.treeView.AttributeElement}.
+ *
+ * Note that for view elements which are not created from model, like elements from mutations, paste or
+ * {@link engine.treeController.DataController#set data.set} it is not possible to define the type of the element, so
+ * these will be instances of the {@link engine.treeView.Element}.
+ *
  * @memberOf engine.treeView
  * @extends engine.treeView.Node
  */
@@ -110,7 +118,8 @@ export default class Element extends Node {
 			}
 		}
 
-		const cloned = new Element( this.name, this._attrs, childrenClone );
+		// ContainerElement and AttributeElement should be also cloned properly.
+		const cloned = new this.constructor( this.name, this._attrs, childrenClone );
 
 		// Classes and styles are cloned separately - this solution is faster than adding them back to attributes and
 		// parse once again in constructor.

+ 37 - 107
packages/ckeditor5-engine/src/treeview/writer.js

@@ -6,7 +6,8 @@
 'use strict';
 
 import Position from './position.js';
-import Element from './element.js';
+import ContainerElement from './containerelement.js';
+import AttributeElement from './attributeelement.js';
 import Text from './text.js';
 import Range from './range.js';
 import CKEditorError from '../../utils/ckeditorerror.js';
@@ -19,79 +20,6 @@ import DocumentFragment from './documentfragment.js';
  * @memberOf engine.treeView
  */
  export default class Writer {
-	constructor() {
-		/**
-		 * Priorities map. Maps node to priority.
-		 * Nodes with priorities are considered as attributes.
-		 *
-		 * @member {WeakMap} engine.treeView.Writer#_priorities
-		 * @protected
-		 */
-		this._priorities = new WeakMap();
-	}
-
-	/**
-	 * Returns `true` if provided node is a `container` node.
-	 *
-	 * `Container` nodes are mostly elements like `<p>` or `<div>`. Break and merge operations are performed only in a
-	 * bounds of a container nodes. Containers will not be broken or merged by
-	 * {@link engine.treeView.Writer#breakAttributes breakAttributes} and
-	 * {@link engine.treeView.Writer#mergeAttributes mergeAttributes}.
-	 *
-	 * `Attribute` nodes are mostly elements like `<b>` or `<span>`. Attributes can be broken and merged. Merging requires
-	 * that attribute nodes are {@link engine.treeView.Element#isSimilar similar} and have same priority. Setting different
-	 * priorities on similar nodes may prevent merging, eg. two `<abbr>` nodes next ot each other shouldn't be merged.
-	 * There might be a need to mark `<span>` element as a container node, for example in situation when it will be a
-	 * container of an inline widget:
-	 *
-	 * 		<span color="red">foobar</span>  // attribute
-	 * 		<span data-widget>foobar</span>  // container
-	 *
-	 * @param {engine.treeView.Element} node Node to check.
-	 * @returns {Boolean} `True` if provided node is a container.
-     */
-	isContainer( node ) {
-		const isElement = node instanceof Element;
-
-		return isElement && !this._priorities.has( node );
-	}
-
-	/**
-	 * Returns `true` if provided node is an `attribute` node.
-	 * For more information about attribute and container nodes see {@link engine.treeView.Writer#isContainer isContainer}
-	 * method description.
-	 *
-	 * @see engine.treeView.Writer#isContainer
-	 * @param {engine.treeView.Element} node Node to check.
-	 * @returns {Boolean} `True` if provided node is an attribute.
-	 */
-	isAttribute( node ) {
-		const isElement = node instanceof Element;
-
-		return isElement && this._priorities.has( node );
-	}
-
-	/**
-	 * Sets node priority. When priority is defined, node is considered as `attribute`.
-	 *
-	 * @see engine.treeView.Writer#isContainer
-	 * @param {engine.treeView.Node} node
-	 * @param {Number} priority
-     */
-	setPriority( node, priority ) {
-		this._priorities.set( node, priority );
-	}
-
-	/**
-	 * Returns node's priority.
-	 *
-	 * @param {engine.treeView.Node} node Node to check its priority.
-	 * @returns {Number|undefined} Priority or `undefined` if there is no priority defined.
-     */
-	getPriority( node ) {
-		return this._priorities.get( node );
-	}
-
 	/**
 	 * Returns first parent container of specified {@link engine.treeView.Position Position}.
 	 * Position's parent node is checked as first, then next parents are checked.
@@ -102,7 +30,7 @@ import DocumentFragment from './documentfragment.js';
 	getParentContainer( position ) {
 		let parent = position.parent;
 
-		while ( !this.isContainer( parent ) ) {
+		while ( !( parent instanceof ContainerElement ) ) {
 			if ( !parent ) {
 				return undefined;
 			}
@@ -132,7 +60,7 @@ import DocumentFragment from './documentfragment.js';
 		const positionParent = position.parent;
 
 		// Position's parent is container, so no attributes to break.
-		if ( this.isContainer( positionParent ) ) {
+		if ( positionParent instanceof ContainerElement ) {
 			return Position.createFromPosition( position );
 		}
 
@@ -181,9 +109,6 @@ import DocumentFragment from './documentfragment.js';
 				// Break element.
 				const clonedNode = positionParent.clone();
 
-				// Clone priority.
-				this.setPriority( clonedNode, this.getPriority( positionParent ) );
-
 				// Insert cloned node to position's parent node.
 				positionParent.parent.insertChildren( offsetAfter, clonedNode );
 
@@ -248,7 +173,7 @@ import DocumentFragment from './documentfragment.js';
 
 	/**
 	 * Merges attribute nodes. It also merges text nodes if needed.
-	 * Only {@link engine.treeView.Element#isSimilar similar} `attribute` nodes, with same priority can be merged.
+	 * Only {@link engine.treeView.AttributeElement#isSimilar similar} `attribute` nodes can be merged.
 	 *
 	 * In following examples `<p>` is a container and `<b>` is an attribute node:
 	 *
@@ -279,28 +204,21 @@ import DocumentFragment from './documentfragment.js';
 		}
 
 		// When one or both nodes are containers - no attributes to merge.
-		if ( this.isContainer( nodeBefore ) || this.isContainer( nodeAfter ) ) {
+		if ( ( nodeBefore instanceof ContainerElement ) || ( nodeAfter instanceof ContainerElement ) ) {
 			return position;
 		}
 
+		// When selection is between two text nodes.
 		if ( nodeBefore instanceof Text && nodeAfter instanceof Text ) {
-			// When selection is between two text nodes.
 			// Merge text data into first text node and remove second one.
 			const nodeBeforeLength = nodeBefore.data.length;
 			nodeBefore.data += nodeAfter.data;
 			positionParent.removeChildren( positionOffset );
 
 			return new Position( nodeBefore, nodeBeforeLength );
-		} else if ( nodeBefore.isSimilar( nodeAfter ) ) {
-			// When selection is between same nodes.
-			const nodeBeforePriority = this._priorities.get( nodeBefore );
-			const nodeAfterPriority = this._priorities.get( nodeAfter );
-
-			// Do not merge same nodes with different priorities.
-			if ( nodeBeforePriority === undefined || nodeBeforePriority !== nodeAfterPriority ) {
-				return Position.createFromPosition( position );
-			}
-
+		}
+		// When selection is between same nodes.
+		else if ( nodeBefore.isSimilar( nodeAfter ) ) {
 			// Move all children nodes from node placed after selection and remove that node.
 			const count = nodeBefore.getChildCount();
 			nodeBefore.appendChildren( nodeAfter.getChildren() );
@@ -414,10 +332,18 @@ import DocumentFragment from './documentfragment.js';
 	 * and {@link engine.treeView.Range#end} positions are not placed inside same parent container.
 	 *
 	 * @param {engine.treeView.Range} range Range to wrap.
-	 * @param {engine.treeView.Element} attribute Attribute element to use as wrapper.
-	 * @param {Number} priority Priority to set.
+	 * @param {engine.treeView.AttributeElement} attribute Attribute element to use as wrapper.
 	 */
-	wrap( range, attribute, priority ) {
+	wrap( range, attribute ) {
+		if ( !( attribute instanceof AttributeElement ) ) {
+			/**
+			 * Attribute element need to be instance of attribute element.
+			 *
+			 * @error treeview-writer-wrap-invalid-attribute
+			 */
+			throw new CKEditorError( 'treeview-writer-wrap-invalid-attribute' );
+		}
+
 		// Range should be placed inside one container.
 		if ( this.getParentContainer( range.start ) !== this.getParentContainer( range.end ) ) {
 			/**
@@ -433,9 +359,6 @@ import DocumentFragment from './documentfragment.js';
 			return range;
 		}
 
-		// Sets wrapper element priority.
-		this.setPriority( attribute, priority );
-
 		// Break attributes at range start and end.
 		const { start: breakStart, end: breakEnd } = this.breakRange( range );
 		const parentContainer = breakStart.parent;
@@ -466,9 +389,18 @@ import DocumentFragment from './documentfragment.js';
 	 * same parent container.
 	 *
 	 * @param {engine.treeView.Range} range
-	 * @param {engine.treeView.Element} element
+	 * @param {engine.treeView.AttributeElement} element
 	 */
 	unwrap( range, attribute ) {
+		if ( !( attribute instanceof AttributeElement ) ) {
+			/**
+			 * Attribute element need to be instance of attribute element.
+			 *
+			 * @error treeview-writer-unwrap-invalid-attribute
+			 */
+			throw new CKEditorError( 'treeview-writer-unwrap-invalid-attribute' );
+		}
+
 		// Range should be placed inside one container.
 		if ( this.getParentContainer( range.start ) !== this.getParentContainer( range.end ) ) {
 			/**
@@ -521,8 +453,8 @@ function unwrapChildren( writer, parent, startOffset, endOffset, attribute ) {
 	while ( i < endOffset ) {
 		const child = parent.getChild( i );
 
-		// If attributes are the similar and have same priority, then unwrap.
-		if (  child.isSimilar( attribute ) && writer.getPriority( child ) == writer.getPriority( attribute ) ) {
+		// If attributes are the similar, then unwrap.
+		if (  child.isSimilar( attribute ) ) {
 			const unwrapped = child.getChildren();
 			const count = child.getChildCount();
 
@@ -542,7 +474,7 @@ function unwrapChildren( writer, parent, startOffset, endOffset, attribute ) {
 			endOffset += count - 1;
 		} else {
 			// If other nested attribute is found start unwrapping there.
-			if ( writer.isAttribute( child ) ) {
+			if ( child instanceof AttributeElement ) {
 				unwrapChildren( writer, child, 0, child.getChildCount(), attribute );
 			}
 
@@ -589,14 +521,12 @@ function wrapChildren( writer, parent, startOffset, endOffset, attribute ) {
 	while ( i < endOffset ) {
 		const child = parent.getChild( i );
 		const isText = child instanceof Text;
-		const isAttribute = writer.isAttribute( child );
-		const priority = writer.getPriority( attribute );
+		const isAttribute = child instanceof AttributeElement;
 
 		// Wrap text or attributes with higher or equal priority.
-		if ( isText || ( isAttribute && priority <= writer.getPriority( child ) ) ) {
+		if ( isText || ( isAttribute && attribute.priority <= child.priority ) ) {
 			// Clone attribute.
 			const newAttribute = attribute.clone();
-			writer.setPriority( newAttribute, priority );
 
 			// Wrap current node with new attribute;
 			child.remove();
@@ -606,7 +536,7 @@ function wrapChildren( writer, parent, startOffset, endOffset, attribute ) {
 			wrapPositions.push(	new Position( parent, i ) );
 		} else {
 			// If other nested attribute is found start wrapping there.
-			if ( writer.isAttribute( child ) ) {
+			if ( child instanceof AttributeElement ) {
 				wrapChildren( writer, child, 0, child.getChildCount(), attribute );
 			}
 		}

+ 59 - 0
packages/ckeditor5-engine/tests/treeview/attributeelement.js

@@ -0,0 +1,59 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: treeview */
+
+'use strict';
+
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
+import Element from '/ckeditor5/engine/treeview/element.js';
+import { DEFAULT_PRIORITY } from '/ckeditor5/engine/treeview/attributeelement.js';
+
+describe( 'AttributeElement', () => {
+	describe( 'constructor', () => {
+		it( 'should create element with default priority', () => {
+			const el = new AttributeElement( 'strong' );
+
+			expect( el ).to.be.an.instanceof( AttributeElement );
+			expect( el ).to.be.an.instanceof( Element );
+			expect( el ).to.have.property( 'name' ).that.equals( 'strong' );
+			expect( el ).to.have.property( 'priority' ).that.equals( DEFAULT_PRIORITY );
+		} );
+	} );
+
+	describe( 'clone', () => {
+		it( 'should clone element with priority', () => {
+			const el = new AttributeElement( 'b' );
+			el.priority = 7;
+
+			const clone = el.clone();
+
+			expect( clone ).to.not.equal( el );
+			expect( clone.name ).to.equal( el.name );
+			expect( clone.priority ).to.equal( el.priority );
+		} );
+	} );
+
+	describe( 'isSimilar', () => {
+		it( 'should return true if priorities are the same', () => {
+			const b1 = new AttributeElement( 'b' );
+			b1.priority = 7;
+
+			const b2 = new AttributeElement( 'b' );
+			b2.priority = 7;
+
+			expect( b1.isSimilar( b2 ) ).to.be.true;
+		} );
+
+		it( 'should return false if priorities are different', () => {
+			const b1 = new AttributeElement( 'b' );
+			b1.priority = 7;
+
+			const b2 = new AttributeElement( 'b' ); // default priority
+
+			expect( b1.isSimilar( b2 ) ).to.be.false;
+		} );
+	} );
+} );

+ 23 - 0
packages/ckeditor5-engine/tests/treeview/containerelement.js

@@ -0,0 +1,23 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: treeview */
+
+'use strict';
+
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import Element from '/ckeditor5/engine/treeview/element.js';
+
+describe( 'ContainerElement', () => {
+	describe( 'constructor', () => {
+		it( 'should create element with default priority', () => {
+			const el = new ContainerElement( 'p' );
+
+			expect( el ).to.be.an.instanceof( ContainerElement );
+			expect( el ).to.be.an.instanceof( Element );
+			expect( el ).to.have.property( 'name' ).that.equals( 'p' );
+		} );
+	} );
+} );

+ 2 - 2
packages/ckeditor5-engine/tests/treeview/writer/_utils/utils.js

@@ -44,7 +44,7 @@ const utils = {
 		}
 
 		if ( description.priority !== undefined ) {
-			writer.setPriority( node, description.priority );
+			node.priority = description.priority;
 		}
 
 		if ( description.rangeStart !== undefined ) {
@@ -125,7 +125,7 @@ const utils = {
 		}
 
 		if ( description.priority !== undefined ) {
-			expect( description.priority ).to.equal( writer.getPriority( node ) );
+			expect( description.priority ).to.equal( node.priority );
 		}
 
 		if ( description.rangeStart !== undefined ) {

+ 33 - 32
packages/ckeditor5-engine/tests/treeview/writer/breakattributes.js

@@ -8,7 +8,8 @@
 'use strict';
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
-import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
 import utils from '/tests/engine/treeview/writer/_utils/utils.js';
 
@@ -25,7 +26,7 @@ describe( 'Writer', () => {
 		// <p>{|foobar}</p> -> <p>|{foobar}</p>
 		it( '<p>{|foobar}</p>', () => {
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 0 }
@@ -35,7 +36,7 @@ describe( 'Writer', () => {
 			const newPosition = writer.breakAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 0,
 				children: [
@@ -47,7 +48,7 @@ describe( 'Writer', () => {
 		it( '<p>foo|bar</p>', () => {
 			// <p>{foo|bar}</p> -> <p>{foo}|{bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 3 }
@@ -57,7 +58,7 @@ describe( 'Writer', () => {
 			const newPosition = writer.breakAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
@@ -70,7 +71,7 @@ describe( 'Writer', () => {
 		it( '<p>{foobar|}</p>', () => {
 			// <p>{foobar|}</p> -> <p>{foobar}|</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 6 }
@@ -80,7 +81,7 @@ describe( 'Writer', () => {
 			const newPosition = writer.breakAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
@@ -92,11 +93,11 @@ describe( 'Writer', () => {
 		it( '<p><b>{foo|bar}</b></p>', () => {
 			// <p><b>{foo|bar}</b></p> -> <p><b>{foo}</b>|<b>{bar}</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -109,12 +110,12 @@ describe( 'Writer', () => {
 			const newPosition = writer.breakAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -122,7 +123,7 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -136,16 +137,16 @@ describe( 'Writer', () => {
 		it( '<p><b><u>{|foobar}</u></b></p>', () => {
 			// <p><b><u>{|foobar}</u></b></p> -> <p>|<b><u>{foobar}</u></b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -160,17 +161,17 @@ describe( 'Writer', () => {
 			const newPosition = writer.breakAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 0,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -186,16 +187,16 @@ describe( 'Writer', () => {
 		// <p><b><u>{foo|ba}r</u></b></p> -> <p><b><u>{foo}</u></b>|<b></u>{bar}</u></b></p>
 		it( '<p><b><u>{foo|bar}</u></b></p>', () => {
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -210,17 +211,17 @@ describe( 'Writer', () => {
 			const newPosition = writer.breakAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -230,12 +231,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -251,16 +252,16 @@ describe( 'Writer', () => {
 		it( '<p><b><u>{foobar|}</u></b></p>', () => {
 			// <p><b><u>{foobar|}</u></b></p> -> <p><b><u>{foobar}</u></b>|</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -275,17 +276,17 @@ describe( 'Writer', () => {
 			const newPosition = writer.breakAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [

+ 21 - 20
packages/ckeditor5-engine/tests/treeview/writer/breakrange.js

@@ -8,7 +8,8 @@
 'use strict';
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
-import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import Range from '/ckeditor5/engine/treeview/range.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
 import utils from '/tests/engine/treeview/writer/_utils/utils.js';
@@ -24,8 +25,8 @@ describe( 'Writer', () => {
 
 	describe( 'breakRange', () => {
 		it( 'should throw when range placed in two containers', () => {
-			const p1 = new Element( 'p' );
-			const p2 = new Element( 'p' );
+			const p1 = new ContainerElement( 'p' );
+			const p2 = new ContainerElement( 'p' );
 
 			expect( () => {
 				writer.breakRange( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
@@ -35,7 +36,7 @@ describe( 'Writer', () => {
 		it( 'should break at collapsed range and return collapsed one', () => {
 			// <p>{foo[]bar}</p> -> <p>{foo}[]{bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 3 }
@@ -45,7 +46,7 @@ describe( 'Writer', () => {
 			const newRange = writer.breakRange( created.range );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 1,
@@ -59,7 +60,7 @@ describe( 'Writer', () => {
 		it( 'should break inside text node #1', () => {
 			// <p>{foo[bar]baz}</p> -> <p>{foo}[{bar}]{baz}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobarbaz', rangeStart: 3, rangeEnd: 6 }
@@ -69,7 +70,7 @@ describe( 'Writer', () => {
 			const newRange = writer.breakRange( created.range );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
@@ -84,7 +85,7 @@ describe( 'Writer', () => {
 		it( 'should break inside text node #2', () => {
 			// <p>{foo[barbaz]}</p> -> <p>{foo}[{barbaz}]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobarbaz', rangeStart: 3, rangeEnd: 9 }
@@ -94,7 +95,7 @@ describe( 'Writer', () => {
 			const newRange = writer.breakRange( created.range );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
@@ -108,7 +109,7 @@ describe( 'Writer', () => {
 		it( 'should break inside text node #3', () => {
 			// <p>{foo[barbaz}]</p> -> <p>{foo}[{barbaz}]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeEnd: 1,
 				children: [
@@ -119,7 +120,7 @@ describe( 'Writer', () => {
 			const newRange = writer.breakRange( created.range );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
@@ -133,7 +134,7 @@ describe( 'Writer', () => {
 		it( 'should break inside text node #4', () => {
 			// <p>{[foo]barbaz}</p> -> <p>[{foo}]{barbaz]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobarbaz', rangeStart: 0, rangeEnd: 3 }
@@ -143,7 +144,7 @@ describe( 'Writer', () => {
 			const newRange = writer.breakRange( created.range );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
@@ -157,7 +158,7 @@ describe( 'Writer', () => {
 		it( 'should break inside text node #5', () => {
 			// <p>[{foo]barbaz}</p> -> <p>[{foo}]{barbaz]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				children: [
@@ -168,7 +169,7 @@ describe( 'Writer', () => {
 			const newRange = writer.breakRange( created.range );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
@@ -183,12 +184,12 @@ describe( 'Writer', () => {
 			// <p>{foo[bar}<b>{baz]qux}</b></p>
 			// <p>{foo}[{bar}<b>{baz}</b>]<b>qux</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', rangeStart: 3 },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -200,7 +201,7 @@ describe( 'Writer', () => {
 
 			const newRange = writer.breakRange( created.range );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 3,
@@ -208,7 +209,7 @@ describe( 'Writer', () => {
 					{ instanceOf: Text, data: 'foo' },
 					{ instanceOf: Text, data: 'bar' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -216,7 +217,7 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [

+ 39 - 38
packages/ckeditor5-engine/tests/treeview/writer/insert.js

@@ -8,7 +8,8 @@
 'use strict';
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
-import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import Position from '/ckeditor5/engine/treeview/position.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
 import utils from '/tests/engine/treeview/writer/_utils/utils.js';
@@ -26,7 +27,7 @@ describe( 'Writer', () => {
 		it( 'should return collapsed range in insertion position when using empty array', () => {
 			// <p>{foo|bar}</p> -> <p>{foo[]bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 3 }
@@ -35,7 +36,7 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, [] );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 3 }
@@ -47,7 +48,7 @@ describe( 'Writer', () => {
 			// <p>{foo|bar}</p> insert {baz}
 			// <p>{foo[baz]bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 3 }
@@ -56,7 +57,7 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, new Text( 'baz' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobazbar', rangeStart: 3, rangeEnd: 6 }
@@ -68,7 +69,7 @@ describe( 'Writer', () => {
 			// <p>{foobar|}</p> insert {baz}
 			// <p>{foobar[baz}]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 6 }
@@ -77,7 +78,7 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, new Text( 'baz' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeEnd: 1,
 				children: [
@@ -90,7 +91,7 @@ describe( 'Writer', () => {
 			// <p>{|foobar}</p> insert {baz}
 			// <p>[{baz]foobar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 0 }
@@ -99,7 +100,7 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, new Text( 'baz' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				children: [
@@ -112,14 +113,14 @@ describe( 'Writer', () => {
 			// <p>{foo|bar}</p> insert <b>{baz}</b>
 			// <p>{foo}[<b>baz</b>]{bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 3 }
 				]
 			} );
 			const toInsert = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'b',
 				priority: 1,
 				children: [
@@ -129,14 +130,14 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, toInsert.node );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: ContainerElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -152,7 +153,7 @@ describe( 'Writer', () => {
 			// <p>|{foobar}</p> insert {baz}
 			// <p>[{baz]foobar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 0,
 				children: [
@@ -162,7 +163,7 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, new Text( 'baz' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				children: [
@@ -175,11 +176,11 @@ describe( 'Writer', () => {
 			// <p><b>{foo|bar}</b></p> insert <b>{baz}</b>
 			// <p><b>{foo[baz]bar}</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -189,7 +190,7 @@ describe( 'Writer', () => {
 				]
 			} );
 			const toInsert = create( writer, {
-				instanceOf: Element,
+				instanceOf: AttributeElement,
 				name: 'b',
 				priority: 1,
 				children: [
@@ -199,11 +200,11 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, toInsert.node );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -218,11 +219,11 @@ describe( 'Writer', () => {
 			// <p><b>{foo|bar}</b></p> insert <b>{baz}</b> ( different priority )
 			// <p><b>{foo}</b>[<b>{baz}</b>]<b>{bar}</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -232,7 +233,7 @@ describe( 'Writer', () => {
 				]
 			} );
 			const toInsert = create( writer, {
-				instanceOf: Element,
+				instanceOf: AttributeElement,
 				name: 'b',
 				priority: 2,
 				children: [
@@ -242,13 +243,13 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, toInsert.node );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -256,7 +257,7 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 2,
 						children: [
@@ -264,7 +265,7 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -278,13 +279,13 @@ describe( 'Writer', () => {
 		it( 'should allow to insert multiple nodes', () => {
 			// <p>|</p> insert <b>{foo}</b>{bar}
 			// <p>[<b>{foo}</b>{bar}]</p>
-			const root = new Element( 'p' );
+			const root = new ContainerElement( 'p' );
 			const toInsert = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'fake',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -298,13 +299,13 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( position, toInsert );
 			test( writer, newRange, root, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 2,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -320,12 +321,12 @@ describe( 'Writer', () => {
 			// <p><b>{qux}</b>|{baz}</p> insert <b>{foo}</b>{bar}
 			// <p><b>{qux[foo}</b>{bar]baz}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -336,11 +337,11 @@ describe( 'Writer', () => {
 				]
 			} );
 			const toInsert = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'fake',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -353,11 +354,11 @@ describe( 'Writer', () => {
 
 			const newRange = writer.insert( created.position, toInsert );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [

+ 30 - 29
packages/ckeditor5-engine/tests/treeview/writer/mergeattributes.js

@@ -8,7 +8,8 @@
 'use strict';
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
-import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
 import utils from '/tests/engine/treeview/writer/_utils/utils.js';
 
@@ -25,7 +26,7 @@ describe( 'Writer', () => {
 		it( 'should not merge if inside text node', () => {
 			// <p>{fo|obar}</p>
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 2 }
@@ -39,19 +40,19 @@ describe( 'Writer', () => {
 		it( 'should not merge if between containers', () => {
 			// <div><p>{foo}</p>|<p>{bar}</p></div>
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'div',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: ContainerElement,
 						name: 'p',
 						children: [
 							{ instanceOf: Text, data: 'foo' }
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: ContainerElement,
 						name: 'p',
 						children: [
 							{ instanceOf: Text, data: 'bar' }
@@ -66,7 +67,7 @@ describe( 'Writer', () => {
 
 		it( 'should return same position when inside empty container', () => {
 			// <p>|</p>
-			const description = { instanceOf: Element, name: 'p', position: 0 };
+			const description = { instanceOf: ContainerElement, name: 'p', position: 0 };
 			const created = create( writer, description );
 			const newPosition = writer.mergeAttributes( created.position );
 			test( writer, newPosition, created.node, description );
@@ -75,12 +76,12 @@ describe( 'Writer', () => {
 		it( 'should not merge when position is placed at the beginning of the container', () => {
 			// <p>|<b></b></p>
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 0,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1
 					}
@@ -94,12 +95,12 @@ describe( 'Writer', () => {
 		it( 'should not merge when position is placed at the end of the container', () => {
 			// <p><b></b>|</p>
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1
 					}
@@ -113,7 +114,7 @@ describe( 'Writer', () => {
 		it( 'should merge when placed between two text nodes', () => {
 			// <p>{foo}|{bar}</p> -> <p>{foo|bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
@@ -125,7 +126,7 @@ describe( 'Writer', () => {
 			const newPosition = writer.mergeAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', position: 3 }
@@ -136,18 +137,18 @@ describe( 'Writer', () => {
 		it( 'should merge when placed between similar attribute nodes', () => {
 			// <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar">|</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'bar' }
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'bar' }
@@ -158,11 +159,11 @@ describe( 'Writer', () => {
 			const newPosition = writer.mergeAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						position: 0,
@@ -176,18 +177,18 @@ describe( 'Writer', () => {
 			// <p><b foo="bar"></b>|<b foo="baz"></b></p> ->
 			// <p><b foo="bar"></b>|<b foo="baz"></b></p>
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'bar' }
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'baz' }
@@ -202,25 +203,25 @@ describe( 'Writer', () => {
 		it( 'should not merge when placed between similar attribute nodes with different priority', () => {
 			// <p><b foo="bar"></b>|<b foo="bar"></b></p> -> <p><b foo="bar"></b>|<b foo="bar"></b></p>
 			const description =  {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'bar' }
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 2,
 						attributes: { foo: 'bar' }
 					}
 				]
 			};
-			const created = create( writer,description );
+			const created = create( writer, description );
 			const newPosition = writer.mergeAttributes( created.position );
 			test( writer, newPosition, created.node, description );
 		} );
@@ -230,12 +231,12 @@ describe( 'Writer', () => {
 			// <p><b foo="bar">{foo}|{bar}</b></p>
 			// <p><b foo="bar">{foo|bar}</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'bar' },
@@ -244,7 +245,7 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'bar' },
@@ -258,11 +259,11 @@ describe( 'Writer', () => {
 			const newPosition = writer.mergeAttributes( created.position );
 
 			test( writer, newPosition, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						attributes: { foo: 'bar' },

+ 31 - 30
packages/ckeditor5-engine/tests/treeview/writer/remove.js

@@ -8,7 +8,8 @@
 'use strict';
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
-import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import Range from '/ckeditor5/engine/treeview/range.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
 import DocumentFragment from '/ckeditor5/engine/treeview/documentfragment.js';
@@ -25,8 +26,8 @@ describe( 'Writer', () => {
 
 	describe( 'remove', () => {
 		it( 'should throw when range placed in two containers', () => {
-			const p1 = new Element( 'p' );
-			const p2 = new Element( 'p' );
+			const p1 = new ContainerElement( 'p' );
+			const p2 = new ContainerElement( 'p' );
 
 			expect( () => {
 				writer.remove( Range.createFromParentsAndOffsets( p1, 0, p2, 0 ) );
@@ -34,7 +35,7 @@ describe( 'Writer', () => {
 		} );
 
 		it( 'should return empty DocumentFragment when range is collapsed', () => {
-			const p = new Element( 'p' );
+			const p = new ContainerElement( 'p' );
 			const range = Range.createFromParentsAndOffsets( p, 0, p, 0 );
 			const fragment = writer.remove( range );
 
@@ -46,7 +47,7 @@ describe( 'Writer', () => {
 		it( 'should remove single text node', () => {
 			// <p>[{foobar}]</p> -> <p>|</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
@@ -57,7 +58,7 @@ describe( 'Writer', () => {
 
 			const removed = writer.remove( created.range );
 			test( writer, created.range.start, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 0,
 				children: []
@@ -72,7 +73,7 @@ describe( 'Writer', () => {
 		it( 'should not leave empty text nodes', () => {
 			// <p>{[foobar]}</p> -> <p>|</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', rangeStart: 0, rangeEnd: 6 }
@@ -81,7 +82,7 @@ describe( 'Writer', () => {
 
 			const removed = writer.remove( created.range );
 			test( writer, created.range.start, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 0,
 				children: []
@@ -96,7 +97,7 @@ describe( 'Writer', () => {
 		it( 'should remove part of the text node', () => {
 			// <p>{f[oob]ar}</p> -> <p>{f|ar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', rangeStart: 1, rangeEnd: 4 }
@@ -105,7 +106,7 @@ describe( 'Writer', () => {
 
 			const removed = writer.remove( created.range );
 			test( writer, created.range.start, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'far', position: 1 }
@@ -121,12 +122,12 @@ describe( 'Writer', () => {
 		it( 'should remove parts of nodes', () => {
 			// <p>{f[oo}<b>{ba]r}</b></p> -> <p>{f}|<b>r</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foo', rangeStart: 1 },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -138,13 +139,13 @@ describe( 'Writer', () => {
 
 			const removed = writer.remove( created.range );
 			test( writer, created.range.start, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				position: 1,
 				children: [
 					{ instanceOf: Text, data: 'f' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -158,9 +159,9 @@ describe( 'Writer', () => {
 			test( writer, null, removed, [
 				{ instanceOf: Text, data: 'oo' },
 				{
-					instanceOf: Element,
-					priority: 1,
+					instanceOf: AttributeElement,
 					name: 'b',
+					priority: 1,
 					children: [
 						{ instanceOf: Text, data: 'ba' }
 					]
@@ -171,13 +172,13 @@ describe( 'Writer', () => {
 		it( 'should merge after removing #1', () => {
 			// <p><b>foo</b>[{bar}]<b>bazqux</b></p> -> <p><b>foo|bazqux</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -186,7 +187,7 @@ describe( 'Writer', () => {
 					},
 					{ instanceOf: Text, data: 'bar' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -198,11 +199,11 @@ describe( 'Writer', () => {
 
 			const removed = writer.remove( created.range );
 			test( writer, created.range.start, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -221,11 +222,11 @@ describe( 'Writer', () => {
 		it( 'should merge after removing #2', () => {
 			// <p><b>fo[o</b>{bar}<b>ba]zqux</b></p> -> <p><b>fo|zqux</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -234,7 +235,7 @@ describe( 'Writer', () => {
 					},
 					{ instanceOf: Text, data: 'bar' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -246,11 +247,11 @@ describe( 'Writer', () => {
 
 			const removed = writer.remove( created.range );
 			test( writer, created.range.start, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -263,18 +264,18 @@ describe( 'Writer', () => {
 			// Test removed nodes.
 			test( writer, null, removed, [
 				{
-					instanceOf: Element,
-					priority: 1,
+					instanceOf: AttributeElement,
 					name: 'b',
+					priority: 1,
 					children: [
 						{ instanceOf: Text, data: 'o' }
 					]
 				},
 				{ instanceOf: Text, data: 'bar' },
 				{
-					instanceOf: Element,
-					priority: 1,
+					instanceOf: AttributeElement,
 					name: 'b',
+					priority: 1,
 					children: [
 						{ instanceOf: Text, data: 'ba' }
 					]

+ 116 - 101
packages/ckeditor5-engine/tests/treeview/writer/unwrap.js

@@ -9,6 +9,8 @@
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
 import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import Position from '/ckeditor5/engine/treeview/position.js';
 import Range from '/ckeditor5/engine/treeview/range.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
@@ -27,21 +29,21 @@ describe( 'Writer', () => {
 	describe( 'unwrap', () => {
 		it( 'should do nothing on collapsed ranges', () => {
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
 				]
 			};
 			const created = create( writer, description );
-			const newRange = writer.unwrap( created.range, new Element( 'b' ), 1 );
+			const newRange = writer.unwrap( created.range, new AttributeElement( 'b' ), 1 );
 			test( writer, newRange, created.node, description );
 		} );
 
 		it( 'should do nothing on single text node', () => {
 			// <p>[{foobar}]</p>
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
@@ -51,21 +53,34 @@ describe( 'Writer', () => {
 			};
 
 			const created = create( writer, description );
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, description );
 		} );
 
+		it( 'should throw error when element is not instance of AttributeElement', () => {
+			const container = new ContainerElement( 'p', null, new AttributeElement( 'b', null, new Text( 'foo' ) ) );
+			const range = new Range(
+				new Position( container, 0 ),
+				new Position( container, 1 )
+			);
+			const b = new Element( 'b' );
+
+			expect( () => {
+				writer.unwrap( range, b );
+			} ).to.throw( CKEditorError, 'treeview-writer-unwrap-invalid-attribute' );
+		} );
+
 		it( 'should throw error when range placed in two containers', () => {
-			const container1 = new Element( 'p' );
-			const container2 = new Element( 'p' );
+			const container1 = new ContainerElement( 'p' );
+			const container2 = new ContainerElement( 'p' );
 			const range = new Range(
 				new Position( container1, 0 ),
 				new Position( container2, 1 )
 			);
-			const b = new Element( 'b' );
+			const b = new AttributeElement( 'b' );
 
 			expect( () => {
 				writer.unwrap( range, b, 1 );
@@ -75,15 +90,15 @@ describe( 'Writer', () => {
 		it( 'should unwrap single node', () => {
 			// <p>[<b>{foobar}</b>]<p> -> <p>[{foobar}]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
-						priority: 1,
+						instanceOf: AttributeElement,
 						name: 'b',
+						priority: 1,
 						children: [
 							{ instanceOf: Text, data: 'foobar' }
 						]
@@ -91,12 +106,12 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
@@ -110,15 +125,15 @@ describe( 'Writer', () => {
 			// <p>[<b>{foobar}</b>]<p> -> <p>[<b>{foobar}</b>]</p>
 			// Unwrapped with <b> but using different priority.
 			const description =  {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
-						priority: 1,
+						instanceOf: AttributeElement,
 						name: 'b',
+						priority: 1,
 						children: [
 							{ instanceOf: Text, data: 'foobar' }
 						]
@@ -127,8 +142,8 @@ describe( 'Writer', () => {
 			};
 			const created = create( writer, description );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 2 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 2;
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, description );
@@ -138,31 +153,31 @@ describe( 'Writer', () => {
 			// <p>[<b>{foo}</b><b>{bar}</b><b>{baz}</b>]<p> -> <p>[{foo}<b>bar</b>{baz}]</p>
 			// <b> around `bar` has different priority than others.
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 3,
 				children: [
 					{
-						instanceOf: Element,
-						priority: 2,
+						instanceOf: AttributeElement,
 						name: 'b',
+						priority: 2,
 						children: [
 							{ instanceOf: Text, data: 'foo' }
 						]
 					},
 					{
-						instanceOf: Element,
-						priority: 1,
+						instanceOf: AttributeElement,
 						name: 'b',
+						priority: 1,
 						children: [
 							{ instanceOf: Text, data: 'bar' }
 						]
 					},
 					{
-						instanceOf: Element,
-						priority: 2,
+						instanceOf: AttributeElement,
 						name: 'b',
+						priority: 2,
 						children: [
 							{ instanceOf: Text, data: 'baz' }
 						]
@@ -170,21 +185,21 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 2 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 2;
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 3,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
-						priority: 1,
+						instanceOf: AttributeElement,
 						name: 'b',
+						priority: 1,
 						children: [
 							{ instanceOf: Text, data: 'bar' }
 						]
@@ -197,13 +212,13 @@ describe( 'Writer', () => {
 		it( 'should unwrap part of the node', () => {
 			// <p>[{baz}<b>{foo]bar}</b><p> -> <p>[{bazfoo}]<b>{bar}</b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				children: [
 					{ instanceOf: Text, data: 'baz' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -214,20 +229,20 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{ instanceOf: Text, data: 'bazfoo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -242,18 +257,18 @@ describe( 'Writer', () => {
 		it( 'should unwrap nested attributes', () => {
 			// <p>[<u><b>{foobar}</b></u>]</p> -> <p>[<u>{foobar}</u>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'b',
 								priority: 1,
 								children: [
@@ -264,18 +279,18 @@ describe( 'Writer', () => {
 					}
 				]
 			} );
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 
 			const newRange = writer.unwrap( created.range, b );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -289,14 +304,14 @@ describe( 'Writer', () => {
 		it( 'should merge unwrapped nodes #1', () => {
 			// <p>{foo}[<b>{bar}</b>]{bom}</p> -> <p>{foo[bar]bom}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
@@ -307,11 +322,11 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b =  new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b =  new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
@@ -327,14 +342,14 @@ describe( 'Writer', () => {
 		it( 'should merge unwrapped nodes #2', () => {
 			// <p>{foo}<u>{bar}</u>[<b><u>{bazqux}</u></b>]</p> -> <p>{foo}<u>{bar[bazqux}</u>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 2,
 				rangeEnd: 3,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -342,12 +357,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -359,12 +374,12 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeEnd: 2,
 				children: [
@@ -373,7 +388,7 @@ describe( 'Writer', () => {
 						data: 'foo'
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -387,13 +402,13 @@ describe( 'Writer', () => {
 		it( 'should merge unwrapped nodes #3', () => {
 			// <p>{foo}<u>{bar}</u>[<b><u>{baz]qux}</u></b></p> -> <p>{foo}<u>{bar[baz}</u>]<b><u>{qux}</u></b></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 2,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -401,12 +416,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -418,12 +433,12 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeEnd: 2,
 				children: [
@@ -432,7 +447,7 @@ describe( 'Writer', () => {
 						data: 'foo'
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -440,12 +455,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -461,14 +476,14 @@ describe( 'Writer', () => {
 		it( 'should merge unwrapped nodes #4', () => {
 			// <p>{foo}<u>{bar}</u>[<b><u>{baz}</u></b>]<u>qux</u></p> -> <p>{foo}<u>{bar[baz]qux}</u></p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 2,
 				rangeEnd: 3,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -476,12 +491,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -491,7 +506,7 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -501,12 +516,12 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{
@@ -514,7 +529,7 @@ describe( 'Writer', () => {
 						data: 'foo'
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -528,18 +543,18 @@ describe( 'Writer', () => {
 		it( 'should merge unwrapped nodes #5', () => {
 			// <p>[<b><u>{foo}</u></b><b><u>{bar}</u></b><b><u>{baz}</u></b>]</p> -> <p>[<u>{foobarbaz}</u>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 3,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -549,12 +564,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -564,12 +579,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'u',
 								priority: 1,
 								children: [
@@ -581,17 +596,17 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -605,17 +620,17 @@ describe( 'Writer', () => {
 		it( 'should unwrap mixed ranges #1', () => {
 			// <p>[<u><b>{foo}]</b></u></p> -> <p>[<u>{foo}</u>]</p
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'b',
 								priority: 1,
 								rangeEnd: 1,
@@ -627,17 +642,17 @@ describe( 'Writer', () => {
 					}
 				]
 			} );
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -651,17 +666,17 @@ describe( 'Writer', () => {
 		it( 'should unwrap mixed ranges #2', () => {
 			// <p>[<u><b>{foo]}</b></u></p> -> <p>[<u>{foo}</u>]</p
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'b',
 								priority: 1,
 								children: [
@@ -672,17 +687,17 @@ describe( 'Writer', () => {
 					}
 				]
 			} );
-			const b = new Element( 'b' );
-			writer.setPriority( b, 1 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 1;
 			const newRange = writer.unwrap( created.range, b );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [

+ 103 - 84
packages/ckeditor5-engine/tests/treeview/writer/wrap.js

@@ -9,6 +9,9 @@
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
 import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
+import { DEFAULT_PRIORITY } from '/ckeditor5/engine/treeview/attributeelement.js';
 import Position from '/ckeditor5/engine/treeview/position.js';
 import Range from '/ckeditor5/engine/treeview/range.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
@@ -27,14 +30,14 @@ describe( 'Writer', () => {
 	describe( 'wrap', () => {
 		it( 'should do nothing on collapsed ranges', () => {
 			const description = {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foo', rangeStart: 1, rangeEnd: 1 }
 				]
 			};
 			const created = create( writer, description );
-			const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
+			const newRange = writer.wrap( created.range, new AttributeElement( 'b' ) );
 			test( writer, newRange, created.node, description );
 		} );
 
@@ -43,7 +46,7 @@ describe( 'Writer', () => {
 			// wrap <b>
 			// <p>[<b>{foobar}<b>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
@@ -52,19 +55,19 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			const newRange = writer.wrap( created.range, b, 1 );
+			const b = new AttributeElement( 'b' );
+			const newRange = writer.wrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foobar' }
 						]
@@ -73,14 +76,27 @@ describe( 'Writer', () => {
 			} );
 		} );
 
+		it( 'should throw error when element is not instance of AttributeElement', () => {
+			const container = new ContainerElement( 'p', null, new Text( 'foo' ) );
+			const range = new Range(
+				new Position( container, 0 ),
+				new Position( container, 1 )
+			);
+			const b = new Element( 'b' );
+
+			expect( () => {
+				writer.wrap( range, b );
+			} ).to.throw( CKEditorError, 'treeview-writer-wrap-invalid-attribute' );
+		} );
+
 		it( 'should throw error when range placed in two containers', () => {
-			const container1 = new Element( 'p' );
-			const container2 = new Element( 'p' );
+			const container1 = new ContainerElement( 'p' );
+			const container2 = new ContainerElement( 'p' );
 			const range = new Range(
 				new Position( container1, 0 ),
 				new Position( container2, 1 )
 			);
-			const b = new Element( 'b' );
+			const b = new AttributeElement( 'b' );
 
 			expect( () => {
 				writer.wrap( range, b, 1 );
@@ -92,7 +108,7 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <p>[<b>{foo}</b>]{bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				children: [
@@ -100,19 +116,19 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			const newRange = writer.wrap( created.range, b, 2 );
+			const b = new AttributeElement( 'b' );
+			const newRange = writer.wrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 2,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foo' }
 						]
@@ -127,26 +143,26 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <p>[<b>{foo}</b>]{bar}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', rangeStart: 0, rangeEnd: 3 }
 				]
 			} );
 
-			const b = new Element( 'b' );
-			const newRange = writer.wrap( created.range, b, 2 );
+			const b = new AttributeElement( 'b' );
+			const newRange = writer.wrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 2,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foo' }
 						]
@@ -161,27 +177,27 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <p>{foo}[<b>{bar}</b>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				children: [
 					{ instanceOf: Text, data: 'foobar', rangeStart: 3, rangeEnd: 6 }
 				]
 			} );
 
-			const b = new Element( 'b' );
-			const newRange = writer.wrap( created.range, b, 2 );
+			const b = new AttributeElement( 'b' );
+			const newRange = writer.wrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 2,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'bar' }
 						]
@@ -195,14 +211,14 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <div>[<b>{foobar}</b><p>{baz}</p>]</div>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'div',
 				rangeStart: 0,
 				rangeEnd: 2,
 				children: [
 					{ instanceOf: Text, data: 'foobar' },
 					{
-						instanceOf: Element,
+						instanceOf: ContainerElement,
 						name: 'p',
 						children: [
 							{ instanceOf: Text, data: 'baz' }
@@ -211,23 +227,23 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
+			const newRange = writer.wrap( created.range, new AttributeElement( 'b' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'div',
 				rangeStart: 0,
 				rangeEnd: 2,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foobar' }
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: ContainerElement,
 						name: 'p',
 						children: [
 							{ instanceOf: Text, data: 'baz' }
@@ -242,13 +258,13 @@ describe( 'Writer', () => {
 			// wrap with <b> that has higher priority than <u>
 			// <p>[<u><b>{foobar}</b></u>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
@@ -258,22 +274,23 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			const newRange = writer.wrap( created.range, b, 2 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 2;
+			const newRange = writer.wrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'u',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'b',
 								priority: 2,
 								children: [
@@ -291,24 +308,24 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <p>[<b>{foobarbaz}</b>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 3,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foo' }
 						]
 					},
 					{ instanceOf: Text, data: 'bar' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'baz' }
 						]
@@ -316,19 +333,19 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const b = new Element( 'b' );
-			const newRange = writer.wrap( created.range, b, 1 );
+			const b = new AttributeElement( 'b' );
+			const newRange = writer.wrap( created.range, b );
 
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foobarbaz' }
 						]
@@ -342,14 +359,14 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <p><b>{foo[bar}</b>]{baz}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foo' }
 						]
@@ -358,16 +375,16 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
+			const newRange = writer.wrap( created.range, new AttributeElement( 'b' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foobar', rangeStart: 3 }
 						]
@@ -382,15 +399,15 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <p><b>{foobar[baz}</b>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 1,
 				rangeEnd: 2,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foobar' }
 						]
@@ -399,16 +416,16 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
+			const newRange = writer.wrap( created.range, new AttributeElement( 'b' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foobarbaz', rangeStart: 6 }
 						]
@@ -422,16 +439,16 @@ describe( 'Writer', () => {
 			// wrap with <b>
 			// <p>[<b>{foo}<i>{bar}</i></b>]{baz}</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 2,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'i',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'bar' }
 						]
@@ -440,23 +457,23 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const newRange = writer.wrap( created.range, new Element( 'b' ), 1 );
+			const newRange = writer.wrap( created.range, new AttributeElement( 'b' ) );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
-						priority: 1,
+						priority: DEFAULT_PRIORITY,
 						children: [
 							{ instanceOf: Text, data: 'foo' },
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'i',
-								priority: 1,
+								priority: DEFAULT_PRIORITY,
 								children: [
 									{ instanceOf: Text, data: 'bar' }
 								]
@@ -473,14 +490,14 @@ describe( 'Writer', () => {
 			// wrap with <b>, that has higher priority than <i>
 			// <p>[<b>{foo}</b><i><b>{bar}</b></i><b>{baz}</b>]</p>
 			const created = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 3,
 				children: [
 					{ instanceOf: Text, data: 'foo' },
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'i',
 						priority: 1,
 						children: [
@@ -491,15 +508,17 @@ describe( 'Writer', () => {
 				]
 			} );
 
-			const newRange = writer.wrap( created.range, new Element( 'b' ), 2 );
+			const b = new AttributeElement( 'b' );
+			b.priority = 2;
+			const newRange = writer.wrap( created.range, b );
 			test( writer, newRange, created.node, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 3,
 				children: [
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 2,
 						children: [
@@ -507,12 +526,12 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'i',
 						priority: 1,
 						children: [
 							{
-								instanceOf: Element,
+								instanceOf: AttributeElement,
 								name: 'b',
 								priority: 2,
 								children: [
@@ -522,7 +541,7 @@ describe( 'Writer', () => {
 						]
 					},
 					{
-						instanceOf: Element,
+						instanceOf: AttributeElement,
 						name: 'b',
 						priority: 2,
 						children: [

+ 9 - 50
packages/ckeditor5-engine/tests/treeview/writer/writer.js

@@ -8,7 +8,8 @@
 'use strict';
 
 import Writer from '/ckeditor5/engine/treeview/writer.js';
-import Element from '/ckeditor5/engine/treeview/element.js';
+import ContainerElement from '/ckeditor5/engine/treeview/containerelement.js';
+import AttributeElement from '/ckeditor5/engine/treeview/attributeelement.js';
 import Text from '/ckeditor5/engine/treeview/text.js';
 import Position from '/ckeditor5/engine/treeview/position.js';
 import utils from '/tests/engine/treeview/writer/_utils/utils.js';
@@ -21,55 +22,13 @@ describe( 'Writer', () => {
 		writer = new Writer();
 	} );
 
-	describe( 'isContainer', () => {
-		it( 'should return true for container elements', () => {
-			const containerElement = new Element( 'p' );
-			const attributeElement = new Element( 'b' );
-
-			writer._priorities.set( attributeElement, 1 );
-
-			expect( writer.isContainer( containerElement ) ).to.be.true;
-			expect( writer.isContainer( attributeElement ) ).to.be.false;
-		} );
-	} );
-
-	describe( 'isAttribute', () => {
-		it( 'should return true for attribute elements', () => {
-			const containerElement = new Element( 'p' );
-			const attributeElement = new Element( 'b' );
-
-			writer._priorities.set( attributeElement, 1 );
-
-			expect( writer.isAttribute( containerElement ) ).to.be.false;
-			expect( writer.isAttribute( attributeElement ) ).to.be.true;
-		} );
-	} );
-
-	describe( 'setPriority', () => {
-		it( 'sets node priority', () => {
-			const nodeMock = {};
-			writer.setPriority( nodeMock, 10 );
-
-			expect( writer._priorities.get( nodeMock ) ).to.equal( 10 );
-		} );
-	} );
-
-	describe( 'getPriority', () => {
-		it( 'gets node priority', () => {
-			const nodeMock = {};
-			writer._priorities.set( nodeMock, 12 );
-
-			expect( writer.getPriority( nodeMock ) ).to.equal( 12 );
-		} );
-	} );
-
 	describe( 'getParentContainer', () => {
 		it( 'should return parent container of the node', () => {
 			const text = new Text( 'foobar' );
-			const b = new Element( 'b', null, [ text ] );
-			const parent = new Element( 'p', null, [ b ] );
+			const b = new AttributeElement( 'b', null, [ text ] );
+			const parent = new ContainerElement( 'p', null, [ b ] );
 
-			writer.setPriority( b, 1 );
+			b.priority = 1;
 			const container = writer.getParentContainer( new Position( text, 0 ) );
 
 			expect( container ).to.equal( parent );
@@ -77,9 +36,9 @@ describe( 'Writer', () => {
 
 		it( 'should return undefined if no parent container', () => {
 			const text = new Text( 'foobar' );
-			const b = new Element( 'b', null, [ text ] );
+			const b = new AttributeElement( 'b', null, [ text ] );
 
-			writer.setPriority( b, 1 );
+			b.priority = 1;
 			const container = writer.getParentContainer( new Position( text, 0 ) );
 
 			expect( container ).to.be.undefined;
@@ -92,7 +51,7 @@ describe( 'Writer', () => {
 			// Move to <div>|</div>
 			// <div>[{foobar}]</div>
 			const source = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'p',
 				rangeStart: 0,
 				rangeEnd: 1,
@@ -101,7 +60,7 @@ describe( 'Writer', () => {
 				]
 			} );
 			const target = create( writer, {
-				instanceOf: Element,
+				instanceOf: ContainerElement,
 				name: 'div',
 				position: 0
 			} );