Browse Source

Added tests to arrow keys handling, updated docs.

Szymon Kupś 9 years ago
parent
commit
703a766da0
2 changed files with 546 additions and 286 deletions
  1. 17 22
      packages/ckeditor5-image/src/widget/widget.js
  2. 529 264
      packages/ckeditor5-image/tests/widget/widget.js

+ 17 - 22
packages/ckeditor5-image/src/widget/widget.js

@@ -156,9 +156,9 @@ export default class Widget extends Plugin {
 				modelDocument.enqueueChanges( () => {
 					modelSelection.setRanges( [ newRange ] );
 				} );
-			}
 
-			return true;
+				return true;
+			}
 		}
 
 		// If selection is next to object element.
@@ -197,7 +197,7 @@ export default class Widget extends Plugin {
 	 * @param {Boolean} forward Direction of checking.
 	 * @returns {module:engine/model/element~Element|null}
 	 */
-	_getObjectElementNextToSelection( forward = true ) {
+	_getObjectElementNextToSelection( forward ) {
 		const modelDocument = this.editor.document;
 		const schema = modelDocument.schema;
 		const modelSelection = modelDocument.selection;
@@ -217,13 +217,12 @@ export default class Widget extends Plugin {
 	}
 }
 
-/**
- * Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only
- * one range in the selection, and that range contains exactly one element.
- * Returns `null` if there is no selected element.
- *
- * @returns {module:engine/model/element~Element|null}
- */
+// Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only
+// one range in the selection, and that range contains exactly one element.
+// Returns `null` if there is no selected element.
+//
+// @param {module:engine/model/selection~Selection} modelSelection
+// @returns {module:engine/model/element~Element|null}
 function getSelectedElement( modelSelection ) {
 	if ( modelSelection.rangeCount !== 1 ) {
 		return null;
@@ -236,12 +235,10 @@ function getSelectedElement( modelSelection ) {
 	return ( nodeAfterStart instanceof ModelElement && nodeAfterStart == nodeBeforeEnd ) ? nodeAfterStart : null;
 }
 
-/**
- * Returns 'true' if provided key code represents one of the arrow keys.
- *
- * @param {Number} keyCode
- * @return {Boolean}
- */
+// Returns 'true' if provided key code represents one of the arrow keys.
+//
+// @param {Number} keyCode
+// @returns {Boolean}
 function isArrowKeyCode( keyCode ) {
 	return keyCode == keyCodes.arrowright ||
 		keyCode == keyCodes.arrowleft ||
@@ -249,12 +246,10 @@ function isArrowKeyCode( keyCode ) {
 		keyCode == keyCodes.arrowdown;
 }
 
-/**
- * Returns 'true' if provided key code represents one of the delete keys: delete or backspace.
- *
- * @param {Number} keyCode
- * @return {Boolean}
- */
+//Returns 'true' if provided key code represents one of the delete keys: delete or backspace.
+//
+//@param {Number} keyCode
+//@returns {Boolean}
 function isDeleteKeyCode( keyCode ) {
 	return keyCode == keyCodes.delete || keyCode == keyCodes.backspace;
 }

+ 529 - 264
packages/ckeditor5-image/tests/widget/widget.js

@@ -14,8 +14,10 @@ import AttributeContainer from 'ckeditor5/engine/view/attributeelement.js';
 import { setData as setModelData, getData as getModelData } from 'ckeditor5/engine/dev-utils/model.js';
 import { keyCodes } from 'ckeditor5/utils/keyboard.js';
 
+/* global document */
+
 describe( 'Widget', () => {
-	let editor, document, viewDocument;
+	let editor, doc, viewDocument;
 
 	beforeEach( () => {
 		return VirtualTestEditor.create( {
@@ -23,14 +25,14 @@ describe( 'Widget', () => {
 		} )
 			.then( newEditor => {
 				editor = newEditor;
-				document = editor.document;
+				doc = editor.document;
 				viewDocument = editor.editing.view;
 
-				document.schema.registerItem( 'widget', '$block' );
-				document.schema.objects.add( 'widget' );
-				document.schema.registerItem( 'paragraph', '$block' );
-				document.schema.registerItem( 'inline', '$inline' );
-				document.schema.objects.add( 'inline' );
+				doc.schema.registerItem( 'widget', '$block' );
+				doc.schema.objects.add( 'widget' );
+				doc.schema.registerItem( 'paragraph', '$block' );
+				doc.schema.registerItem( 'inline', '$inline' );
+				doc.schema.objects.add( 'inline' );
 
 				buildModelConverter().for( editor.editing.modelToView )
 					.fromElement( 'paragraph' )
@@ -60,7 +62,7 @@ describe( 'Widget', () => {
 	} );
 
 	it( 'should create selection over clicked widget', () => {
-		setModelData( document, '[]<widget></widget>' );
+		setModelData( doc, '[]<widget></widget>' );
 		const viewDiv = viewDocument.getRoot().getChild( 0 );
 		const domEventDataMock = {
 			target: viewDiv,
@@ -69,12 +71,12 @@ describe( 'Widget', () => {
 
 		viewDocument.fire( 'mousedown', domEventDataMock );
 
-		expect( getModelData( document ) ).to.equal( '[<widget></widget>]' );
+		expect( getModelData( doc ) ).to.equal( '[<widget></widget>]' );
 		sinon.assert.calledOnce( domEventDataMock.preventDefault );
 	} );
 
 	it( 'should create selection when clicked in nested element', () => {
-		setModelData( document, '[]<widget></widget>' );
+		setModelData( doc, '[]<widget></widget>' );
 		const viewDiv = viewDocument.getRoot().getChild( 0 );
 		const viewB = viewDiv.getChild( 0 );
 		const domEventDataMock = {
@@ -84,12 +86,12 @@ describe( 'Widget', () => {
 
 		viewDocument.fire( 'mousedown', domEventDataMock );
 
-		expect( getModelData( document ) ).to.equal( '[<widget></widget>]' );
+		expect( getModelData( doc ) ).to.equal( '[<widget></widget>]' );
 		sinon.assert.calledOnce( domEventDataMock.preventDefault );
 	} );
 
 	it( 'should do nothing if clicked in non-widget element', () => {
-		setModelData( document, '<paragraph>[]foo bar</paragraph><widget></widget>' );
+		setModelData( doc, '<paragraph>[]foo bar</paragraph><widget></widget>' );
 		const viewP = viewDocument.getRoot().getChild( 0 );
 		const domEventDataMock = {
 			target: viewP,
@@ -99,12 +101,12 @@ describe( 'Widget', () => {
 		viewDocument.focus();
 		viewDocument.fire( 'mousedown', domEventDataMock );
 
-		expect( getModelData( document ) ).to.equal( '<paragraph>[]foo bar</paragraph><widget></widget>' );
+		expect( getModelData( doc ) ).to.equal( '<paragraph>[]foo bar</paragraph><widget></widget>' );
 		sinon.assert.notCalled( domEventDataMock.preventDefault );
 	} );
 
 	it( 'should not focus editable if already is focused', () => {
-		setModelData( document, '<widget></widget>' );
+		setModelData( doc, '<widget></widget>' );
 		const widget = viewDocument.getRoot().getChild( 0 );
 		const domEventDataMock = {
 			target: widget,
@@ -117,255 +119,518 @@ describe( 'Widget', () => {
 
 		sinon.assert.calledOnce( domEventDataMock.preventDefault );
 		sinon.assert.notCalled( focusSpy );
-		expect( getModelData( document ) ).to.equal( '[<widget></widget>]' );
+		expect( getModelData( doc ) ).to.equal( '[<widget></widget>]' );
 	} );
 
-	describe( 'delete and backspace handling', () => {
-		test(
-			'should select widget when backspace is pressed',
-			'<widget></widget><paragraph>[]foo</paragraph>',
-			keyCodes.backspace,
-			'[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should remove empty element after selecting widget when backspace is pressed',
-			'<widget></widget><paragraph>[]</paragraph>',
-			keyCodes.backspace,
-			'[<widget></widget>]'
-		);
-
-		test(
-			'should select widget when delete is pressed',
-			'<paragraph>foo[]</paragraph><widget></widget>',
-			keyCodes.delete,
-			'<paragraph>foo</paragraph>[<widget></widget>]'
-		);
-
-		test(
-			'should remove empty element after selecting widget when delete is pressed',
-			'<paragraph>[]</paragraph><widget></widget>',
-			keyCodes.delete,
-			'[<widget></widget>]'
-		);
-
-		test(
-			'should not respond to other keys',
-			'<widget></widget><paragraph>[]foo</paragraph>',
-			65,
-			'<widget></widget><paragraph>[]foo</paragraph>'
-		);
-
-		test(
-			'should do nothing on non-collapsed selection',
-			'<widget></widget><paragraph>[f]oo</paragraph>',
-			keyCodes.backspace,
-			'<widget></widget><paragraph>[f]oo</paragraph>'
-		);
-
-		test(
-			'should do nothing on non-object elements',
-			'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>',
-			keyCodes.backspace,
-			'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>'
-		);
-
-		test(
-			'should work correctly with modifier key: backspace + ctrl',
-			'<widget></widget><paragraph>[]foo</paragraph>',
-			{ keyCode: keyCodes.backspace, ctrlKey: true, preventDefault: () => {} },
-			'[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should work correctly with modifier key: backspace + alt',
-			'<widget></widget><paragraph>[]foo</paragraph>',
-			{ keyCode: keyCodes.backspace, altKey: true, preventDefault: () => {} },
-			'[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should work correctly with modifier key: backspace + shift',
-			'<widget></widget><paragraph>[]foo</paragraph>',
-			{ keyCode: keyCodes.backspace, shiftKey: true, preventDefault: () => {} },
-			'[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should work correctly with modifier key: delete + ctrl',
-			'<paragraph>foo[]</paragraph><widget></widget>',
-			{ keyCode: keyCodes.delete, ctrlKey: true, preventDefault: () => {} },
-			'<paragraph>foo</paragraph>[<widget></widget>]'
-		);
-
-		test(
-			'should work correctly with modifier key: delete + alt',
-			'<paragraph>foo[]</paragraph><widget></widget>',
-			{ keyCode: keyCodes.delete, altKey: true, preventDefault: () => {} },
-			'<paragraph>foo</paragraph>[<widget></widget>]'
-		);
-
-		test(
-			'should work correctly with modifier key: delete + shift',
-			'<paragraph>foo[]</paragraph><widget></widget>',
-			{ keyCode: keyCodes.delete, shiftKey: true, preventDefault: () => {} },
-			'<paragraph>foo</paragraph>[<widget></widget>]'
-		);
-
-		test(
-			'should not modify backspace default behaviour in single paragraph boundaries',
-			'<paragraph>[]foo</paragraph>',
-			keyCodes.backspace,
-			'<paragraph>[]foo</paragraph>'
-		);
-
-		test(
-			'should not modify delete default behaviour in single paragraph boundaries',
-			'<paragraph>foo[]</paragraph>',
-			keyCodes.delete,
-			'<paragraph>foo[]</paragraph>'
-		);
-
-		test(
-			'should do nothing on selected widget preceded by a paragraph - backspace',
-			'<paragraph>foo</paragraph>[<widget></widget>]',
-			keyCodes.backspace,
-			'<paragraph>foo</paragraph>[<widget></widget>]'
-		);
-
-		test(
-			'should do nothing on selected widget preceded by another widget - backspace',
-			'<widget></widget>[<widget></widget>]',
-			keyCodes.backspace,
-			'<widget></widget>[<widget></widget>]'
-		);
-
-		test(
-			'should do nothing on selected widget before paragraph - backspace',
-			'[<widget></widget>]<paragraph>foo</paragraph>',
-			keyCodes.backspace,
-			'[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should do nothing on selected widget before another widget - backspace',
-			'[<widget></widget>]<widget></widget>',
-			keyCodes.backspace,
-			'[<widget></widget>]<widget></widget>'
-		);
-
-		test(
-			'should do nothing on selected widget between paragraphs - backspace',
-			'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>',
-			keyCodes.backspace,
-			'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should do nothing on selected widget between other widgets - backspace',
-			'<widget></widget>[<widget></widget>]<widget></widget>',
-			keyCodes.backspace,
-			'<widget></widget>[<widget></widget>]<widget></widget>'
-		);
-
-		test(
-			'should do nothing on selected widget preceded by a paragraph - delete',
-			'<paragraph>foo</paragraph>[<widget></widget>]',
-			keyCodes.delete,
-			'<paragraph>foo</paragraph>[<widget></widget>]'
-		);
-
-		test(
-			'should do nothing on selected widget preceded by another widget - delete',
-			'<widget></widget>[<widget></widget>]',
-			keyCodes.delete,
-			'<widget></widget>[<widget></widget>]'
-		);
-
-		test(
-			'should do nothing on selected widget before paragraph - delete',
-			'[<widget></widget>]<paragraph>foo</paragraph>',
-			keyCodes.delete,
-			'[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should do nothing on selected widget before another widget - delete',
-			'[<widget></widget>]<widget></widget>',
-			keyCodes.delete,
-			'[<widget></widget>]<widget></widget>'
-		);
-
-		test(
-			'should do nothing on selected widget between paragraphs - delete',
-			'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>',
-			keyCodes.delete,
-			'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>'
-		);
-
-		test(
-			'should do nothing on selected widget between other widgets - delete',
-			'<widget></widget>[<widget></widget>]<widget></widget>',
-			keyCodes.delete,
-			'<widget></widget>[<widget></widget>]<widget></widget>'
-		);
-
-		test(
-			'should select inline objects - backspace',
-			'<paragraph>foo<inline></inline>[]bar</paragraph>',
-			keyCodes.backspace,
-			'<paragraph>foo[<inline></inline>]bar</paragraph>'
-		);
-
-		test(
-			'should select inline objects - delete',
-			'<paragraph>foo[]<inline></inline>bar</paragraph>',
-			keyCodes.delete,
-			'<paragraph>foo[<inline></inline>]bar</paragraph>'
-		);
-
-		test(
-			'should do nothing on selected inline objects - backspace',
-			'<paragraph>foo[<inline></inline>]bar</paragraph>',
-			keyCodes.backspace,
-			'<paragraph>foo[<inline></inline>]bar</paragraph>'
-		);
-
-		test(
-			'should do nothing on selected inline objects - delete',
-			'<paragraph>foo[<inline></inline>]bar</paragraph>',
-			keyCodes.delete,
-			'<paragraph>foo[<inline></inline>]bar</paragraph>'
-		);
-
-		test(
-			'should do nothing if selection is placed after first letter - backspace',
-			'<paragraph>a[]</paragraph>',
-			keyCodes.backspace,
-			'<paragraph>a[]</paragraph>'
-		);
-
-		test(
-			'should do nothing if selection is placed before first letter - delete',
-			'<paragraph>[]a</paragraph>',
-			keyCodes.delete,
-			'<paragraph>[]a</paragraph>'
-		);
-
-		it( 'should prevent default behaviour and stop event propagation', () => {
-			const keydownHandler = sinon.spy();
-			const domEventDataMock = {
-				keyCode: keyCodes.delete,
-				preventDefault: sinon.spy(),
-			};
-			setModelData( document, '<paragraph>foo[]</paragraph><widget></widget>' );
-			viewDocument.on( 'keydown',  keydownHandler );
-
-			viewDocument.fire( 'keydown', domEventDataMock );
-
-			expect( getModelData( document ) ).to.equal( '<paragraph>foo</paragraph>[<widget></widget>]' );
-			sinon.assert.calledOnce( domEventDataMock.preventDefault );
-			sinon.assert.notCalled( keydownHandler );
+	describe( 'keys handling', () => {
+		describe( 'delete and backspace', () => {
+			test(
+				'should select widget when backspace is pressed',
+				'<widget></widget><paragraph>[]foo</paragraph>',
+				keyCodes.backspace,
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should remove empty element after selecting widget when backspace is pressed',
+				'<widget></widget><paragraph>[]</paragraph>',
+				keyCodes.backspace,
+				'[<widget></widget>]'
+			);
+
+			test(
+				'should select widget when delete is pressed',
+				'<paragraph>foo[]</paragraph><widget></widget>',
+				keyCodes.delete,
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should remove empty element after selecting widget when delete is pressed',
+				'<paragraph>[]</paragraph><widget></widget>',
+				keyCodes.delete,
+				'[<widget></widget>]'
+			);
+
+			test(
+				'should not respond to other keys',
+				'<widget></widget><paragraph>[]foo</paragraph>',
+				65,
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should do nothing on non-collapsed selection',
+				'<widget></widget><paragraph>[f]oo</paragraph>',
+				keyCodes.backspace,
+				'<widget></widget><paragraph>[f]oo</paragraph>'
+			);
+
+			test(
+				'should do nothing on non-object elements',
+				'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>',
+				keyCodes.backspace,
+				'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: backspace + ctrl',
+				'<widget></widget><paragraph>[]foo</paragraph>',
+				{ keyCode: keyCodes.backspace, ctrlKey: true },
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: backspace + alt',
+				'<widget></widget><paragraph>[]foo</paragraph>',
+				{ keyCode: keyCodes.backspace, altKey: true },
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: backspace + shift',
+				'<widget></widget><paragraph>[]foo</paragraph>',
+				{ keyCode: keyCodes.backspace, shiftKey: true },
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: delete + ctrl',
+				'<paragraph>foo[]</paragraph><widget></widget>',
+				{ keyCode: keyCodes.delete, ctrlKey: true },
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should work correctly with modifier key: delete + alt',
+				'<paragraph>foo[]</paragraph><widget></widget>',
+				{ keyCode: keyCodes.delete, altKey: true },
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should work correctly with modifier key: delete + shift',
+				'<paragraph>foo[]</paragraph><widget></widget>',
+				{ keyCode: keyCodes.delete, shiftKey: true },
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should not modify backspace default behaviour in single paragraph boundaries',
+				'<paragraph>[]foo</paragraph>',
+				keyCodes.backspace,
+				'<paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should not modify delete default behaviour in single paragraph boundaries',
+				'<paragraph>foo[]</paragraph>',
+				keyCodes.delete,
+				'<paragraph>foo[]</paragraph>'
+			);
+
+			test(
+				'should do nothing on selected widget preceded by a paragraph - backspace',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				keyCodes.backspace,
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should do nothing on selected widget preceded by another widget - backspace',
+				'<widget></widget>[<widget></widget>]',
+				keyCodes.backspace,
+				'<widget></widget>[<widget></widget>]'
+			);
+
+			test(
+				'should do nothing on selected widget before paragraph - backspace',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.backspace,
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should do nothing on selected widget before another widget - backspace',
+				'[<widget></widget>]<widget></widget>',
+				keyCodes.backspace,
+				'[<widget></widget>]<widget></widget>'
+			);
+
+			test(
+				'should do nothing on selected widget between paragraphs - backspace',
+				'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.backspace,
+				'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should do nothing on selected widget between other widgets - backspace',
+				'<widget></widget>[<widget></widget>]<widget></widget>',
+				keyCodes.backspace,
+				'<widget></widget>[<widget></widget>]<widget></widget>'
+			);
+
+			test(
+				'should do nothing on selected widget preceded by a paragraph - delete',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				keyCodes.delete,
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should do nothing on selected widget preceded by another widget - delete',
+				'<widget></widget>[<widget></widget>]',
+				keyCodes.delete,
+				'<widget></widget>[<widget></widget>]'
+			);
+
+			test(
+				'should do nothing on selected widget before paragraph - delete',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.delete,
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should do nothing on selected widget before another widget - delete',
+				'[<widget></widget>]<widget></widget>',
+				keyCodes.delete,
+				'[<widget></widget>]<widget></widget>'
+			);
+
+			test(
+				'should do nothing on selected widget between paragraphs - delete',
+				'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.delete,
+				'<paragraph>bar</paragraph>[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should do nothing on selected widget between other widgets - delete',
+				'<widget></widget>[<widget></widget>]<widget></widget>',
+				keyCodes.delete,
+				'<widget></widget>[<widget></widget>]<widget></widget>'
+			);
+
+			test(
+				'should select inline objects - backspace',
+				'<paragraph>foo<inline></inline>[]bar</paragraph>',
+				keyCodes.backspace,
+				'<paragraph>foo[<inline></inline>]bar</paragraph>'
+			);
+
+			test(
+				'should select inline objects - delete',
+				'<paragraph>foo[]<inline></inline>bar</paragraph>',
+				keyCodes.delete,
+				'<paragraph>foo[<inline></inline>]bar</paragraph>'
+			);
+
+			test(
+				'should do nothing on selected inline objects - backspace',
+				'<paragraph>foo[<inline></inline>]bar</paragraph>',
+				keyCodes.backspace,
+				'<paragraph>foo[<inline></inline>]bar</paragraph>'
+			);
+
+			test(
+				'should do nothing on selected inline objects - delete',
+				'<paragraph>foo[<inline></inline>]bar</paragraph>',
+				keyCodes.delete,
+				'<paragraph>foo[<inline></inline>]bar</paragraph>'
+			);
+
+			test(
+				'should do nothing if selection is placed after first letter - backspace',
+				'<paragraph>a[]</paragraph>',
+				keyCodes.backspace,
+				'<paragraph>a[]</paragraph>'
+			);
+
+			test(
+				'should do nothing if selection is placed before first letter - delete',
+				'<paragraph>[]a</paragraph>',
+				keyCodes.delete,
+				'<paragraph>[]a</paragraph>'
+			);
+
+			it( 'should prevent default behaviour and stop event propagation', () => {
+				const keydownHandler = sinon.spy();
+				const domEventDataMock = {
+					keyCode: keyCodes.delete,
+					preventDefault: sinon.spy(),
+				};
+				setModelData( doc, '<paragraph>foo[]</paragraph><widget></widget>' );
+				viewDocument.on( 'keydown',  keydownHandler );
+
+				viewDocument.fire( 'keydown', domEventDataMock );
+
+				expect( getModelData( doc ) ).to.equal( '<paragraph>foo</paragraph>[<widget></widget>]' );
+				sinon.assert.calledOnce( domEventDataMock.preventDefault );
+				sinon.assert.notCalled( keydownHandler );
+			} );
+		} );
+
+		describe( 'arrows', () => {
+			test(
+				'should move selection forward from selected object - right arrow',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.arrowright,
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should move selection forward from selected object - down arrow',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.arrowdown,
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should move selection backward from selected object - left arrow',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				keyCodes.arrowleft,
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should move selection backward from selected object - up arrow',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				keyCodes.arrowup,
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should move selection to next widget - right arrow',
+				'[<widget></widget>]<widget></widget>',
+				keyCodes.arrowright,
+				'<widget></widget>[<widget></widget>]'
+			);
+
+			test(
+				'should move selection to next widget - down arrow',
+				'[<widget></widget>]<widget></widget>',
+				keyCodes.arrowdown,
+				'<widget></widget>[<widget></widget>]'
+			);
+
+			test(
+				'should move selection to previous widget - left arrow',
+				'<widget></widget>[<widget></widget>]',
+				keyCodes.arrowleft,
+				'[<widget></widget>]<widget></widget>'
+			);
+
+			test(
+				'should move selection to previous widget - up arrow',
+				'<widget></widget>[<widget></widget>]',
+				keyCodes.arrowup,
+				'[<widget></widget>]<widget></widget>'
+			);
+
+			test(
+				'should do nothing on non-collapsed selection next to object - right arrow',
+				'<paragraph>ba[r]</paragraph><widget></widget>',
+				keyCodes.arrowright,
+				'<paragraph>ba[r]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should do nothing on non-collapsed selection next to object - down arrow',
+				'<paragraph>ba[r]</paragraph><widget></widget>',
+				keyCodes.arrowdown,
+				'<paragraph>ba[r]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should do nothing on non-collapsed selection next to object - left arrow',
+				'<widget></widget><paragraph>[b]ar</paragraph>',
+				keyCodes.arrowleft,
+				'<widget></widget><paragraph>[b]ar</paragraph>'
+			);
+
+			test(
+				'should do nothing on non-collapsed selection next to object - up arrow',
+				'<widget></widget><paragraph>[b]ar</paragraph>',
+				keyCodes.arrowup,
+				'<widget></widget><paragraph>[b]ar</paragraph>'
+			);
+
+			test(
+				'should not move selection if there is no correct location - right arrow',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				keyCodes.arrowright,
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should not move selection if there is no correct location - down arrow',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				keyCodes.arrowdown,
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should not move selection if there is no correct location - left arrow',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.arrowleft,
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should not move selection if there is no correct location - up arrow',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				keyCodes.arrowup,
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should move selection to object element - right arrow',
+				'<paragraph>foo[]</paragraph><widget></widget>',
+				keyCodes.arrowright,
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should move selection to object element - down arrow',
+				'<paragraph>foo[]</paragraph><widget></widget>',
+				keyCodes.arrowdown,
+				'<paragraph>foo</paragraph>[<widget></widget>]'
+			);
+
+			test(
+				'should move selection to object element - left arrow',
+				'<widget></widget><paragraph>[]foo</paragraph>',
+				keyCodes.arrowleft,
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'should move selection to object element - up arrow',
+				'<widget></widget><paragraph>[]foo</paragraph>',
+				keyCodes.arrowup,
+				'[<widget></widget>]<paragraph>foo</paragraph>'
+			);
+
+			test(
+				'do nothing on non objects - right arrow',
+				'<paragraph>foo[]</paragraph><paragraph>bar</paragraph>',
+				keyCodes.arrowright,
+				'<paragraph>foo[]</paragraph><paragraph>bar</paragraph>'
+			);
+
+			test(
+				'do nothing on non objects - down arrow',
+				'<paragraph>foo[]</paragraph><paragraph>bar</paragraph>',
+				keyCodes.arrowdown,
+				'<paragraph>foo[]</paragraph><paragraph>bar</paragraph>'
+			);
+
+			test(
+				'do nothing on non objects - left arrow',
+				'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>',
+				keyCodes.arrowleft,
+				'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>'
+			);
+
+			test(
+				'do nothing on non objects - up arrow',
+				'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>',
+				keyCodes.arrowup,
+				'<paragraph>foo</paragraph><paragraph>[]bar</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: right arrow + ctrl',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				{ keyCode: keyCodes.arrowright, ctrlKey: true },
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: right arrow + alt',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				{ keyCode: keyCodes.arrowright, altKey: true },
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: right arrow + shift',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				{ keyCode: keyCodes.arrowright, shiftKey: true },
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: down arrow + ctrl',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				{ keyCode: keyCodes.arrowdown, ctrlKey: true },
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: down arrow + alt',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				{ keyCode: keyCodes.arrowdown, altKey: true },
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: down arrow + shift',
+				'[<widget></widget>]<paragraph>foo</paragraph>',
+				{ keyCode: keyCodes.arrowdown, shiftKey: true },
+				'<widget></widget><paragraph>[]foo</paragraph>'
+			);
+
+			test(
+				'should work correctly with modifier key: left arrow + ctrl',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				{ keyCode: keyCodes.arrowleft, ctrlKey: true },
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should work correctly with modifier key: left arrow + alt',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				{ keyCode: keyCodes.arrowleft, altKey: true },
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should work correctly with modifier key: left arrow + shift',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				{ keyCode: keyCodes.arrowleft, shiftKey: true },
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should work correctly with modifier key: up arrow + ctrl',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				{ keyCode: keyCodes.arrowup, ctrlKey: true },
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should work correctly with modifier key: up arrow + alt',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				{ keyCode: keyCodes.arrowup, altKey: true },
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should work correctly with modifier key: up arrow + shift',
+				'<paragraph>foo</paragraph>[<widget></widget>]',
+				{ keyCode: keyCodes.arrowup, shiftKey: true },
+				'<paragraph>foo[]</paragraph><widget></widget>'
+			);
+
+			test(
+				'should do nothing if there is more than one selection in model',
+				'<paragraph>[foo]</paragraph><widget></widget><paragraph>[bar]</paragraph>',
+				keyCodes.arrowright,
+				'<paragraph>[foo]</paragraph><widget></widget><paragraph>[bar]</paragraph>'
+			);
 		} );
 
 		function test( name, data, keyCodeOrMock, expected ) {
@@ -374,14 +639,14 @@ describe( 'Widget', () => {
 					keyCode: keyCodeOrMock
 				};
 
-				setModelData( document, data );
+				setModelData( doc, data );
 				viewDocument.fire( 'keydown', new DomEventData(
 					viewDocument,
-					{ target: null, preventDefault: () => {} },
+					{ target: document.createElement( 'div' ), preventDefault: () => {} },
 					domEventDataMock
 				) );
 
-				expect( getModelData( document ) ).to.equal( expected );
+				expect( getModelData( doc ) ).to.equal( expected );
 			} );
 		}
 	} );