Sfoglia il codice sorgente

Tests: Refactoring in bindTwoStepCaretToAttribute tests.

Aleksander Nowodzinski 7 anni fa
parent
commit
e302ecde5b

+ 143 - 187
packages/ckeditor5-engine/tests/utils/bindtwostepcarettoattribute.js

@@ -8,8 +8,6 @@
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import DomEmitterMixin from '@ckeditor/ckeditor5-utils/src/dom/emittermixin';
 import bindTwoStepCaretToAttribute from '../../src/utils/bindtwostepcarettoattribute';
-import Position from '../../src/model/position';
-import Range from '../../src/model/range';
 import { upcastElementToAttribute } from '../../src/conversion/upcast-converters';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 
@@ -49,98 +47,62 @@ describe( 'bindTwoStepCaretToAttribute()', () => {
 		it( 'should "enter" the text with attribute in two steps', () => {
 			setData( model, '<$text c="true">foo[]</$text><$text a="true" b="true">bar</$text>' );
 
-			// Gravity is not overridden, caret is at the beginning of the text but is "outside" of the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'c' ] );
-			expect( selection.isGravityOverridden ).to.false;
-
-			// Press right key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Gravity is overridden, caret movement is blocked, selection at the beginning but "inside" the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'a', 'b' ] );
-			expect( selection.isGravityOverridden ).to.true;
-			sinon.assert.calledOnce( preventDefaultSpy );
-
-			// Press right key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Caret movement was not blocked this time (still once) so everything works normally.
-			sinon.assert.calledOnce( preventDefaultSpy );
+			testTwoStepCaretMovement( [
+				// Gravity is not overridden, caret is at the beginning of the text but is "outside" of the text.
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'→',
+				// Gravity is overridden, caret movement is blocked, selection at the beginning but "inside" the text.
+				{ selectionAttributes: [ 'a', 'b' ], isGravityOverridden: true, preventDefault: 1 },
+				'→',
+				// Caret movement was not blocked this time (still once) so everything works normally.
+				{ selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 1 },
+			] );
 		} );
 
 		it( 'should "leave" the text with attribute in two steps', () => {
 			setData( model, '<$text a="true" b="true">bar[]</$text><$text c="true">foo</$text>' );
 
-			// Gravity is not overridden, caret is at the end of the text but is "inside" of the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'a', 'b' ] );
-			expect( selection.isGravityOverridden ).to.false;
-
-			// Press right key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Gravity is overridden, caret movement is blocked, selection at the end but "outside" the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'c' ] );
-			expect( selection.isGravityOverridden ).to.true;
-			sinon.assert.calledOnce( preventDefaultSpy );
-
-			// Press right key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Caret movement was not blocked this time (still once) so everything works normally.
-			sinon.assert.calledOnce( preventDefaultSpy );
+			testTwoStepCaretMovement( [
+				// Gravity is not overridden, caret is at the end of the text but is "inside" of the text.
+				{ selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0 },
+				'→',
+				// Gravity is overridden, caret movement is blocked, selection at the end but "outside" the text.
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: true, preventDefault: 1 },
+				'→',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 1 },
+			] );
 		} );
 
 		it( 'should do nothing for not bound attribute (at the beginning)', () => {
 			setData( model, '[]<$text c="true">foo</$text>' );
 
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			sinon.assert.notCalled( preventDefaultSpy );
-			expect( selection.isGravityOverridden ).to.false;
+			testTwoStepCaretMovement( [
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'→',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+			] );
 		} );
 
 		it( 'should do nothing for not bound attribute (at the end)', () => {
 			setData( model, '<$text c="true">foo[]</$text>' );
 
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			sinon.assert.notCalled( preventDefaultSpy );
-			expect( selection.isGravityOverridden ).to.false;
+			testTwoStepCaretMovement( [
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'→',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+			] );
 		} );
 
 		it( 'should require two-steps movement when caret goes between text node with the same attribute but different value', () => {
 			setData( model, '<$text a="1">bar[]</$text><$text a="2">foo</$text>' );
 
-			// Gravity is not overridden.
-			expect( selection.isGravityOverridden ).to.false;
-
-			// Press right key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Gravity is overridden, caret movement is blocked.
-			expect( selection.isGravityOverridden ).to.true;
-			sinon.assert.calledOnce( preventDefaultSpy );
+			testTwoStepCaretMovement( [
+				{ selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0 },
+				'→',
+				{ selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 1 },
+				'→',
+				{ selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1 },
+			] );
 		} );
 	} );
 
@@ -148,144 +110,81 @@ describe( 'bindTwoStepCaretToAttribute()', () => {
 		it( 'should "enter" the text with attribute in two steps', () => {
 			setData( model, '<$text>foo</$text><$text a="true" b="true">bar</$text><$text c="true">b[]iz</$text>' );
 
-			// Gravity is not overridden, caret is a one character after the and of the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'c' ] );
-			expect( selection.isGravityOverridden ).to.false;
-
-			// Press left key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowleft,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Caret movement was not blocked.
-			sinon.assert.notCalled( preventDefaultSpy );
-
-			// So we need to move caret one character left like it should be done in the real world.
-			// Caret should ends up at the end of text with attribute but still outside of it.
-			model.change( writer => writer.setSelection( new Range( new Position( model.document.getRoot(), [ 6 ] ) ) ) );
-
-			// Gravity is overridden.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'c' ] );
-			expect( selection.isGravityOverridden ).to.true;
-
-			// Press left key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowleft,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Caret movement was blocked but now is "inside" the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'a', 'b' ] );
-			expect( selection.isGravityOverridden ).to.false;
-			sinon.assert.calledOnce( preventDefaultSpy );
-
-			// Press left key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowleft,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Caret movement was not blocked this time (still once) so everything works normally.
-			sinon.assert.calledOnce( preventDefaultSpy );
-
-			// And again we need to move the caret like it should be done in the real world to be shure that everything is
-			// like it should to be.
-			model.change( writer => writer.setSelection( new Range( new Position( model.document.getRoot(), [ 5 ] ) ) ) );
+			testTwoStepCaretMovement( [
+				// Gravity is not overridden, caret is a one character after the and of the text.
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				// Caret movement was not blocked but the gravity is overridden.
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: true, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 1 },
+				'←',
+				{ selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 1 },
+			] );
 		} );
 
 		it( 'should "leave" the text with attribute in two steps', () => {
 			setData( model, '<$text c="true">foo</$text><$text a="true" b="true">b[]ar</$text>' );
 
-			// Gravity is not overridden, caret is a one character after the beginning of the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'a', 'b' ] );
-			expect( selection.isGravityOverridden ).to.false;
-
-			// Press left key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowleft,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Caret movement was not blocked.
-			sinon.assert.notCalled( preventDefaultSpy );
-
-			// So we need to move caret one character left like it should be done in the real world.
-			// Caret should ends up at the beginning of text with attribute but still inside of it.
-			model.change( writer => writer.setSelection( new Range( new Position( model.document.getRoot(), [ 3 ] ) ) ) );
-
-			// Gravity is overridden, caret is at the beginning of the text and is "inside" of the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'a', 'b' ] );
-			expect( selection.isGravityOverridden ).to.true;
-
-			// Press left key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowleft,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Gravity is not overridden, caret movement was blocked but now is "outside" the text.
-			expect( Array.from( selection.getAttributeKeys() ) ).to.have.members( [ 'c' ] );
-			expect( selection.isGravityOverridden ).to.false;
-			sinon.assert.calledOnce( preventDefaultSpy );
-
-			// Press left key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowleft,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Caret movement was not blocked this time (still once) so everything works normally.
-			sinon.assert.calledOnce( preventDefaultSpy );
+			testTwoStepCaretMovement( [
+				// Gravity is not overridden, caret is a one character after the beginning of the text.
+				{ selectionAttributes: [ 'a', 'b' ], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				// Caret movement was not blocked.
+				{ selectionAttributes: [ 'a', 'b' ], isGravityOverridden: true, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 1 },
+				'←',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 1 }
+			] );
 		} );
 
 		it( 'should do nothing for not bound attribute (at the beginning)', () => {
 			setData( model, '<$text c="true">[]foo</$text>' );
 
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			sinon.assert.notCalled( preventDefaultSpy );
-			expect( selection.isGravityOverridden ).to.false;
+			testTwoStepCaretMovement( [
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 }
+			] );
 		} );
 
 		it( 'should do nothing for not bound attribute (at the end)', () => {
 			setData( model, '<$text c="true">foo</$text>[]' );
 
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowright,
-				preventDefault: preventDefaultSpy
-			} );
-
-			sinon.assert.notCalled( preventDefaultSpy );
-			expect( selection.isGravityOverridden ).to.false;
+			testTwoStepCaretMovement( [
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'c' ], isGravityOverridden: false, preventDefault: 0 }
+			] );
 		} );
 
 		it( 'should do nothing when caret is at the beginning of block element', () => {
 			setData( model, '[]foo', { lastRangeBackward: true } );
 
-			expect( () => {
-				fireKeyDownEvent( { keyCode: keyCodes.arrowleft } );
-			} ).to.not.throw();
+			testTwoStepCaretMovement( [
+				{ selectionAttributes: [], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [], isGravityOverridden: false, preventDefault: 0 },
+			] );
 		} );
 
 		it( 'should require two-steps movement when caret goes between text node with the same attribute but different value', () => {
 			setData( model, '<$text a="2">foo</$text><$text a="1">b[]ar</$text>' );
 
-			// Gravity is not overridden.
-			expect( selection.isGravityOverridden ).to.false;
-
-			// Press left key.
-			fireKeyDownEvent( {
-				keyCode: keyCodes.arrowleft,
-				preventDefault: preventDefaultSpy
-			} );
-
-			// Gravity is overridden, caret movement was not blocked.
-			sinon.assert.notCalled( preventDefaultSpy );
-			expect( selection.isGravityOverridden ).to.true;
+			testTwoStepCaretMovement( [
+				{ selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'a' ], isGravityOverridden: true, preventDefault: 0 },
+				'←',
+				{ selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1 },
+				'←',
+				{ selectionAttributes: [ 'a' ], isGravityOverridden: false, preventDefault: 1 }
+			] );
 		} );
 	} );
 
@@ -352,9 +251,66 @@ describe( 'bindTwoStepCaretToAttribute()', () => {
 		expect( selection.isGravityOverridden ).to.false;
 	} );
 
+	const keyMap = {
+		'→': 'arrowright',
+		'←': 'arrowleft'
+	};
+
 	function fireKeyDownEvent( options ) {
 		const eventData = Object.assign( { domTarget: document.body }, options );
 
 		viewDoc.fire( 'keydown', eventData );
 	}
+
+	function getSelectionAttributesArray( selection ) {
+		return Array.from( selection.getAttributeKeys() );
+	}
+
+	function testTwoStepCaretMovement( scenario ) {
+		for ( const step of scenario ) {
+			if ( typeof step == 'string' ) {
+				let preventDefaultCalled;
+
+				fireKeyDownEvent( {
+					keyCode: keyCodes[ keyMap[ step ] ],
+					preventDefault: () => {
+						preventDefaultSpy();
+
+						preventDefaultCalled = true;
+					}
+				} );
+
+				if ( !preventDefaultCalled ) {
+					const position = selection.getFirstPosition();
+					let shift;
+
+					if ( step == '→' ) {
+						if ( position.isAtEnd ) {
+							return;
+						}
+
+						shift = 1;
+					} else if ( step == '←' ) {
+						if ( position.isAtStart ) {
+							return;
+						}
+
+						shift = -1;
+					}
+
+					model.change( writer => {
+						writer.setSelection( selection.getFirstPosition().getShiftedBy( shift ) );
+					} );
+				}
+			} else {
+				const stepIndex = scenario.indexOf( step );
+				const stepString = `in step #${ stepIndex } (${ JSON.stringify( step ) })`;
+
+				expect( getSelectionAttributesArray( selection ) ).to.have.members( step.selectionAttributes, '#attributes ' + stepString );
+				expect( selection.isGravityOverridden ).to.equal( step.isGravityOverridden, '#isGravityOverridden ' + stepString );
+				expect( preventDefaultSpy.callCount ).to.equal( step.preventDefault, '#preventDefault ' + stepString );
+			}
+		}
+	}
 } );
+