Forráskód Böngészése

Renderer tests. Export converting functions to the separate file.

Piotr Jasiun 10 éve
szülő
commit
0df14be539

+ 152 - 0
packages/ckeditor5-engine/src/treeview/converter.js

@@ -0,0 +1,152 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import ViewText from './text.js';
+import ViewElement from './element.js';
+
+const _domToViewMapping = new WeakMap();
+const _viewToDomMapping = new WeakMap();
+
+const converter = {
+	viewToDom: function( view, domDocument ) {
+		if ( view instanceof ViewText ) {
+			return domDocument.createTextNode( view.getText() );
+		} else {
+			if ( converter.getCorespondingDom( view ) ) {
+				return converter.getCorespondingDom( view );
+			}
+
+			const domElement = domDocument.createElement( view.name );
+			converter.bindElement( view, domElement );
+
+			for ( let key of view.getAttrKeys() ) {
+				domElement.setAttribute( key, view.getAttr( key ) );
+			}
+
+			for ( let childView of view.getChildren() ) {
+				domElement.appendChild( converter.viewToDom( childView ) );
+			}
+
+			return domElement;
+		}
+	},
+
+	// Note that created elements will not have coresponding DOM elements created it these did not exist before.
+	domToView: function( domElement ) {
+		let viewElement = converter.getCorespondingView( domElement );
+
+		if ( viewElement ) {
+			return viewElement;
+		}
+
+		if ( domElement instanceof Text ) {
+			return new ViewText( domElement.data );
+		} else {
+			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 i = 0, len = domElement.childNodes.length; i < len; i++ ) {
+				let domChild = domElement.childNodes[ i ];
+
+				viewElement.appendChildren( converter.domToView( domChild ) );
+			}
+
+			return viewElement;
+		}
+	},
+
+	compareNodes: function( domNode, viewNode ) {
+		// Elements.
+		if ( domNode instanceof HTMLElement && viewNode instanceof ViewElement ) {
+			return domNode === converter.getCorespondingDomElement( viewNode );
+		}
+		// Texts.
+		else if ( domNode instanceof Text && viewNode instanceof ViewText ) {
+			return domNode.data === viewNode.getText();
+		}
+
+		// Not matching types.
+		return false;
+	},
+
+	getCorespondingView: function( domNode ) {
+		if ( domNode instanceof HTMLElement ) {
+			return domNode.getCorespondingViewElement( domNode );
+		} else {
+			return domNode.getCorespondingViewText( domNode );
+		}
+	},
+
+	// Coresponding elements exists only for rendered elementes.
+	getCorespondingViewElement: function( domElement ) {
+		return _domToViewMapping.get( domElement );
+	},
+
+	getCorespondingViewText: function( domText ) {
+		const previousSibling = domText.previousSibling;
+
+		if ( previousSibling ) {
+			const viewElement = Element.getCorespondingView( previousSibling );
+
+			if ( viewElement ) {
+				return viewElement.getNextSibling();
+			}
+		} else {
+			const viewElement = Element.getCorespondingView( domText.parentElement );
+
+			if ( viewElement ) {
+				return viewElement.getChild( 0 );
+			}
+		}
+
+		return null;
+	},
+
+	getCorespondingDom: function( viewNode ) {
+		if ( viewNode instanceof ViewElement ) {
+			return converter.getCorespondingDomElement( viewNode );
+		} else {
+			return converter.getCorespondingDomText( viewNode );
+		}
+	},
+
+	getCorespondingDomElement: function( viewElement ) {
+		return _viewToDomMapping.get( viewElement );
+	},
+
+	getCorespondingDomText: function( viewText ) {
+		const previousSibling = viewText.getPreviousSibling();
+
+		if ( previousSibling && converter.getCorespondingDom( previousSibling ) ) {
+			return converter.getCorespondingDom( previousSibling ).nextSibling;
+		}
+
+		if ( !previousSibling && converter.getCorespondingDom( viewText.parent ) ) {
+			return converter.getCorespondingDom( viewText.parent ).childNodes[ 0 ];
+		}
+
+		return null;
+	},
+
+	bindElement: function( viewElement, domElement ) {
+		_domToViewMapping.set( domElement, viewElement );
+		_viewToDomMapping.set( viewElement, domElement );
+	},
+
+	cloneDomAttrs: function( domElement, viewElement ) {
+		for ( let i = domElement.attributes.length - 1; i >= 0; i-- ) {
+			let attr = domElement.attributes[ i ];
+			viewElement.setAttr( attr.name, attr.value );
+		}
+	}
+};
+
+export default converter;

+ 0 - 55
packages/ckeditor5-engine/src/treeview/element.js

@@ -5,7 +5,6 @@
 
 'use strict';
 
-import ViewText from './text.js';
 import Node from './node.js';
 import utils from '../utils.js';
 import langUtils from '../lib/lodash/lang.js';
@@ -38,21 +37,6 @@ export default class Element extends Node {
 		this.insertChildren( this.getChildCount(), nodes );
 	}
 
-	bindDomElement( domElement ) {
-		this._domElement = domElement;
-
-		Element._domToViewMapping.set( domElement, this );
-	}
-
-	cloneDomAttrs( element ) {
-		this.markToSync( 'ATTRIBUTES_NEED_UPDATE' );
-
-		for ( let i = element.attributes.length - 1; i >= 0; i-- ) {
-			let attr = element.attributes[ i ];
-			this.setAttr( attr.name, attr.value );
-		}
-	}
-
 	getChild( index ) {
 		return this._children[ index ];
 	}
@@ -69,10 +53,6 @@ export default class Element extends Node {
 		return this._children[ Symbol.iterator ]();
 	}
 
-	getCorespondingDom() {
-		return this._domElement;
-	}
-
 	getAttrKeys() {
 		return this._attrs.keys();
 	}
@@ -121,39 +101,4 @@ export default class Element extends Node {
 
 		return this._children.splice( index, number );
 	}
-
-	// Note that created elements will not have coresponding DOM elements created it these did not exist before.
-	static createFromDom( domElement ) {
-		let viewElement = this.getCorespondingView( domElement );
-
-		if ( viewElement ) {
-			return viewElement;
-		}
-
-		if ( domElement instanceof Text ) {
-			return new ViewText( domElement.data );
-		} else {
-			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 i = 0, len = domElement.childNodes.length; i < len; i++ ) {
-				let domChild = domElement.childNodes[ i ];
-
-				viewElement.appendChildren( this.createFromDom( domChild ) );
-			}
-
-			return viewElement;
-		}
-	}
-
-	// Coresponding elements exists only for rendered elementes.
-	static getCorespondingView( domElement ) {
-		return this._domToViewMapping.get( domElement );
-	}
 }
-
-Element._domToViewMapping = new WeakMap();

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

@@ -57,7 +57,7 @@ export default class Node {
 
 		// If element is not attached to the Tree view it is a child of the detached subtree and will be rendered anyway with this
 		// subtree.
-		if ( !treeView ) {
+		if ( !treeView || !treeView.renderer ) {
 			return;
 		}
 

+ 17 - 54
packages/ckeditor5-engine/src/treeview/renderer.js

@@ -6,8 +6,7 @@
 'use strict';
 
 import diff from '../utils-diff.js';
-import ViewText from './text.js';
-import ViewElement from './element.js';
+import converter from './converter.js';
 import CKEditorError from '../ckeditorerror.js';
 
 export default class Renderer {
@@ -19,12 +18,12 @@ export default class Renderer {
 
 	markToSync( node, type ) {
 		if ( type === 'TEXT_NEEDS_UPDATE' ) {
-			if ( node.parent.getCorespondingDom() ) {
+			if ( converter.getCorespondingDom( node.parent ) ) {
 				this.markedTexts.add( node );
 			}
 		} else {
 			// If the node has no DOM element it is not rendered yet, its children/attributes do not need to be marked to be sync.
-			if ( !node.getCorespondingDom() ) {
+			if ( !converter.getCorespondingDom( node ) ) {
 				return;
 			}
 
@@ -32,19 +31,20 @@ export default class Renderer {
 				this.markedAttrs.add( node );
 			} else if ( type === 'CHILDREN_NEED_UPDATE' ) {
 				this.markedChildren.add( node );
+			} else {
+				/**
+				 * Unknown type passed to Renderer.markToSync.
+				 *
+				 * @error renderer-unknown-type
+				 */
+				throw new CKEditorError( 'renderer-unknown-type: Unknown type passed to Renderer.markToSync.' );
 			}
 		}
-		/**
-		 * Unknown type passed to Renderer.markToSync.
-		 *
-		 * @error renderer-unknown-type
-		 */
-		throw new CKEditorError( 'renderer-unknown-type: Unknown type passed to Renderer.markToSync.' );
 	}
 
 	render() {
 		for ( let node of this.markedTexts ) {
-			if ( !this.markedChildren.has( node.parent ) && node.parent.getCorespondingDom() ) {
+			if ( !this.markedChildren.has( node.parent ) && converter.getCorespondingDom( node.parent ) ) {
 				updateText( node );
 			}
 		}
@@ -62,7 +62,7 @@ export default class Renderer {
 		this.markedChildren.clear();
 
 		function updateText( viewText ) {
-			const domText = viewText.getCorespondingDom();
+			const domText = converter.getCorespondingDom( viewText );
 
 			if ( domText.data != viewText.getText() ) {
 				domText.data = viewText.getText();
@@ -70,8 +70,8 @@ export default class Renderer {
 		}
 
 		function updateAttrs( viewElement ) {
-			const domElement = viewElement.getCorespondingDom();
-			const domAttrKeys = domElement.attributes;
+			const domElement = converter.getCorespondingDom( viewElement );
+			const domAttrKeys = Array.from( domElement.attributes ).map( attr => attr.name );
 			const viewAttrKeys = viewElement.getAttrKeys();
 
 			// Add or overwrite attributes.
@@ -88,61 +88,24 @@ export default class Renderer {
 		}
 
 		function updateChildren( viewElement ) {
-			const domElement = viewElement.getCorespondingDom();
+			const domElement = converter.getCorespondingDom( viewElement );
 			const domChildren = domElement.childNodes;
 			const viewChildren = Array.from( viewElement.getChildren() );
 			const domDocument = domElement.ownerDocument;
 
-			const actions = diff( domChildren, viewChildren, compareNodes );
+			const actions = diff( domChildren, viewChildren, converter.compareNodes );
 			let i = 0;
 
 			for ( let action of actions ) {
 				if ( action === 'EQUAL' ) {
 					i++;
 				} else if ( action === 'INSERT' ) {
-					domElement.insertBefore( viewToDom( viewChildren[ i ], domDocument ), domChildren[ i ] || null  );
+					domElement.insertBefore( converter.viewToDom( viewChildren[ i ], domDocument ), domChildren[ i ] || null  );
 					i++;
 				} else if ( action === 'DELETE' ) {
 					domElement.removeChild( domChildren[ i ] );
 				}
 			}
 		}
-
-		function compareNodes( domNode, viewNode ) {
-			// Elements.
-			if ( domNode instanceof HTMLElement && viewNode instanceof ViewElement ) {
-				return domNode === viewNode.getCorespondingDom();
-			}
-			// Texts.
-			else if ( domNode instanceof Text && viewNode instanceof ViewText ) {
-				return domNode.data === viewNode.getText();
-			}
-
-			// Not matching types.
-			return false;
-		}
-
-		function viewToDom( view, domDocument ) {
-			if ( view instanceof ViewText ) {
-				return domDocument.createTextNode( view.getText() );
-			} else {
-				if ( view.getCorespondingDom() ) {
-					return view.getCorespondingDom();
-				}
-
-				const domElement = domDocument.createElement( view.name );
-				view.bindDomElement( domElement );
-
-				for ( let key of view.getAttrKeys() ) {
-					domElement.setAttribute( key, view.getAttr( key ) );
-				}
-
-				for ( let childView of view.getChildren() ) {
-					domElement.appendChild( viewToDom( childView ) );
-				}
-
-				return domElement;
-			}
-		}
 	}
 }

+ 0 - 35
packages/ckeditor5-engine/src/treeview/text.js

@@ -6,7 +6,6 @@
 'use strict';
 
 import Node from './node.js';
-import Element from './element.js';
 
 export default class Text extends Node {
 	constructor( text ) {
@@ -15,20 +14,6 @@ export default class Text extends Node {
 		this._text = text;
 	}
 
-	getCorespondingDom() {
-		const previousSibling = this.getPreviousSibling();
-
-		if ( previousSibling && previousSibling.getCorespondingDom() ) {
-			return previousSibling.getCorespondingDom().nextSibling;
-		}
-
-		if ( !previousSibling && this.parent.getCorespondingDom() ) {
-			return this.parent.getCorespondingDom().childNodes[ 0 ];
-		}
-
-		return null;
-	}
-
 	getText() {
 		return this._text;
 	}
@@ -38,24 +23,4 @@ export default class Text extends Node {
 
 		this._text = text;
 	}
-
-	static getCorespondingView( domText ) {
-		const previousSibling = domText.previousSibling;
-
-		if ( previousSibling ) {
-			const viewElement = Element.getCorespondingView( previousSibling );
-
-			if ( viewElement ) {
-				return viewElement.getNextSibling();
-			}
-		} else {
-			const viewElement = Element.getCorespondingView( domText.parentElement );
-
-			if ( viewElement ) {
-				return viewElement.getChild( 0 );
-			}
-		}
-
-		return null;
-	}
 }

+ 11 - 9
packages/ckeditor5-engine/src/treeview/treeview.js

@@ -5,29 +5,31 @@
 
 'use strict';
 
-import objectUtils from '../lib/lodash/object.js';
 import EmitterMixin from '../emittermixin.js';
 import RootElement from './rootelement.js';
 import Renderer from './renderer.js';
 
+import objectUtils from '../lib/lodash/object.js';
+import converter from './converter.js';
+
 export default class TreeView {
-	constructor( domElement ) {
+	constructor( domRoot ) {
 		/**
 		 * Root of the DOM.
 		 */
-		this.domRoot = domElement;
+		this.domRoot = domRoot;
 
 		this.observers = new Set();
 
-		this.renderer = new Renderer( this );
-
 		/**
 		 * Root of the view
 		 */
-		this.viewRoot = new RootElement( domElement.tagName.toLowerCase(), this );
-		this.viewRoot.cloneDomAttrs( domElement );
-		this.viewRoot.bindDomElement( domElement );
-		this.viewRoot.markToSync( 'CHILDREN_NEED_UPDATE' );
+		this.viewRoot = new RootElement( domRoot.tagName.toLowerCase(), this );
+		converter.cloneDomAttrs( domRoot, this.viewRoot );
+		converter.bindElement( this.viewRoot, domRoot );
+
+		this.renderer = new Renderer( this );
+		this.renderer.markToSync( this.viewRoot, 'CHILDREN_NEED_UPDATE' );
 	}
 
 	addObserver( observer ) {

+ 0 - 70
packages/ckeditor5-engine/tests/treeview/element.js

@@ -204,75 +204,5 @@ describe( 'Element', () => {
 				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( '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( viewP.getCorespondingDom() ).to.not.equal( domP );
-				expect( viewP.getChild( 0 ).getCorespondingDom() ).to.equal( domImg );
-			} );
-		} );
 	} );
 } );

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

@@ -58,18 +58,6 @@ describe( 'Node.markToSync', () => {
 		} );
 	} );
 
-	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' ) );

+ 259 - 0
packages/ckeditor5-engine/tests/treeview/renderer.js

@@ -0,0 +1,259 @@
+/**
+ * @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/renderer',
+	'core/treeview/element',
+	'core/treeview/text',
+	'core/treeview/converter',
+	'core/ckeditorerror'
+);
+
+describe( 'Renderer', () => {
+	let Renderer, ViewElement, ViewText, CKEditorError, converter;
+	let renderer;
+
+	before( () => {
+		Renderer = modules[ 'core/treeview/renderer' ];
+		ViewElement = modules[ 'core/treeview/element' ];
+		ViewText = modules[ 'core/treeview/text' ];
+		CKEditorError = modules[ 'core/ckeditorerror' ];
+		converter = modules[ 'core/treeview/converter' ];
+
+		renderer = new Renderer();
+	} );
+
+	describe( 'markToSync', () => {
+		let viewNode;
+
+		beforeEach( () => {
+			viewNode = new ViewElement( 'p' );
+
+			const domNode = document.createElement( 'p' );
+			converter.bindElement( viewNode, domNode );
+			viewNode.appendChildren( new ViewText( 'foo' ) );
+
+			renderer.markedTexts.clear();
+			renderer.markedAttrs.clear();
+			renderer.markedChildren.clear();
+		} );
+
+		it( 'should mark attributes which need update', () => {
+			viewNode.setAttr( 'class', 'foo' );
+
+			renderer.markToSync( viewNode, 'ATTRIBUTES_NEED_UPDATE' );
+
+			expect( renderer.markedAttrs.has( viewNode ) ).to.be.true;
+		} );
+
+		it( 'should mark children which need update', () => {
+			viewNode.appendChildren( new ViewText( 'foo' ) );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+
+			expect( renderer.markedChildren.has( viewNode ) ).to.be.true;
+		} );
+
+		it( 'should not mark children if element has no corresponding node', () => {
+			// Overwrite viewNode with node without coresponding DOM node.
+			viewNode = new ViewElement( 'p' );
+
+			viewNode.appendChildren( new ViewText( 'foo' ) );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+
+			expect( renderer.markedTexts.has( viewNode ) ).to.be.false;
+		} );
+
+		it( 'should mark text which need update', () => {
+			const viewText = new ViewText( 'foo' );
+			viewNode.appendChildren( viewText );
+			viewText.setText( 'bar' );
+
+			renderer.markToSync( viewText, 'TEXT_NEEDS_UPDATE' );
+
+			expect( renderer.markedTexts.has( viewText ) ).to.be.true;
+		} );
+
+		it( 'should not mark text if parent has no corresponding node', () => {
+			const viewText = new ViewText( 'foo' );
+			// Overwrite viewNode with node without coresponding DOM node.
+			viewNode = new ViewElement( 'p' );
+
+			viewNode.appendChildren( viewText );
+			viewText.setText( 'bar' );
+
+			renderer.markToSync( viewText, 'TEXT_NEEDS_UPDATE' );
+
+			expect( renderer.markedTexts.has( viewText ) ).to.be.false;
+		} );
+
+		it( 'should throw if the type is unknown', () => {
+			expect( () => {
+				renderer.markToSync( viewNode, 'UNKNOWN' );
+			} ).to.throw( CKEditorError, /^renderer-unknown-type/ );
+		} );
+	} );
+
+	describe( 'render', () => {
+		let viewNode, domNode;
+
+		beforeEach( () => {
+			viewNode = new ViewElement( 'p' );
+			domNode = document.createElement( 'p' );
+
+			converter.bindElement( viewNode, domNode );
+
+			renderer.markedTexts.clear();
+			renderer.markedAttrs.clear();
+			renderer.markedChildren.clear();
+		} );
+
+		it( 'should update attributes', () => {
+			viewNode.setAttr( 'class', 'foo' );
+
+			renderer.markToSync( viewNode, 'ATTRIBUTES_NEED_UPDATE' );
+			renderer.render();
+
+			expect( domNode.getAttribute( 'class' ) ).to.equal( 'foo' );
+
+			expect( renderer.markedAttrs.size ).to.equal( 0 );
+		} );
+
+		it( 'should remove attributes', () => {
+			viewNode.setAttr( 'class', 'foo' );
+			domNode.setAttribute( 'id', 'bar' );
+			domNode.setAttribute( 'class', 'bar' );
+
+			renderer.markToSync( viewNode, 'ATTRIBUTES_NEED_UPDATE' );
+			renderer.render();
+
+			expect( domNode.getAttribute( 'class' ) ).to.equal( 'foo' );
+			expect( domNode.getAttribute( 'id' ) ).to.be.not.ok;
+
+			expect( renderer.markedAttrs.size ).to.equal( 0 );
+		} );
+
+		it( 'should add children', () => {
+			viewNode.appendChildren( new ViewText( 'foo' ) );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 1 );
+			expect( domNode.childNodes[ 0 ].data ).to.equal( 'foo' );
+
+			expect( renderer.markedChildren.size ).to.equal( 0 );
+		} );
+
+		it( 'should update text', () => {
+			const viewText = new ViewText( 'foo' );
+			viewNode.appendChildren( viewText );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 1 );
+			expect( domNode.childNodes[ 0 ].data ).to.equal( 'foo' );
+
+			viewText.setText( 'bar' );
+
+			renderer.markToSync( viewText, 'TEXT_NEEDS_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 1 );
+			expect( domNode.childNodes[ 0 ].data ).to.equal( 'bar' );
+
+			expect( renderer.markedTexts.size ).to.equal( 0 );
+		} );
+
+		it( 'should not update text parent child list changed', () => {
+			const viewImg = new ViewElement( 'img' );
+			const viewText = new ViewText( 'foo' );
+			viewNode.appendChildren( [ viewImg, viewText ] );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.markToSync( viewText, 'TEXT_NEEDS_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 2 );
+			expect( domNode.childNodes[ 0 ].tagName.toLowerCase() ).to.equal( 'img' );
+			expect( domNode.childNodes[ 1 ].data ).to.equal( 'foo' );
+		} );
+
+		it( 'should not change text if it is the same during text rendering', () => {
+			const viewText = new ViewText( 'foo' );
+			viewNode.appendChildren( viewText );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			// This should not be changed during the render.
+			const domText = domNode.childNodes[ 0 ];
+
+			renderer.markToSync( viewText, 'TEXT_NEEDS_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 1 );
+			expect( domNode.childNodes[ 0 ] ).to.equal( domText );
+		} );
+
+		it( 'should not change text if it is the same during children rendering', () => {
+			const viewText = new ViewText( 'foo' );
+			viewNode.appendChildren( viewText );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			// This should not be changed during the render.
+			const domText = domNode.childNodes[ 0 ];
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 1 );
+			expect( domNode.childNodes[ 0 ] ).to.equal( domText );
+		} );
+
+		it( 'should not change element if it is the same', () => {
+			const viewImg = new ViewElement( 'img' );
+			viewNode.appendChildren( viewImg );
+
+			// This should not be changed during the render.
+			const domImg = document.createElement( 'img' );
+			domNode.appendChild( domImg );
+
+			converter.bindElement( viewImg, domImg );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 1 );
+			expect( domNode.childNodes[ 0 ] ).to.equal( domImg );
+		} );
+
+		it( 'should change element if it is different', () => {
+			const viewImg = new ViewElement( 'img' );
+			viewNode.appendChildren( viewImg );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			const viewP = new ViewElement( 'p' );
+			viewNode.removeChildren( 0, 1 );
+			viewNode.appendChildren( viewP );
+
+			renderer.markToSync( viewNode, 'CHILDREN_NEED_UPDATE' );
+			renderer.render();
+
+			expect( domNode.childNodes.length ).to.equal( 1 );
+			expect( domNode.childNodes[ 0 ].tagName.toLowerCase() ).to.equal( 'p' );
+		} );
+	} );
+} );

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

@@ -40,126 +40,4 @@ describe( 'Element', () => {
 			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;
-			} );
-		} );
-	} );
 } );

+ 5 - 3
packages/ckeditor5-engine/tests/treeview/treeview.js

@@ -10,16 +10,18 @@
 const modules = bender.amd.require(
 	'core/treeview/treeview',
 	'core/treeview/observer/observer',
-	'core/treeview/renderer'
+	'core/treeview/renderer',
+	'core/treeview/converter'
 );
 
 describe( 'TreeView', () => {
-	let TreeView, Observer, Renderer;
+	let TreeView, Observer, Renderer, converter;
 
 	before( () => {
 		TreeView = modules[ 'core/treeview/treeview' ];
 		Observer = modules[ 'core/treeview/observer/observer' ];
 		Renderer = modules[ 'core/treeview/renderer' ];
+		converter = modules[ 'core/treeview/converter' ];
 	} );
 
 	describe( 'constructor', () => {
@@ -36,7 +38,7 @@ describe( 'TreeView', () => {
 			expect( treeView ).to.have.property( 'renderer' ).that.is.instanceOf( Renderer );
 			expect( treeView ).to.have.property( 'viewRoot' );
 
-			expect( treeView.viewRoot.getCorespondingDom() ).to.equal( domDiv );
+			expect( converter.getCorespondingDom( treeView.viewRoot ) ).to.equal( domDiv );
 			expect( treeView.viewRoot.name ).to.equal( 'div' );
 			expect( treeView.viewRoot.getAttr( 'id' ) ).to.equal( 'editor' );
 			expect( treeView.renderer.markedChildren.has( treeView.viewRoot ) ).to.be.true;