8
0
Prechádzať zdrojové kódy

Merge pull request #1070 from ckeditor/t/1069

Tests: Added additional tests for jumping over UI element. Closes #1069.
Piotrek Koszuliński 8 rokov pred
rodič
commit
302acead81

+ 148 - 81
packages/ckeditor5-engine/tests/view/document/jumpoveruielement.js

@@ -8,10 +8,10 @@
 import ViewDocument from '../../../src/view/document';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
-import { setData } from '../../../src/dev-utils/view';
+import { setData as setViewData } from '../../../src/dev-utils/view';
 
 describe( 'Document', () => {
-	let viewDocument, domRoot;
+	let viewDocument, domRoot, domSelection;
 
 	beforeEach( () => {
 		domRoot = createElement( document, 'div', {
@@ -22,7 +22,8 @@ describe( 'Document', () => {
 		viewDocument = new ViewDocument();
 		viewDocument.createRoot( domRoot );
 
-		document.getSelection().removeAllRanges();
+		domSelection = document.getSelection();
+		domSelection.removeAllRanges();
 
 		viewDocument.isFocused = true;
 	} );
@@ -33,92 +34,158 @@ describe( 'Document', () => {
 		domRoot.parentElement.removeChild( domRoot );
 	} );
 
-	describe( 'jump over ui element handler', () => {
-		it( 'jump over ui element when right arrow is pressed before ui element', () => {
-			setData( viewDocument, '<container:p>foo{}<ui:span></ui:span>bar</container:p>' );
-			viewDocument.render();
-
-			viewDocument.fire( 'keydown', { keyCode: keyCodes.arrowright, domTarget: viewDocument.domRoots.get( 'main' ) } );
-
-			const domSelection = document.getSelection();
-
-			expect( domSelection.anchorNode.nodeName.toUpperCase() ).to.equal( 'P' );
-			expect( domSelection.anchorOffset ).to.equal( 2 );
-			expect( domSelection.isCollapsed ).to.be.true;
-		} );
-
-		it( 'should do nothing when another key is pressed', () => {
-			setData( viewDocument, '<container:p>foo<ui:span></ui:span>{}bar</container:p>' );
-			viewDocument.render();
+	function prepare( view, options ) {
+		setViewData( viewDocument, view );
+		viewDocument.render();
 
-			viewDocument.fire( 'keydown', { keyCode: keyCodes.arrowleft, domTarget: viewDocument.domRoots.get( 'main' ) } );
+		const eventData = Object.assign( { keyCode: keyCodes.arrowright, domTarget: viewDocument.domRoots.get( 'main' ) }, options );
+		viewDocument.fire( 'keydown', eventData );
+	}
 
-			const domSelection = document.getSelection();
-
-			expect( domSelection.anchorNode.data ).to.equal( 'bar' );
-			expect( domSelection.anchorOffset ).to.equal( 0 );
-			expect( domSelection.isCollapsed ).to.be.true;
-		} );
-
-		it( 'should do nothing if range is not collapsed', () => {
-			setData( viewDocument, '<container:p>f{oo}<ui:span></ui:span>bar</container:p>' );
-			viewDocument.render();
+	function check( anchorNode, anchorOffset, focusNode, focusOffset ) {
+		const anchor = domSelection.anchorNode.data ? domSelection.anchorNode.data : domSelection.anchorNode.nodeName.toUpperCase();
 
-			viewDocument.fire( 'keydown', { keyCode: keyCodes.arrowright, domTarget: viewDocument.domRoots.get( 'main' ) } );
-
-			const domSelection = document.getSelection();
+		expect( anchor, 'anchorNode' ).to.equal( anchorNode );
+		expect( domSelection.anchorOffset, 'anchorOffset' ).to.equal( anchorOffset );
 
-			expect( domSelection.anchorNode.data ).to.equal( 'foo' );
-			expect( domSelection.anchorOffset ).to.equal( 1 );
-			expect( domSelection.focusNode.data ).to.equal( 'foo' );
-			expect( domSelection.focusOffset ).to.equal( 3 );
-			expect( domSelection.isCollapsed ).to.be.false;
-		} );
+		if ( focusNode ) {
+			const focus = domSelection.focusNode.data ? domSelection.focusNode.data : domSelection.focusNode.nodeName.toUpperCase();
 
-		it( 'jump over ui element if selection is not collapsed but shift key is pressed', () => {
-			setData( viewDocument, '<container:p>fo{o}<ui:span></ui:span>bar</container:p>' );
-			viewDocument.render();
+			expect( focus, 'focusNode' ).to.equal( focusNode );
+			expect( domSelection.focusOffset, 'focusOffset' ).to.equal( focusOffset );
+		} else {
+			expect( domSelection.isCollapsed, 'isCollapsed' ).to.be.true;
+		}
+	}
 
-			viewDocument.fire(
-				'keydown',
-				{ keyCode: keyCodes.arrowright, shiftKey: true, domTarget: viewDocument.domRoots.get( 'main' ) }
-			);
-
-			const domSelection = document.getSelection();
-
-			expect( domSelection.anchorNode.nodeName.toUpperCase() ).to.equal( '#TEXT' );
-			expect( domSelection.anchorOffset ).to.equal( 2 );
-			expect( domSelection.focusNode.nodeName.toUpperCase() ).to.equal( 'P' );
-			expect( domSelection.focusOffset ).to.equal( 2 );
-		} );
-
-		it( 'jump over ui element if selection is in attribute element', () => {
-			setData( viewDocument, '<container:p><attribute:b>foo{}</attribute:b><ui:span></ui:span>bar</container:p>' );
-			viewDocument.render();
-
-			viewDocument.fire(
-				'keydown',
-				{ keyCode: keyCodes.arrowright, shiftKey: true, domTarget: viewDocument.domRoots.get( 'main' ) }
-			);
-
-			const domSelection = document.getSelection();
-
-			expect( domSelection.anchorNode.nodeName.toUpperCase() ).to.equal( 'P' );
-			expect( domSelection.anchorOffset ).to.equal( 2 );
-			expect( domSelection.isCollapsed ).to.be.true;
+	describe( 'jump over ui element handler', () => {
+		describe( 'collapsed selection', () => {
+			it( 'do nothing when another key is pressed', () => {
+				prepare( '<container:p>foo<ui:span></ui:span>{}bar</container:p>', { keyCode: keyCodes.arrowleft } );
+				check( 'bar', 0 );
+			} );
+
+			it( 'jump over ui element when right arrow is pressed before ui element - directly before ui element', () => {
+				prepare( '<container:p>foo[]<ui:span></ui:span>bar</container:p>' );
+				check( 'P', 2 );
+			} );
+
+			it( 'jump over ui element when right arrow is pressed before ui element - not directly before ui element', () => {
+				prepare( '<container:p>foo{}<ui:span></ui:span>bar</container:p>' );
+				check( 'P', 2 );
+			} );
+
+			it( 'jump over multiple ui elements when right arrow is pressed before ui element', () => {
+				prepare( '<container:p>foo{}<ui:span></ui:span><ui:span></ui:span>bar</container:p>' );
+				check( 'P', 3 );
+			} );
+
+			it( 'jump over ui elements at the end of container element', () => {
+				prepare( '<container:p>foo{}<ui:span></ui:span><ui:span></ui:span></container:p><container:div></container:div>' );
+				check( 'P', 3 );
+			} );
+
+			it( 'jump over ui element if selection is in attribute element - case 1', () => {
+				prepare( '<container:p><attribute:b>foo{}</attribute:b><ui:span></ui:span>bar</container:p>' );
+				check( 'P', 2 );
+			} );
+
+			it( 'jump over ui element if selection is in attribute element - case 2', () => {
+				prepare( '<container:p><attribute:b>foo{}</attribute:b><ui:span></ui:span>bar</container:p>' );
+				check( 'P', 2 );
+			} );
+
+			it( 'jump over ui element if selection is in multiple attribute elements', () => {
+				prepare( '<container:p><attribute:i><attribute:b>foo{}</attribute:b></attribute:i><ui:span></ui:span>bar</container:p>' );
+				check( 'P', 2 );
+			} );
+
+			it( 'jump over empty attribute elements and ui elements', () => {
+				prepare(
+					'<container:p>' +
+						'foo{}<attribute:b></attribute:b><ui:span></ui:span><ui:span></ui:span><attribute:b></attribute:b>bar' +
+					'</container:p>'
+				);
+
+				check( 'P', 5 );
+			} );
+
+			it( 'jump over empty attribute elements and ui elements if shift key is pressed', () => {
+				prepare(
+					'<container:p>' +
+						'foo{}<attribute:b></attribute:b><ui:span></ui:span><ui:span></ui:span><attribute:b></attribute:b>bar' +
+					'</container:p>',
+					{ shiftKey: true }
+				);
+
+				check( 'P', 5 );
+			} );
+
+			it( 'do nothing if selection is not directly before ui element', () => {
+				prepare( '<container:p>fo{}o<ui:span></ui:span>bar</container:p>' );
+				check( 'foo', 2 );
+			} );
+
+			it( 'do nothing if selection is in attribute element but not before ui element', () => {
+				prepare( '<container:p><attribute:b>foo{}</attribute:b>bar</container:p>' );
+				check( 'foo', 3 );
+			} );
+
+			it( 'do nothing if selection is before non-empty attribute element', () => {
+				prepare( '<container:p>fo{}<attribute:b>o</attribute:b><ui:span></ui:span>bar</container:p>' );
+				check( 'fo', 2 );
+			} );
+
+			it( 'do nothing if selection is before container element - case 1', () => {
+				prepare( '<container:p>foo{}</container:p><ui:span></ui:span><container:div>bar</container:div>' );
+				check( 'foo', 3 );
+			} );
+
+			it( 'do nothing if selection is before container element - case 2', () => {
+				prepare( '<container:div>foo{}<container:p></container:p><ui:span></ui:span></container:div>' );
+				check( 'foo', 3 );
+			} );
+
+			it( 'do nothing if selection is at the end of last container element', () => {
+				prepare( '<container:p>foo{}</container:p>' );
+				check( 'foo', 3 );
+			} );
 		} );
 
-		it( 'should do nothing if caret is not directly before ui element', () => {
-			setData( viewDocument, '<container:p>fo{}o<ui:span></ui:span>bar</container:p>' );
-			viewDocument.render();
-
-			viewDocument.fire( 'keydown', { keyCode: keyCodes.arrowright, domTarget: viewDocument.domRoots.get( 'main' ) } );
-
-			const domSelection = document.getSelection();
-
-			expect( domSelection.anchorNode.data ).to.equal( 'foo' );
-			expect( domSelection.anchorOffset ).to.equal( 2 );
-			expect( domSelection.isCollapsed ).to.be.true;
+		describe( 'non-collapsed selection', () => {
+			it( 'should do nothing', () => {
+				prepare( '<container:p>f{oo}<ui:span></ui:span>bar</container:p>' );
+				check( 'foo', 1, 'foo', 3 );
+			} );
+
+			it( 'should do nothing if selection is not before ui element - shift key pressed', () => {
+				prepare( '<container:p>f{o}o<ui:span></ui:span>bar</container:p>', { shiftKey: true } );
+				check( 'foo', 1, 'foo', 2 );
+			} );
+
+			it( 'jump over ui element if shift key is pressed', () => {
+				prepare( '<container:p>fo{o}<ui:span></ui:span>bar</container:p>', { shiftKey: true } );
+				check( 'foo', 2, 'P', 2 );
+			} );
+
+			it( 'jump over ui element if selection is in multiple attribute elements', () => {
+				prepare(
+					'<container:p><attribute:i><attribute:b>fo{o}</attribute:b></attribute:i><ui:span></ui:span>bar</container:p>',
+					{ shiftKey: true }
+				);
+				check( 'foo', 2, 'P', 2 );
+			} );
+
+			it( 'jump over empty attribute elements and ui elements if shift key is pressed', () => {
+				prepare(
+					'<container:p>' +
+						'fo{o}<attribute:b></attribute:b><ui:span></ui:span><ui:span></ui:span><attribute:b></attribute:b>bar' +
+					'</container:p>',
+					{ shiftKey: true }
+				);
+
+				check( 'foo', 2, 'P', 5 );
+			} );
 		} );
 
 		it( 'should do nothing if dom position cannot be converted to view position', () => {

+ 27 - 2
packages/ckeditor5-engine/tests/view/manual/uielement.js

@@ -14,8 +14,10 @@ import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
 import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
 import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import UIElement from '../../../src/view/uielement';
+import Position from '../../../src/view/position';
+import writer from '../../../src/view/writer';
 
-class MyUIElement extends UIElement {
+class EndingUIElement extends UIElement {
 	render( domDocument ) {
 		const root = super.render( domDocument );
 
@@ -26,6 +28,17 @@ class MyUIElement extends UIElement {
 	}
 }
 
+class MiddleUIElement extends UIElement {
+	render( domDocument ) {
+		const root = super.render( domDocument );
+
+		root.classList.add( 'ui-element' );
+		root.innerHTML = 'X';
+
+		return root;
+	}
+}
+
 class UIElementTestPlugin extends Plugin {
 	init() {
 		const editor = this.editor;
@@ -34,7 +47,7 @@ class UIElementTestPlugin extends Plugin {
 		// Add some UIElement to each paragraph.
 		editing.modelToView.on( 'insert:paragraph', ( evt, data, consumable, conversionApi ) => {
 			const viewP = conversionApi.mapper.toViewElement( data.item );
-			viewP.appendChildren( new MyUIElement( 'span' ) );
+			viewP.appendChildren( new EndingUIElement( 'span' ) );
 		}, { priority: 'lowest' } );
 	}
 }
@@ -46,6 +59,18 @@ ClassicEditor
 	} )
 	.then( editor => {
 		window.editor = editor;
+
+		// Add some UI elements.
+		const viewRoot = editor.editing.view.getRoot();
+		const viewText1 = viewRoot.getChild( 0 ).getChild( 0 );
+		const viewText2 = viewRoot.getChild( 1 ).getChild( 0 );
+
+		writer.insert( new Position( viewText1, 20 ), new MiddleUIElement( 'span' ) );
+		writer.insert( new Position( viewText1, 20 ), new MiddleUIElement( 'span' ) );
+		writer.insert( new Position( viewText2, 0 ), new MiddleUIElement( 'span' ) );
+		writer.insert( new Position( viewText2, 6 ), new MiddleUIElement( 'span' ) );
+
+		editor.editing.view.render();
 	} )
 	.catch( err => {
 		console.error( err.stack );