Parcourir la source

TreeView classes tests.

Piotr Jasiun il y a 10 ans
Parent
commit
06a67bd20e

+ 7 - 5
packages/ckeditor5-engine/src/treeview/element.js

@@ -119,7 +119,7 @@ export default class Element extends Node {
 			this._children[ i ].parent = null;
 		}
 
-		return new this._children.splice( index, number );
+		return this._children.splice( index, number );
 	}
 
 	// Note that created elements will not have coresponding DOM elements created it these did not exist before.
@@ -133,18 +133,20 @@ export default class Element extends Node {
 		if ( domElement instanceof Text ) {
 			return new ViewText( domElement.data );
 		} else {
-			viewElement = new Element( domElement.name );
+			viewElement = new Element( domElement.tagName.toLowerCase() );
 			const attrs = domElement.attributes;
 
 			for ( let i = attrs.length - 1; i >= 0; i-- ) {
 				viewElement.setAttr( attrs[ i ].name, attrs[ i ].value );
 			}
 
-			for ( let childView of viewElement.getChildren() ) {
-				domElement.appendChild( this.createFromDom( childView ) );
+			for ( let i = 0, len = domElement.childNodes.length; i < len; i++ ) {
+				let domChild = domElement.childNodes[ i ];
+
+				viewElement.appendChildren( this.createFromDom( domChild ) );
 			}
 
-			return domElement;
+			return viewElement;
 		}
 	}
 

+ 0 - 72
packages/ckeditor5-engine/src/treeview/position.js

@@ -5,8 +5,6 @@
 
 'use strict';
 
-import CKEditorError from '../ckeditorerror.js';
-
 /**
  * Position in the tree. Position is always located before or after a node.
  *
@@ -35,74 +33,4 @@ import CKEditorError from '../ckeditorerror.js';
 		 */
 		this.offset = offset;
 	}
-
-	/**
-	 * Node directly after the position.
-	 *
-	 * @readonly
-	 * @property {treeView.Node}
-	 */
-	getNodeAfter() {
-		return this.parent.getChild( this.offset ) || null;
-	}
-
-	/**
-	 * Node directly before the position.
-	 *
-	 * @readonly
-	 * @type {treeView.Node}
-	 */
-	getNodeBefore() {
-		return this.parent.getChild( this.offset - 1 ) || null;
-	}
-
-	/**
-	 * Creates a new position after given node.
-	 *
-	 * @param {treeView.Node} node Node the position should be directly after.
-	 * @returns {treeView.Position}
-	 */
-	static createAfter( node ) {
-		if ( !node.parent ) {
-			/**
-			 * You can not make position after root.
-			 *
-			 * @error position-after-root
-			 * @param {treeView.Node} root
-			 */
-			throw new CKEditorError( 'position-after-root: You can not make position after root.', { root: node } );
-		}
-
-		return new Position( node.parent, node.getIndex() + 1 );
-	}
-
-	/**
-	 * Creates a new position before the given node.
-	 *
-	 * @param {treeView.node} node Node the position should be directly before.
-	 * @returns {treeView.Position}
-	 */
-	static createBefore( node ) {
-		if ( !node.parent ) {
-			/**
-			 * You can not make position before root.
-			 *
-			 * @error position-before-root
-			 * @param {treeView.Node} root
-			 */
-			throw new CKEditorError( 'position-before-root: You can not make position before root.', { root: node } );
-		}
-
-		return new Position( node.parent, node.getIndex() );
-	}
-
-	/**
-	 * Creates and returns a new instance of Position, which is equal to passed position.
-	 *
-	 * @param {treeView.Position} position Position to be cloned.
-	 * @returns {treeView.Position}
-	 */
-	static createFromPosition( position ) {
-		return new this( position.parent, position.offset );
-	}
 }

+ 1 - 1
packages/ckeditor5-engine/src/treeview/rootelement.js

@@ -13,7 +13,7 @@ import Element from './element.js';
  * @class treeView.RootElement
  */
 export default class RootElement extends Element {
-	constructor( treeView, name ) {
+	constructor( name, treeView ) {
 		super( name );
 
 		this._treeView = treeView;

+ 1 - 1
packages/ckeditor5-engine/src/treeview/text.js

@@ -46,7 +46,7 @@ export default class Text extends Node {
 			const viewElement = Element.getCorespondingView( previousSibling );
 
 			if ( viewElement ) {
-				return viewElement.getNextSibling;
+				return viewElement.getNextSibling();
 			}
 		} else {
 			const viewElement = Element.getCorespondingView( domText.parentElement );

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

@@ -24,7 +24,7 @@ export default class TreeView {
 		/**
 		 * Root of the view
 		 */
-		this.viewRoot = new RootElement( this, domElement.name );
+		this.viewRoot = new RootElement( domElement.name, this );
 		this.viewRoot.cloneDomAttrs( domElement );
 		this.viewRoot.bindDomElement( domElement );
 		this.viewRoot.markToSync( 'CHILDREN_NEED_UPDATE' );

+ 226 - 72
packages/ckeditor5-engine/tests/treeview/element.js

@@ -3,8 +3,6 @@
  * For licensing, see LICENSE.md.
  */
 
-/* jshint expr: true */
-
 /* bender-tags: treeview */
 
 /* bender-include: ../_tools/tools.js */
@@ -19,106 +17,262 @@ const modules = bender.amd.require(
 );
 
 describe( 'Element', () => {
-	let Element, Node;
+	let ViewElement, Node;
 
 	before( () => {
-		Element = modules[ 'core/treeview/element' ];
+		ViewElement = modules[ 'core/treeview/element' ];
 		Node = modules[ 'core/treeview/node' ];
 	} );
 
 	describe( 'constructor', () => {
 		it( 'should create element without attributes', () => {
-			let element = new Element( 'elem' );
-			let parent = new Element( 'parent', [], [ element ] );
+			const elem = new ViewElement( 'p' );
+
+			expect( elem ).to.be.an.instanceof( Node );
+			expect( elem ).to.have.property( 'name' ).that.equals( 'p' );
+			expect( elem ).to.have.property( 'parent' ).that.is.null;
+			expect( getIteratorCount( elem.getAttrKeys() ) ).to.equal( 0 );
+		} );
+
+		it( 'should create element with attributes as plain object', () => {
+			const elem = new ViewElement( 'p', { 'foo': 'bar' } );
 
-			expect( element ).to.be.an.instanceof( Node );
-			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
-			expect( element ).to.have.property( 'parent' ).that.equals( parent );
-			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 0 );
+			expect( elem ).to.have.property( 'name' ).that.equals( 'p' );
+			expect( getIteratorCount( elem.getAttrKeys() ) ).to.equal( 1 );
+			expect( elem.getAttr( 'foo' ) ).to.equal( 'bar' );
 		} );
 
-		it( 'should create element with attributes', () => {
-			let attr = new Attribute( 'foo', 'bar' );
+		it( 'should create element with attributes as map', () => {
+			const attrs = new Map();
+			attrs.set( 'foo', 'bar' );
 
-			let element = new Element( 'elem', [ attr ] );
-			let parent = new Element( 'parent', [], [ element ] );
+			const elem = new ViewElement( 'p', attrs );
 
-			expect( element ).to.be.an.instanceof( Node );
-			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
-			expect( element ).to.have.property( 'parent' ).that.equals( parent );
-			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 1 );
-			expect( element.getAttr( attr.key ) ).to.equal( attr.value );
+			expect( elem ).to.have.property( 'name' ).that.equals( 'p' );
+			expect( getIteratorCount( elem.getAttrKeys() ) ).to.equal( 1 );
+			expect( elem.getAttr( 'foo' ) ).to.equal( 'bar' );
 		} );
 
 		it( 'should create element with children', () => {
-			let element = new Element( 'elem', [], 'foo' );
+			const child = new ViewElement( 'p', { 'foo': 'bar' } );
+			const parent = new ViewElement( 'div', [], [ child ] );
 
-			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
-			expect( element.getChildCount() ).to.equal( 3 );
-			expect( element.getChild( 0 ) ).to.have.property( 'character' ).that.equals( 'f' );
-			expect( element.getChild( 1 ) ).to.have.property( 'character' ).that.equals( 'o' );
-			expect( element.getChild( 2 ) ).to.have.property( 'character' ).that.equals( 'o' );
+			expect( parent ).to.have.property( 'name' ).that.equals( 'div' );
+			expect( parent.getChildCount() ).to.equal( 1 );
+			expect( parent.getChild( 0 ) ).to.have.property( 'name' ).that.equals( 'p' );
 		} );
 	} );
 
-	describe( 'insertChildren', () => {
-		it( 'should add children to the element', () => {
-			let element = new Element( 'elem', [], [ 'xy' ] );
-			element.insertChildren( 1, 'foo' );
+	describe( 'children manipulation methods', () => {
+		let parent, e1, e2, e3, e4;
 
-			expect( element ).to.have.property( 'name' ).that.equals( 'elem' );
-			expect( element.getChildCount() ).to.equal( 5 );
-			expect( element.getChild( 0 ) ).to.have.property( 'character' ).that.equals( 'x' );
-			expect( element.getChild( 1 ) ).to.have.property( 'character' ).that.equals( 'f' );
-			expect( element.getChild( 2 ) ).to.have.property( 'character' ).that.equals( 'o' );
-			expect( element.getChild( 3 ) ).to.have.property( 'character' ).that.equals( 'o' );
-			expect( element.getChild( 4 ) ).to.have.property( 'character' ).that.equals( 'y' );
+		beforeEach( () => {
+			parent = new ViewElement( 'p' );
+			e1 = new ViewElement( 'e1' );
+			e2 = new ViewElement( 'e2' );
+			e3 = new ViewElement( 'e3' );
+			e4 = new ViewElement( 'e4' );
 		} );
-	} );
 
-	describe( 'removeChildren', () => {
-		it( 'should remove children from the element and return them as a NodeList', () => {
-			let element = new Element( 'elem', [], [ 'foobar' ] );
-			let o = element.getChild( 2 );
-			let b = element.getChild( 3 );
-			let a = element.getChild( 4 );
-			let removed = element.removeChildren( 2, 3 );
-
-			expect( element.getChildCount() ).to.equal( 3 );
-			expect( element.getChild( 0 ) ).to.have.property( 'character' ).that.equals( 'f' );
-			expect( element.getChild( 1 ) ).to.have.property( 'character' ).that.equals( 'o' );
-			expect( element.getChild( 2 ) ).to.have.property( 'character' ).that.equals( 'r' );
-
-			expect( o ).to.have.property( 'parent' ).that.is.null;
-			expect( b ).to.have.property( 'parent' ).that.is.null;
-			expect( a ).to.have.property( 'parent' ).that.is.null;
-
-			expect( removed ).to.be.instanceof( NodeList );
-			expect( removed.length ).to.equal( 3 );
-			expect( removed.get( 0 ) ).to.equal( o );
-			expect( removed.get( 1 ) ).to.equal( b );
-			expect( removed.get( 2 ) ).to.equal( a );
+		describe( 'insertion', () => {
+			it( 'should insert children', () => {
+				parent.insertChildren( 0, [ e1, e3 ] );
+				parent.insertChildren( 1, e2 );
+
+				expect( parent.getChildCount() ).to.equal( 3 );
+				expect( parent.getChild( 0 ) ).to.have.property( 'name' ).that.equals( 'e1' );
+				expect( parent.getChild( 1 ) ).to.have.property( 'name' ).that.equals( 'e2' );
+				expect( parent.getChild( 2 ) ).to.have.property( 'name' ).that.equals( 'e3' );
+			} );
+
+			it( 'should append children', () => {
+				parent.insertChildren( 0, e1 );
+				parent.appendChildren( e2 );
+				parent.appendChildren( e3 );
+
+				expect( parent.getChildCount() ).to.equal( 3 );
+				expect( parent.getChild( 0 ) ).to.have.property( 'name' ).that.equals( 'e1' );
+				expect( parent.getChild( 1 ) ).to.have.property( 'name' ).that.equals( 'e2' );
+				expect( parent.getChild( 2 ) ).to.have.property( 'name' ).that.equals( 'e3' );
+			} );
+		} );
+
+		describe( 'getChildIndex', () => {
+			it( 'should return child index', () => {
+				parent.appendChildren( e1 );
+				parent.appendChildren( e2 );
+				parent.appendChildren( e3 );
+
+				expect( parent.getChildCount() ).to.equal( 3 );
+				expect( parent.getChildIndex( e1 ) ).to.equal( 0 );
+				expect( parent.getChildIndex( e2 ) ).to.equal( 1 );
+				expect( parent.getChildIndex( e3 ) ).to.equal( 2 );
+			} );
+		} );
+
+		describe( 'getChildren', () => {
+			it( 'should renturn children iterator', () => {
+				parent.appendChildren( e1 );
+				parent.appendChildren( e2 );
+				parent.appendChildren( e3 );
+
+				const expected = [ e1, e2, e3 ];
+				let i = 0;
+
+				for ( let child of parent.getChildren() ) {
+					expect( child ).to.equal( expected[ i ] );
+					i++;
+				}
+
+				expect( i ).to.equal( 3 );
+			} );
+		} );
+
+		describe( 'removeChildren', () => {
+			it( 'should remove children', () => {
+				parent.appendChildren( e1 );
+				parent.appendChildren( e2 );
+				parent.appendChildren( e3 );
+				parent.appendChildren( e4 );
+
+				parent.removeChildren( 1, 2 );
+
+				expect( parent.getChildCount() ).to.equal( 2 );
+				expect( parent.getChild( 0 ) ).to.have.property( 'name' ).that.equals( 'e1' );
+				expect( parent.getChild( 1 ) ).to.have.property( 'name' ).that.equals( 'e4' );
+
+				expect( e1.parent ).to.equal( parent );
+				expect( e2.parent ).to.be.null;
+				expect( e3.parent ).to.be.null;
+				expect( e4.parent ).equal( parent );
+			} );
 		} );
 	} );
 
-	describe( 'getChildIndex', () => {
-		it( 'should return child index', () => {
-			let element = new Element( 'elem', [], [ 'bar' ] );
-			let b = element.getChild( 0 );
-			let a = element.getChild( 1 );
-			let r = element.getChild( 2 );
+	describe( 'attributes manipulation methods', () => {
+		let elem;
+
+		beforeEach( () => {
+			elem = new ViewElement( 'p' );
+		} );
+
+		describe( 'getAttr', () => {
+			it( 'should return attribute', () => {
+				elem.setAttr( 'foo', 'bar' );
+
+				expect( elem.getAttr( 'foo' ) ).to.equal( 'bar' );
+				expect( elem.getAttr( 'bom' ) ).to.not.be.ok;
+			} );
+		} );
+
+		describe( 'hasAttr', () => {
+			it( 'should return true if element has attribute', () => {
+				elem.setAttr( 'foo', 'bar' );
+
+				expect( elem.hasAttr( 'foo' ) ).to.be.true;
+				expect( elem.hasAttr( 'bom' ) ).to.be.false;
+			} );
+		} );
+
+		describe( 'getAttrKeys', () => {
+			it( 'should return keys', () => {
+				elem.setAttr( 'foo', true );
+				elem.setAttr( 'bar', true );
+
+				const expected = [ 'foo', 'bar' ];
+				let i = 0;
 
-			expect( element.getChildIndex( b ) ).to.equal( 0 );
-			expect( element.getChildIndex( a ) ).to.equal( 1 );
-			expect( element.getChildIndex( r ) ).to.equal( 2 );
+				for ( let child of elem.getAttrKeys() ) {
+					expect( child ).to.equal( expected[ i ] );
+					i++;
+				}
+
+				expect( i ).to.equal( 2 );
+			} );
+		} );
+
+		describe( 'removeAttr', () => {
+			it( 'should remove attributes', () => {
+				elem.setAttr( 'foo', true );
+
+				expect( elem.hasAttr( 'foo' ) ).to.be.true;
+
+				elem.removeAttr( 'foo' );
+
+				expect( elem.hasAttr( 'foo' ) ).to.be.false;
+
+				expect( getIteratorCount( elem.getAttrKeys() ) ).to.equal( 0 );
+			} );
+		} );
+
+		describe( 'cloneDomAttrs', () => {
+			it( 'should clone DOM attributes', () => {
+				const domElement = document.createElement( 'p' );
+				domElement.setAttribute( 'foo', '1' );
+				domElement.setAttribute( 'bar', '2' );
+
+				elem.cloneDomAttrs( domElement );
+
+				expect( elem.getAttr( 'foo' ) ).to.equal( '1' );
+				expect( elem.getAttr( 'bar' ) ).to.equal( '2' );
+				expect( getIteratorCount( elem.getAttrKeys() ) ).to.equal( 2 );
+			} );
 		} );
 	} );
 
-	describe( 'getChildCount', () => {
-		it( 'should return number of children', () => {
-			let element = new Element( 'elem', [], [ 'bar' ] );
+	describe( 'DOM-View binding', () => {
+		describe( 'getCorespondingView', () => {
+			it( 'should return coresponding view element', () => {
+				const domElement = document.createElement( 'p' );
+				const viewElement = new ViewElement( 'p' );
+
+				viewElement.bindDomElement( domElement );
+
+				expect( ViewElement.getCorespondingView( domElement ) ).to.equal( viewElement );
+			} );
+		} );
+
+		describe( 'getCorespondingDom', () => {
+			it( 'should return coresponding DOM element', () => {
+				const domElement = document.createElement( 'p' );
+				const viewElement = new ViewElement( 'p' );
+
+				viewElement.bindDomElement( domElement );
+
+				expect( viewElement.getCorespondingDom() ).to.equal( domElement );
+			} );
+		} );
+
+		describe( 'createFromDom', () => {
+			it( 'should create tree of view elements from DOM elements', () => {
+				const domImg = document.createElement( 'img' );
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.setAttribute( 'class', 'foo' );
+
+				domP.appendChild( domImg );
+				domP.appendChild( domText );
+
+				const viewImg = new ViewElement( 'img' );
+
+				viewImg.bindDomElement( domImg );
+
+				const viewP = ViewElement.createFromDom( domP );
+
+				expect( viewP ).to.be.an.instanceof( ViewElement );
+				expect( viewP.name ).to.equal( 'p' );
+
+				expect( viewP.getAttr( 'class' ) ).to.equal( 'foo' );
+				expect( getIteratorCount( viewP.getAttrKeys() ) ).to.equal( 1 );
+
+				expect( viewP.getChildCount() ).to.equal( 2 );
+				expect( viewP.getChild( 0 ).name ).to.equal( 'img' );
+				expect( viewP.getChild( 1 ).getText() ).to.equal( 'foo' );
 
-			expect( element.getChildCount() ).to.equal( 3 );
+				expect( viewP.getCorespondingDom() ).to.not.equal( domP );
+				expect( viewP.getChild( 0 ).getCorespondingDom() ).to.equal( domImg );
+			} );
 		} );
 	} );
 } );

+ 108 - 0
packages/ckeditor5-engine/tests/treeview/marktosync.js

@@ -0,0 +1,108 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: treeview */
+
+/* bender-include: ../_tools/tools.js */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'core/treeview/element',
+	'core/treeview/rootelement',
+	'core/treeview/text'
+);
+
+describe( 'Node.markToSync', () => {
+	let ViewElement, ViewText, RootElement;
+
+	let root, text, img;
+	let markToSyncSpy;
+
+	before( () => {
+		ViewElement = modules[ 'core/treeview/element' ];
+		ViewText = modules[ 'core/treeview/text' ];
+		RootElement = modules[ 'core/treeview/rootelement' ];
+
+		markToSyncSpy = sinon.spy();
+	} );
+
+	beforeEach( () => {
+		text = new ViewText( 'foo' );
+		img = new ViewElement( 'img' );
+		img.setAttr( 'src', 'img.png' );
+
+		root = new RootElement( 'p', { renderer: { markToSync: markToSyncSpy } } );
+		root.appendChildren( [ text, img ] );
+
+		markToSyncSpy.reset();
+	} );
+
+	describe( 'setAttr', () => {
+		it( 'should mark attibutes to sync', () => {
+			img.setAttr( 'width', 100 );
+
+			sinon.assert.calledOnce( markToSyncSpy );
+			sinon.assert.calledWith( markToSyncSpy, img, 'ATTRIBUTES_NEED_UPDATE' );
+		} );
+	} );
+
+	describe( 'removeAttr', () => {
+		it( 'should mark attibutes to sync', () => {
+			img.removeAttr( 'src' );
+
+			sinon.assert.calledOnce( markToSyncSpy );
+			sinon.assert.calledWith( markToSyncSpy, img, 'ATTRIBUTES_NEED_UPDATE' );
+		} );
+	} );
+
+	describe( 'cloneDomAttrs', () => {
+		it( 'should mark attibutes to sync', () => {
+			const domImg = document.createElement( 'img' );
+			domImg.setAttribute( 'width', '10' );
+			domImg.setAttribute( 'height', '10' );
+
+			img.cloneDomAttrs( domImg );
+
+			sinon.assert.calledWith( markToSyncSpy, img, 'ATTRIBUTES_NEED_UPDATE' );
+		} );
+	} );
+
+	describe( 'insertChildren', () => {
+		it( 'should mark children to sync', () => {
+			root.insertChildren( 1, new ViewElement( 'img' ) );
+
+			sinon.assert.calledOnce( markToSyncSpy );
+			sinon.assert.calledWith( markToSyncSpy, root, 'CHILDREN_NEED_UPDATE' );
+		} );
+	} );
+
+	describe( 'appendChildren', () => {
+		it( 'should mark children to sync', () => {
+			root.appendChildren( new ViewElement( 'img' ) );
+
+			sinon.assert.calledOnce( markToSyncSpy );
+			sinon.assert.calledWith( markToSyncSpy, root, 'CHILDREN_NEED_UPDATE' );
+		} );
+	} );
+
+	describe( 'removeChildren', () => {
+		it( 'should mark children to sync', () => {
+			root.removeChildren( 1, 1 );
+
+			sinon.assert.calledOnce( markToSyncSpy );
+			sinon.assert.calledWith( markToSyncSpy, root, 'CHILDREN_NEED_UPDATE' );
+		} );
+	} );
+
+	describe( 'removeChildren', () => {
+		it( 'should mark children to sync', () => {
+			text.setText( 'bar' );
+
+			sinon.assert.calledOnce( markToSyncSpy );
+			sinon.assert.calledWith( markToSyncSpy, text, 'TEXT_NEEDS_UPDATE' );
+		} );
+	} );
+} );

+ 20 - 174
packages/ckeditor5-engine/tests/treeview/node.js

@@ -9,17 +9,16 @@
 
 'use strict';
 
-const getIteratorCount = bender.tools.core.getIteratorCount;
-
 const modules = bender.amd.require(
 	'core/treeview/element',
+	'core/treeview/rootelement',
 	'core/treeview/node',
 	'core/treeview/text',
 	'core/ckeditorerror'
 );
 
 describe( 'Node', () => {
-	let Element, Text, Node, CKEditorError;
+	let Element, Text, Node, RootElement, CKEditorError;
 
 	let root;
 	let one, two, three;
@@ -30,6 +29,7 @@ describe( 'Node', () => {
 		Node = modules[ 'core/treeview/node' ];
 		Text = modules[ 'core/treeview/text' ];
 		CKEditorError = modules[ 'ckeditorerror ' ];
+		RootElement = modules[ 'core/treeview/rootelement' ];
 
 		charB = new Text( 'b' );
 		charA = new Text( 'a' );
@@ -43,34 +43,8 @@ describe( 'Node', () => {
 		root = new Element( null, null, [ one, two, three ] );
 	} );
 
-	describe( 'should have a correct property', () => {
-		it( 'depth', () => {
-			expect( root ).to.have.property( 'depth' ).that.equals( 0 );
-
-			expect( one ).to.have.property( 'depth' ).that.equals( 1 );
-			expect( two ).to.have.property( 'depth' ).that.equals( 1 );
-			expect( three ).to.have.property( 'depth' ).that.equals( 1 );
-
-			expect( charB ).to.have.property( 'depth' ).that.equals( 2 );
-			expect( charA ).to.have.property( 'depth' ).that.equals( 2 );
-			expect( img ).to.have.property( 'depth' ).that.equals( 2 );
-			expect( charR ).to.have.property( 'depth' ).that.equals( 2 );
-		} );
-
-		it( 'root', () => {
-			expect( root ).to.have.property( 'root' ).that.equals( root );
-
-			expect( one ).to.have.property( 'root' ).that.equals( root );
-			expect( two ).to.have.property( 'root' ).that.equals( root );
-			expect( three ).to.have.property( 'root' ).that.equals( root );
-
-			expect( charB ).to.have.property( 'root' ).that.equals( root );
-			expect( charA ).to.have.property( 'root' ).that.equals( root );
-			expect( img ).to.have.property( 'root' ).that.equals( root );
-			expect( charR ).to.have.property( 'root' ).that.equals( root );
-		} );
-
-		it( 'getNextSibling', () => {
+	describe( 'getNextSibling/getPreviousSibling', () => {
+		it( 'should return next sibling', () => {
 			expect( root.getNextSibling() ).to.be.null;
 
 			expect( one.getNextSibling() ).to.equal( two );
@@ -83,7 +57,7 @@ describe( 'Node', () => {
 			expect( charR.getNextSibling() ).to.be.null;
 		} );
 
-		it( 'previousSibling', () => {
+		it( 'should return previous sibling', () => {
 			expect( root.getPreviousSibling() ).to.be.null;
 
 			expect( one.getPreviousSibling() ).to.be.null;
@@ -97,136 +71,6 @@ describe( 'Node', () => {
 		} );
 	} );
 
-	describe( 'constructor', () => {
-		it( 'should copy attributes, not pass by reference', () => {
-			let attrs = [ new Attribute( 'attr', true ) ];
-			let foo = new Element( 'foo', { 'class': 'bold' } );
-			let bar = new Element( 'bar', { 'class': 'bold' } );
-
-			foo.removeAttr( 'attr' );
-
-			expect( getIteratorCount( foo.getAttrs() ) ).to.equal( 0 );
-			expect( getIteratorCount( bar.getAttrs() ) ).to.equal( 1 );
-		} );
-	} );
-
-	describe( 'getAttr', () => {
-		let fooAttr, element;
-
-		beforeEach( () => {
-			fooAttr = new Attribute( 'foo', true );
-			element = new Element( 'foo', [ fooAttr ] );
-		} );
-
-		it( 'should be possible to get attribute by key', () => {
-			expect( element.getAttr( 'foo' ) ).to.equal( fooAttr.value );
-		} );
-
-		it( 'should return null if attribute was not found by key', () => {
-			expect( element.getAttr( 'bar' ) ).to.be.null;
-		} );
-	} );
-
-	describe( 'setAttr', () => {
-		it( 'should insert an attribute', () => {
-			let element = new Element( 'elem' );
-			let attr = new Attribute( 'foo', 'bar' );
-
-			element.setAttr( attr );
-
-			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 1 );
-			expect( element.getAttr( attr.key ) ).to.equal( attr.value );
-		} );
-
-		it( 'should overwrite attribute with the same key', () => {
-			let oldAttr = new Attribute( 'foo', 'bar' );
-			let newAttr = new Attribute( 'foo', 'bar' );
-			let element = new Element( 'elem', [ oldAttr ] );
-
-			element.setAttr( newAttr );
-
-			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 1 );
-			expect( element.getAttr( newAttr.key ) ).to.equal( newAttr.value );
-		} );
-	} );
-
-	describe( 'removeAttr', () => {
-		it( 'should remove an attribute', () => {
-			let attrA = new Attribute( 'a', 'A' );
-			let attrB = new Attribute( 'b', 'b' );
-			let attrC = new Attribute( 'c', 'C' );
-			let element = new Element( 'elem', [ attrA, attrB, attrC ] );
-
-			element.removeAttr( attrB.key );
-
-			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 2 );
-			expect( element.getAttr( attrA.key ) ).to.equal( attrA.value );
-			expect( element.getAttr( attrC.key ) ).to.equal( attrC.value );
-			expect( element.getAttr( attrB.key ) ).to.be.null;
-		} );
-	} );
-
-	describe( 'hasAttr', () => {
-		it( 'should check attribute by key', () => {
-			let fooAttr = new Attribute( 'foo', true );
-			let element = new Element( 'foo', [ fooAttr ] );
-
-			expect( element.hasAttr( 'foo' ) ).to.be.true;
-		} );
-
-		it( 'should return false if attribute was not found by key', () => {
-			let fooAttr = new Attribute( 'foo', true );
-			let element = new Element( 'foo', [ fooAttr ] );
-
-			expect( element.hasAttr( 'bar' ) ).to.be.false;
-		} );
-
-		it( 'should check attribute by object', () => {
-			let fooAttr = new Attribute( 'foo', true );
-			let foo2Attr = new Attribute( 'foo', true );
-			let element = new Element( 'foo', [ fooAttr ] );
-
-			expect( element.hasAttr( foo2Attr ) ).to.be.true;
-		} );
-
-		it( 'should return false if attribute was not found by object', () => {
-			let fooAttr = new Attribute( 'foo', true );
-			let element = new Element( 'foo' );
-
-			expect( element.hasAttr( fooAttr ) ).to.be.false;
-		} );
-
-		it( 'should create proper JSON string using toJSON method', () => {
-			let b = new Character( 'b' );
-			let foo = new Element( 'foo', [], [ b ] );
-
-			let parsedFoo = JSON.parse( JSON.stringify( foo ) );
-			let parsedBar = JSON.parse( JSON.stringify( b ) );
-
-			expect( parsedFoo.parent ).to.equal( null );
-			expect( parsedBar.parent ).to.equal( 'foo' );
-		} );
-	} );
-
-	describe( 'getAttrs', () => {
-		it( 'should allows to get attribute count', () => {
-			let element = new Element( 'foo', [
-				new Attribute( 1, true ),
-				new Attribute( 2, true ),
-				new Attribute( 3, true )
-			] );
-
-			expect( getIteratorCount( element.getAttrs() ) ).to.equal( 3 );
-		} );
-
-		it( 'should allows to copy attributes', () => {
-			let element = new Element( 'foo', [ new Attribute( 'x', true ) ] );
-			let copy = new Element( 'bar', element.getAttrs() );
-
-			expect( copy.getAttr( 'x' ) ).to.be.true;
-		} );
-	} );
-
 	describe( 'getIndex', () => {
 		it( 'should return null if the parent is null', () => {
 			expect( root.getIndex() ).to.be.null;
@@ -244,7 +88,7 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should throw an error if parent does not contains element', () => {
-			let f = new Character( 'f' );
+			let f = new Text( 'f' );
 			let bar = new Element( 'bar', [], [] );
 
 			f.parent = bar;
@@ -253,22 +97,24 @@ describe( 'Node', () => {
 				() => {
 					f.getIndex();
 				}
-			).to.throw( CKEditorError, /node-not-found-in-parent/ );
+			).to.throw( CKEditorError, /treeview-node-not-found-in-parent/ );
 		} );
 	} );
 
-	describe( 'getPath', () => {
-		it( 'should return proper path', () => {
-			expect( root.getPath() ).to.deep.equal( [] );
+	describe( 'getTreeView', () => {
+		it( 'should return null if root is not a RootElement', () => {
+			expect( charA.getTreeView() ).to.be.null;
+		} );
+
+		it( 'should return TreeView attached to the RootElement', () => {
+			const tvMock = {};
+			const parent = new RootElement( 'div', tvMock );
+			const child = new Element( 'p' );
 
-			expect( one.getPath() ).to.deep.equal( [ 0 ] );
-			expect( two.getPath() ).to.deep.equal( [ 1 ] );
-			expect( three.getPath() ).to.deep.equal( [ 2 ] );
+			child.parent = parent;
 
-			expect( charB.getPath() ).to.deep.equal( [ 1, 0 ] );
-			expect( charA.getPath() ).to.deep.equal( [ 1, 1 ] );
-			expect( img.getPath() ).to.deep.equal( [ 1, 2 ] );
-			expect( charR.getPath() ).to.deep.equal( [ 1, 3 ] );
+			expect( parent.getTreeView() ).to.equal( tvMock );
+			expect( child.getTreeView() ).to.equal( tvMock );
 		} );
 	} );
 } );

+ 30 - 0
packages/ckeditor5-engine/tests/treeview/position.js

@@ -0,0 +1,30 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: treeview */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'core/treeview/position'
+);
+
+describe( 'Position', () => {
+	let Position;
+
+	before( () => {
+		Position = modules[ 'core/treeview/position' ];
+	} );
+
+	describe( 'constructor', () => {
+		it( 'should create element without attributes', () => {
+			const parentMock = {};
+			const elem = new Position( parentMock, 5 );
+
+			expect( elem ).to.have.property( 'parent' ).that.equals( parentMock );
+			expect( elem ).to.have.property( 'offset' ).that.equals( 5 );
+		} );
+	} );
+} );

+ 167 - 0
packages/ckeditor5-engine/tests/treeview/text.js

@@ -0,0 +1,167 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* bender-tags: treeview */
+
+/* bender-include: ../_tools/tools.js */
+
+'use strict';
+
+const modules = bender.amd.require(
+	'core/treeview/node',
+	'core/treeview/element',
+	'core/treeview/text'
+);
+
+describe( 'Element', () => {
+	let ViewText, ViewElement, ViewNode;
+
+	before( () => {
+		ViewText = modules[ 'core/treeview/text' ];
+		ViewElement = modules[ 'core/treeview/element' ];
+		ViewNode = modules[ 'core/treeview/node' ];
+	} );
+
+	describe( 'constructor', () => {
+		it( 'should create element without attributes', () => {
+			const text = new ViewText( 'foo' );
+
+			expect( text ).to.be.an.instanceof( ViewNode );
+			expect( text.getText() ).to.equal( 'foo' );
+			expect( text ).to.have.property( 'parent' ).that.is.null;
+		} );
+	} );
+
+	describe( 'setText', () => {
+		it( 'should change the text', () => {
+			const text = new ViewText( 'foo' );
+			text.setText( 'bar' );
+
+			expect( text.getText() ).to.equal( 'bar' );
+		} );
+	} );
+
+	describe( 'DOM-View binding', () => {
+		describe( 'getCorespondingView', () => {
+			it( 'should return coresponding view element based on sibling', () => {
+				const domImg = document.createElement( 'img' );
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domImg );
+				domP.appendChild( domText );
+
+				const viewImg = new ViewElement( 'img' );
+
+				viewImg.bindDomElement( domImg );
+
+				const viewP = ViewElement.createFromDom( domP );
+				const viewText = viewP.getChild( 1 );
+
+				expect( ViewText.getCorespondingView( domText ) ).to.equal( viewText );
+			} );
+
+			it( 'should return coresponding view element based on parent', () => {
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domText );
+
+				const viewP = ViewElement.createFromDom( domP );
+				const viewText = viewP.getChild( 0 );
+
+				viewP.bindDomElement( domP );
+
+				expect( ViewText.getCorespondingView( domText ) ).to.equal( viewText );
+			} );
+
+			it( 'should return null if sibling is not binded', () => {
+				const domImg = document.createElement( 'img' );
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domImg );
+				domP.appendChild( domText );
+
+				const viewP = ViewElement.createFromDom( domP );
+
+				viewP.bindDomElement( domP );
+
+				expect( ViewText.getCorespondingView( domText ) ).to.be.null;
+			} );
+
+			it( 'should return null if parent is not binded', () => {
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domText );
+
+				expect( ViewText.getCorespondingView( domText ) ).to.be.null;
+			} );
+		} );
+
+		describe( 'getCorespondingDom', () => {
+			it( 'should return coresponding DOM element based on sibling', () => {
+				const domImg = document.createElement( 'img' );
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domImg );
+				domP.appendChild( domText );
+
+				const viewImg = new ViewElement( 'img' );
+
+				viewImg.bindDomElement( domImg );
+
+				const viewP = ViewElement.createFromDom( domP );
+				const viewText = viewP.getChild( 1 );
+
+				expect( viewText.getCorespondingDom() ).to.equal( domText );
+			} );
+
+			it( 'should return coresponding DOM element based on parent', () => {
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domText );
+
+				const viewP = ViewElement.createFromDom( domP );
+				const viewText = viewP.getChild( 0 );
+
+				viewP.bindDomElement( domP );
+
+				expect( viewText.getCorespondingDom() ).to.equal( domText );
+			} );
+
+			it( 'should return null if sibling is not binded', () => {
+				const domImg = document.createElement( 'img' );
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domImg );
+				domP.appendChild( domText );
+
+				const viewP = ViewElement.createFromDom( domP );
+				const viewText = viewP.getChild( 1 );
+
+				viewP.bindDomElement( domP );
+
+				expect( viewText.getCorespondingDom() ).to.be.null;
+			} );
+
+			it( 'should return null if parent is not binded', () => {
+				const domText = document.createTextNode( 'foo' );
+				const domP = document.createElement( 'p' );
+
+				domP.appendChild( domText );
+
+				const viewP = ViewElement.createFromDom( domP );
+				const viewText = viewP.getChild( 0 );
+
+				expect( viewText.getCorespondingDom() ).to.be.null;
+			} );
+		} );
+	} );
+} );