浏览代码

Tests: renderer similar selection unit tests.

Krzysztof Krztoń 8 年之前
父节点
当前提交
6618af24bf
共有 1 个文件被更改,包括 276 次插入0 次删除
  1. 276 0
      packages/ckeditor5-engine/tests/view/renderer.js

+ 276 - 0
packages/ckeditor5-engine/tests/view/renderer.js

@@ -8,6 +8,7 @@
 import ViewElement from '../../src/view/element';
 import ViewText from '../../src/view/text';
 import ViewRange from '../../src/view/range';
+import ViewPosition from '../../src/view/position';
 import Selection from '../../src/view/selection';
 import DomConverter from '../../src/view/domconverter';
 import Renderer from '../../src/view/renderer';
@@ -1349,6 +1350,281 @@ describe( 'Renderer', () => {
 				expect( bindSelection.isEqual( selection ) ).to.be.true;
 			} );
 		} );
+
+		// #887
+		describe( 'similar selection', () => {
+			// Use spies to check selection updates. Some selection positions are not achievable in some
+			// browsers (e.g. <p>Foo<b>{}Bar</b></p> in Chrome) so asserting dom selection after rendering would fail.
+			let selectionCollapseSpy, selectionExtendSpy;
+
+			afterEach( () => {
+				if ( selectionCollapseSpy ) {
+					selectionCollapseSpy.restore();
+					selectionCollapseSpy = null;
+				}
+
+				if ( selectionExtendSpy ) {
+					selectionExtendSpy.restore();
+					selectionExtendSpy = null;
+				}
+			} );
+
+			it( 'should always render collapsed selection even if it is similar', () => {
+				const domSelection = document.getSelection();
+
+				const { view: viewP, selection: newSelection } = parse(
+					'<container:p>foo{}<attribute:b>bar</attribute:b></container:p>' );
+
+				viewRoot.appendChildren( viewP );
+				selection.setTo( newSelection );
+
+				renderer.markToSync( 'children', viewRoot );
+				renderer.render();
+
+				const domP = domRoot.childNodes[ 0 ];
+				const domB = domP.childNodes[ 1 ];
+				const viewB = viewRoot.getChild( 0 ).getChild( 1 );
+
+				expect( domSelection.isCollapsed ).to.true;
+				expect( domSelection.rangeCount ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).startContainer ).to.equal( domP.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).startOffset ).to.equal( 3 );
+				expect( domSelection.getRangeAt( 0 ).endContainer ).to.equal( domP.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).endOffset ).to.equal( 3 );
+
+				selectionCollapseSpy = sinon.spy( window.Selection.prototype, 'collapse' );
+				selectionExtendSpy = sinon.spy( window.Selection.prototype, 'extend' );
+
+				// <container:p>foo<attribute:b>{}bar</attribute:b></container:p>
+				selection.setRanges( [ new ViewRange( new ViewPosition( viewB.getChild( 0 ), 0 ), new ViewPosition( viewB.getChild( 0 ), 0 ) ) ] );
+
+				renderer.markToSync( 'children', viewP );
+				renderer.render();
+
+				expect( selectionCollapseSpy.calledOnce ).to.true;
+				expect( selectionCollapseSpy.calledWith( domB.childNodes[ 0 ], 0 ) ).to.true;
+				expect( selectionExtendSpy.calledOnce ).to.true;
+				expect( selectionExtendSpy.calledWith( domB.childNodes[ 0 ], 0 ) ).to.true;
+			} );
+
+			it( 'should always render collapsed selection even if it is similar (with empty element)', () => {
+				const domSelection = document.getSelection();
+
+				const { view: viewP, selection: newSelection } = parse(
+					'<container:p>foo<attribute:b>[]</attribute:b></container:p>' );
+
+				viewRoot.appendChildren( viewP );
+				selection.setTo( newSelection );
+
+				renderer.markToSync( 'children', viewRoot );
+				renderer.render();
+
+				const domP = domRoot.childNodes[ 0 ];
+				const domB = domP.childNodes[ 1 ];
+
+				expect( domSelection.isCollapsed ).to.true;
+				expect( domSelection.rangeCount ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).startContainer ).to.equal( domB.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).startOffset ).to.equal( INLINE_FILLER_LENGTH );
+				expect( domSelection.getRangeAt( 0 ).endContainer ).to.equal( domB.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).endOffset ).to.equal( INLINE_FILLER_LENGTH );
+
+				selectionCollapseSpy = sinon.spy( window.Selection.prototype, 'collapse' );
+				selectionExtendSpy = sinon.spy( window.Selection.prototype, 'extend' );
+
+				// <container:p>foo{}<attribute:b></attribute:b></container:p>
+				selection.setRanges( [ new ViewRange( new ViewPosition( viewP.getChild( 0 ), 3 ), new ViewPosition( viewP.getChild( 0 ), 3 ) ) ] );
+
+				renderer.markToSync( 'children', viewP );
+				renderer.render();
+
+				expect( selectionCollapseSpy.calledOnce ).to.true;
+				expect( selectionCollapseSpy.calledWith( domP.childNodes[ 0 ], 3 ) ).to.true;
+				expect( selectionExtendSpy.calledOnce ).to.true;
+				expect( selectionExtendSpy.calledWith( domP.childNodes[ 0 ], 3 ) ).to.true;
+			} );
+
+			it( 'should always render non-collapsed selection if it not is similar', () => {
+				const domSelection = document.getSelection();
+
+				const { view: viewP, selection: newSelection } = parse(
+					'<container:p>fo{o}<attribute:b>bar</attribute:b></container:p>' );
+
+				viewRoot.appendChildren( viewP );
+				selection.setTo( newSelection );
+
+				renderer.markToSync( 'children', viewRoot );
+				renderer.render();
+
+				const domP = domRoot.childNodes[ 0 ];
+				const domB = domP.childNodes[ 1 ];
+				const viewB = viewRoot.getChild( 0 ).getChild( 1 );
+
+				expect( domSelection.isCollapsed ).to.false;
+				expect( domSelection.rangeCount ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).startContainer ).to.equal( domP.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).startOffset ).to.equal( 2 );
+				expect( domSelection.getRangeAt( 0 ).endContainer ).to.equal( domP.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).endOffset ).to.equal( 3 );
+
+				selectionCollapseSpy = sinon.spy( window.Selection.prototype, 'collapse' );
+				selectionExtendSpy = sinon.spy( window.Selection.prototype, 'extend' );
+
+				// <container:p>fo{o<attribute:b>b}ar</attribute:b></container:p>
+				selection.setRanges( [ new ViewRange( new ViewPosition( viewP.getChild( 0 ), 2 ), new ViewPosition( viewB.getChild( 0 ), 1 ) ) ] );
+
+				renderer.markToSync( 'children', viewP );
+				renderer.render();
+
+				expect( selectionCollapseSpy.calledOnce ).to.true;
+				expect( selectionCollapseSpy.calledWith( domP.childNodes[ 0 ], 2 ) ).to.true;
+				expect( selectionExtendSpy.calledOnce ).to.true;
+				expect( selectionExtendSpy.calledWith( domB.childNodes[ 0 ], 1 ) ).to.true;
+			} );
+
+			it( 'should not render non-collapsed selection it is similar (element start)', () => {
+				const domSelection = document.getSelection();
+
+				const { view: viewP, selection: newSelection } = parse(
+					'<container:p>foo<attribute:b>{ba}r</attribute:b></container:p>' );
+
+				viewRoot.appendChildren( viewP );
+				selection.setTo( newSelection );
+
+				renderer.markToSync( 'children', viewRoot );
+				renderer.render();
+
+				const domP = domRoot.childNodes[ 0 ];
+				const domB = domP.childNodes[ 1 ];
+				const viewB = viewRoot.getChild( 0 ).getChild( 1 );
+
+				expect( domSelection.isCollapsed ).to.false;
+				expect( domSelection.rangeCount ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).startContainer ).to.equal( domB.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).startOffset ).to.equal( 0 );
+				expect( domSelection.getRangeAt( 0 ).endContainer ).to.equal( domB.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).endOffset ).to.equal( 2 );
+
+				selectionCollapseSpy = sinon.spy( window.Selection.prototype, 'collapse' );
+				selectionExtendSpy = sinon.spy( window.Selection.prototype, 'extend' );
+
+				// <container:p>foo{<attribute:b>ba}r</attribute:b></container:p>
+				selection.setRanges( [ new ViewRange( new ViewPosition( viewP.getChild( 0 ), 3 ), new ViewPosition( viewB.getChild( 0 ), 2 ) ) ] );
+
+				renderer.markToSync( 'children', viewP );
+				renderer.render();
+
+				expect( selectionCollapseSpy.notCalled ).to.true;
+				expect( selectionExtendSpy.notCalled ).to.true;
+			} );
+
+			it( 'should not render non-collapsed selection it is similar (element end)', () => {
+				const domSelection = document.getSelection();
+
+				const { view: viewP, selection: newSelection } = parse(
+					'<container:p>foo<attribute:b>b{ar}</attribute:b>baz</container:p>' );
+
+				viewRoot.appendChildren( viewP );
+				selection.setTo( newSelection );
+
+				renderer.markToSync( 'children', viewRoot );
+				renderer.render();
+
+				const domP = domRoot.childNodes[ 0 ];
+				const domB = domP.childNodes[ 1 ];
+				const viewB = viewRoot.getChild( 0 ).getChild( 1 );
+
+				expect( domSelection.isCollapsed ).to.false;
+				expect( domSelection.rangeCount ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).startContainer ).to.equal( domB.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).startOffset ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).endContainer ).to.equal( domB.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).endOffset ).to.equal( 3 );
+
+				selectionCollapseSpy = sinon.spy( window.Selection.prototype, 'collapse' );
+				selectionExtendSpy = sinon.spy( window.Selection.prototype, 'extend' );
+
+				// <container:p>foo<attribute:b>b{ar</attribute:b>}baz</container:p>
+				selection.setRanges( [ new ViewRange( new ViewPosition( viewB.getChild( 0 ), 1 ), new ViewPosition( viewP.getChild( 2 ), 0 ) ) ] );
+
+				renderer.markToSync( 'children', viewP );
+				renderer.render();
+
+				expect( selectionCollapseSpy.notCalled ).to.true;
+				expect( selectionExtendSpy.notCalled ).to.true;
+			} );
+
+			it( 'should not render non-collapsed selection it is similar (element start - nested)', () => {
+				const domSelection = document.getSelection();
+
+				const { view: viewP, selection: newSelection } = parse(
+					'<container:p>foo<attribute:b><attribute:i>{ba}r</attribute:i></attribute:b></container:p>' );
+
+				viewRoot.appendChildren( viewP );
+				selection.setTo( newSelection );
+
+				renderer.markToSync( 'children', viewRoot );
+				renderer.render();
+
+				const domP = domRoot.childNodes[ 0 ];
+				const domB = domP.childNodes[ 1 ];
+				const viewI = viewRoot.getChild( 0 ).getChild( 1 ).getChild( 0 );
+
+				expect( domSelection.isCollapsed ).to.false;
+				expect( domSelection.rangeCount ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).startContainer ).to.equal( domB.childNodes[ 0 ].childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).startOffset ).to.equal( 0 );
+				expect( domSelection.getRangeAt( 0 ).endContainer ).to.equal( domB.childNodes[ 0 ].childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).endOffset ).to.equal( 2 );
+
+				selectionCollapseSpy = sinon.spy( window.Selection.prototype, 'collapse' );
+				selectionExtendSpy = sinon.spy( window.Selection.prototype, 'extend' );
+
+				// <container:p>foo{<attribute:b><attribute:i>ba}r</attribute:i></attribute:b></container:p>
+				selection.setRanges( [ new ViewRange( new ViewPosition( viewP.getChild( 0 ), 3 ), new ViewPosition( viewI.getChild( 0 ), 2 ) ) ] );
+
+				renderer.markToSync( 'children', viewP );
+				renderer.render();
+
+				expect( selectionCollapseSpy.notCalled ).to.true;
+				expect( selectionExtendSpy.notCalled ).to.true;
+			} );
+
+			it( 'should not render non-collapsed selection it is similar (element end - nested)', () => {
+				const domSelection = document.getSelection();
+
+				const { view: viewP, selection: newSelection } = parse(
+					'<container:p>f{oo<attribute:b><attribute:i>bar}</attribute:i></attribute:b>baz</container:p>' );
+
+				viewRoot.appendChildren( viewP );
+				selection.setTo( newSelection );
+
+				renderer.markToSync( 'children', viewRoot );
+				renderer.render();
+
+				const domP = domRoot.childNodes[ 0 ];
+				const domB = domP.childNodes[ 1 ];
+
+				expect( domSelection.isCollapsed ).to.false;
+				expect( domSelection.rangeCount ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).startContainer ).to.equal( domP.childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).startOffset ).to.equal( 1 );
+				expect( domSelection.getRangeAt( 0 ).endContainer ).to.equal( domB.childNodes[ 0 ].childNodes[ 0 ] );
+				expect( domSelection.getRangeAt( 0 ).endOffset ).to.equal( 3 );
+
+				selectionCollapseSpy = sinon.spy( window.Selection.prototype, 'collapse' );
+				selectionExtendSpy = sinon.spy( window.Selection.prototype, 'extend' );
+
+				// <container:p>f{oo<attribute:b><attribute:i>bar</attribute:i></attribute:b>}baz</container:p>
+				selection.setRanges( [ new ViewRange( new ViewPosition( viewP.getChild( 0 ), 1 ), new ViewPosition( viewP.getChild( 2 ), 0 ) ) ] );
+
+				renderer.markToSync( 'children', viewP );
+				renderer.render();
+
+				expect( selectionCollapseSpy.notCalled ).to.true;
+				expect( selectionExtendSpy.notCalled ).to.true;
+			} );
+		} );
 	} );
 } );